@@ -516,3 +516,152 @@ TEST_F (FilterDesignerTests, FirFloatDoubleConsistency)
516516 for (int i = 0 ; i < numCoeffs; ++i)
517517 EXPECT_NEAR (doubleCoeffs[i], static_cast <double > (floatCoeffs[i]), toleranceF);
518518}
519+
520+ TEST_F (FilterDesignerTests, DISABLED_ExportFIRCoefficientsForAnalysis)
521+ {
522+ const int numCoeffs = 129 ;
523+ const float sampleRateF = 44100 .0f ;
524+
525+ // Design different FIR filters
526+ auto lowpass = FilterDesigner<float >::designFIRLowpass (numCoeffs, 10000 .0f , sampleRateF);
527+ auto highpass = FilterDesigner<float >::designFIRHighpass (numCoeffs, 10000 .0f , sampleRateF);
528+ auto bandpass = FilterDesigner<float >::designFIRBandpass (numCoeffs, 8000 .0f , 12000 .0f , sampleRateF);
529+ auto bandstop = FilterDesigner<float >::designFIRBandstop (numCoeffs, 8000 .0f , 12000 .0f , sampleRateF);
530+
531+ // Different windows for lowpass
532+ auto lowpassHann = FilterDesigner<float >::designFIRLowpass (numCoeffs, 10000 .0f , sampleRateF, WindowType::hann);
533+ auto lowpassHamming = FilterDesigner<float >::designFIRLowpass (numCoeffs, 10000 .0f , sampleRateF, WindowType::hamming);
534+ auto lowpassBlackman = FilterDesigner<float >::designFIRLowpass (numCoeffs, 10000 .0f , sampleRateF, WindowType::blackman);
535+
536+ // Helper lambda to write coefficients to file
537+ auto writeCoeffs = [] (const std::vector<float >& coeffs, const std::string& filename)
538+ {
539+ std::ofstream file (filename);
540+ if (file.is_open ())
541+ {
542+ for (size_t i = 0 ; i < coeffs.size (); ++i)
543+ {
544+ file << coeffs[i];
545+ if (i < coeffs.size () - 1 )
546+ file << " \n " ;
547+ }
548+ file.close ();
549+ }
550+ };
551+
552+ // Write all coefficient sets to files
553+ writeCoeffs (lowpass, " fir_lowpass_10000hz.txt" );
554+ writeCoeffs (highpass, " fir_highpass_10000hz.txt" );
555+ writeCoeffs (bandpass, " fir_bandpass_8000_12000hz.txt" );
556+ writeCoeffs (bandstop, " fir_bandstop_8000_12000hz.txt" );
557+ writeCoeffs (lowpassHann, " fir_lowpass_hann_10000hz.txt" );
558+ writeCoeffs (lowpassHamming, " fir_lowpass_hamming_10000hz.txt" );
559+ writeCoeffs (lowpassBlackman, " fir_lowpass_blackman_10000hz.txt" );
560+
561+ // Create a Python script to plot the frequency responses
562+ std::ofstream pyScript (" plot_fir_responses.py" );
563+ if (pyScript.is_open ())
564+ {
565+ pyScript << R"( #!/usr/bin/env python3
566+ import numpy as np
567+ import matplotlib.pyplot as plt
568+ from scipy import signal
569+
570+ def load_coeffs(filename):
571+ with open(filename, 'r') as f:
572+ return [float(line.strip()) for line in f.readlines()]
573+
574+ def plot_frequency_response(coeffs, title, sample_rate=44100):
575+ w, h = signal.freqz(coeffs, worN=8000, fs=sample_rate)
576+
577+ plt.figure(figsize=(12, 8))
578+
579+ # Magnitude response
580+ plt.subplot(2, 1, 1)
581+ plt.plot(w, 20 * np.log10(np.abs(h)))
582+ plt.title(f'{title} - Magnitude Response')
583+ plt.xlabel('Frequency (Hz)')
584+ plt.ylabel('Magnitude (dB)')
585+ plt.grid(True)
586+ plt.xlim(0, sample_rate/2)
587+ plt.ylim(-80, 5)
588+
589+ # Phase response
590+ plt.subplot(2, 1, 2)
591+ plt.plot(w, np.unwrap(np.angle(h)) * 180 / np.pi)
592+ plt.title(f'{title} - Phase Response')
593+ plt.xlabel('Frequency (Hz)')
594+ plt.ylabel('Phase (degrees)')
595+ plt.grid(True)
596+ plt.xlim(0, sample_rate/2)
597+
598+ plt.tight_layout()
599+ plt.savefig(f'{title.lower().replace(" ", "_").replace("-", "_")}_response.png', dpi=150, bbox_inches='tight')
600+ plt.show()
601+
602+ # Load and plot all FIR filter responses
603+ filters = [
604+ ('fir_lowpass_10000hz.txt', 'FIR Lowpass 10000Hz'),
605+ ('fir_highpass_10000hz.txt', 'FIR Highpass 10000Hz'),
606+ ('fir_bandpass_8000_12000hz.txt', 'FIR Bandpass 8000-12000Hz'),
607+ ('fir_bandstop_8000_12000hz.txt', 'FIR Bandstop 8000-12000Hz'),
608+ ('fir_lowpass_hann_10000hz.txt', 'FIR Lowpass Hann Window'),
609+ ('fir_lowpass_hamming_10000hz.txt', 'FIR Lowpass Hamming Window'),
610+ ('fir_lowpass_blackman_10000hz.txt', 'FIR Lowpass Blackman Window')
611+ ]
612+
613+ for filename, title in filters:
614+ try:
615+ coeffs = load_coeffs(filename)
616+ plot_frequency_response(coeffs, title)
617+ except FileNotFoundError:
618+ print(f"File {filename} not found!")
619+
620+ # Compare window types on same plot
621+ plt.figure(figsize=(12, 6))
622+ window_files = [
623+ ('fir_lowpass_hann_1000hz.txt', 'Hann', 'blue'),
624+ ('fir_lowpass_hamming_1000hz.txt', 'Hamming', 'red'),
625+ ('fir_lowpass_blackman_1000hz.txt', 'Blackman', 'green')
626+ ]
627+
628+ for filename, label, color in window_files:
629+ try:
630+ coeffs = load_coeffs(filename)
631+ w, h = signal.freqz(coeffs, worN=8000, fs=44100)
632+ plt.plot(w, 20 * np.log10(np.abs(h)), label=label, color=color)
633+ except FileNotFoundError:
634+ print(f"File {filename} not found!")
635+
636+ plt.title('FIR Lowpass 1000Hz - Window Comparison')
637+ plt.xlabel('Frequency (Hz)')
638+ plt.ylabel('Magnitude (dB)')
639+ plt.grid(True)
640+ plt.legend()
641+ plt.xlim(0, 22050)
642+ plt.ylim(-80, 5)
643+ plt.savefig('fir_window_comparison.png', dpi=150, bbox_inches='tight')
644+ plt.show()
645+
646+ print("All plots generated successfully!")
647+ )" ;
648+ pyScript.close ();
649+ }
650+
651+ // Just verify the files were created - the actual validation will be done visually with Python
652+ EXPECT_EQ (lowpass.size (), numCoeffs);
653+ EXPECT_EQ (highpass.size (), numCoeffs);
654+ EXPECT_EQ (bandpass.size (), numCoeffs);
655+ EXPECT_EQ (bandstop.size (), numCoeffs);
656+
657+ std::cout << " \n FIR coefficient files and Python plotting script created:\n " ;
658+ std::cout << " - fir_lowpass_10000hz.txt\n " ;
659+ std::cout << " - fir_highpass_10000hz.txt\n " ;
660+ std::cout << " - fir_bandpass_8000_12000hz.txt\n " ;
661+ std::cout << " - fir_bandstop_8000_12000hz.txt\n " ;
662+ std::cout << " - fir_lowpass_hann_10000hz.txt\n " ;
663+ std::cout << " - fir_lowpass_hamming_10000hz.txt\n " ;
664+ std::cout << " - fir_lowpass_blackman_10000hz.txt\n " ;
665+ std::cout << " - plot_fir_responses.py\n\n " ;
666+ std::cout << " Run: python3 plot_fir_responses.py (requires numpy, matplotlib, scipy)\n " ;
667+ }
0 commit comments