@@ -516,152 +516,3 @@ 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, ExportFIRCoefficientsForAnalysis)
521- {
522- const int numCoeffs = 65 ;
523- const float sampleRateF = 44100 .0f ;
524-
525- // Design different FIR filters
526- auto lowpass = FilterDesigner<float >::designFIRLowpass (numCoeffs, 1000 .0f , sampleRateF);
527- auto highpass = FilterDesigner<float >::designFIRHighpass (numCoeffs, 1000 .0f , sampleRateF);
528- auto bandpass = FilterDesigner<float >::designFIRBandpass (numCoeffs, 800 .0f , 1200 .0f , sampleRateF);
529- auto bandstop = FilterDesigner<float >::designFIRBandstop (numCoeffs, 800 .0f , 1200 .0f , sampleRateF);
530-
531- // Different windows for lowpass
532- auto lowpassHann = FilterDesigner<float >::designFIRLowpass (numCoeffs, 1000 .0f , sampleRateF, WindowType::hann);
533- auto lowpassHamming = FilterDesigner<float >::designFIRLowpass (numCoeffs, 1000 .0f , sampleRateF, WindowType::hamming);
534- auto lowpassBlackman = FilterDesigner<float >::designFIRLowpass (numCoeffs, 1000 .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_1000hz.txt" );
554- writeCoeffs (highpass, " fir_highpass_1000hz.txt" );
555- writeCoeffs (bandpass, " fir_bandpass_800_1200hz.txt" );
556- writeCoeffs (bandstop, " fir_bandstop_800_1200hz.txt" );
557- writeCoeffs (lowpassHann, " fir_lowpass_hann_1000hz.txt" );
558- writeCoeffs (lowpassHamming, " fir_lowpass_hamming_1000hz.txt" );
559- writeCoeffs (lowpassBlackman, " fir_lowpass_blackman_1000hz.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_1000hz.txt', 'FIR Lowpass 1000Hz'),
605- ('fir_highpass_1000hz.txt', 'FIR Highpass 1000Hz'),
606- ('fir_bandpass_800_1200hz.txt', 'FIR Bandpass 800-1200Hz'),
607- ('fir_bandstop_800_1200hz.txt', 'FIR Bandstop 800-1200Hz'),
608- ('fir_lowpass_hann_1000hz.txt', 'FIR Lowpass Hann Window'),
609- ('fir_lowpass_hamming_1000hz.txt', 'FIR Lowpass Hamming Window'),
610- ('fir_lowpass_blackman_1000hz.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_1000hz.txt\n " ;
659- std::cout << " - fir_highpass_1000hz.txt\n " ;
660- std::cout << " - fir_bandpass_800_1200hz.txt\n " ;
661- std::cout << " - fir_bandstop_800_1200hz.txt\n " ;
662- std::cout << " - fir_lowpass_hann_1000hz.txt\n " ;
663- std::cout << " - fir_lowpass_hamming_1000hz.txt\n " ;
664- std::cout << " - fir_lowpass_blackman_1000hz.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