Skip to content

Commit 0dfd4ea

Browse files
committed
Export FIR coefficients for analysis
1 parent 66b0e1b commit 0dfd4ea

File tree

1 file changed

+149
-0
lines changed

1 file changed

+149
-0
lines changed

tests/yup_dsp/yup_FilterDesigner.cpp

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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 << "\nFIR 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

Comments
 (0)