From bbfcca8ec099ef8e478989f8d012270696498102 Mon Sep 17 00:00:00 2001 From: Jared Boone Date: Tue, 29 Dec 2015 10:58:53 -0800 Subject: [PATCH] Rebake of AM, NFM demodulators -- more flexible filtering/configuration. Also make SpectrumCollector dynamically configurable. Add deemphasis filter to NFM. --- firmware/baseband/dsp_iir_config.hpp | 6 + firmware/baseband/proc_am_audio.cpp | 82 ++++++--- firmware/baseband/proc_am_audio.hpp | 19 ++- firmware/baseband/proc_nfm_audio.cpp | 168 ++++++++++++++++--- firmware/baseband/proc_nfm_audio.hpp | 25 ++- firmware/baseband/proc_wideband_spectrum.hpp | 2 +- firmware/baseband/spectrum_collector.cpp | 6 + firmware/baseband/spectrum_collector.hpp | 5 +- 8 files changed, 249 insertions(+), 64 deletions(-) diff --git a/firmware/baseband/dsp_iir_config.hpp b/firmware/baseband/dsp_iir_config.hpp index d238c6d2..ad9b81f9 100644 --- a/firmware/baseband/dsp_iir_config.hpp +++ b/firmware/baseband/dsp_iir_config.hpp @@ -34,6 +34,12 @@ constexpr iir_biquad_config_t audio_hpf_config { constexpr iir_biquad_config_t non_audio_hpf_config { { 0.51891061f, -0.95714180f, 0.51891061f }, { 1.0f , -0.79878302f, 0.43960231f } + +// scipy.signal.butter(1, 300 / 24000.0, 'lowpass', analog=False) +// NOTE: Technically, order-1 filter, b[2] = a[2] = 0. +constexpr iir_biquad_config_t audio_deemph_300_6_config { + { 0.01925927f, 0.01925927f, 0.00000000f, }, + { 1.00000000f, -0.96148145f, 0.00000000f, }, }; #endif/*__DSP_IIR_CONFIG_H__*/ diff --git a/firmware/baseband/proc_am_audio.cpp b/firmware/baseband/proc_am_audio.cpp index b2d480c7..34fb5541 100644 --- a/firmware/baseband/proc_am_audio.cpp +++ b/firmware/baseband/proc_am_audio.cpp @@ -21,39 +21,77 @@ #include "proc_am_audio.hpp" -#include +// DSB AM 6K00A3E emission type /////////////////////////////////////////// + +// IFIR image-reject filter: fs=3072000, pass=3000, stop=339000, decim=8, fout=384000 +static constexpr std::array taps_6k0_decim_0 { { + 39, 104, 224, 412, 674, 1008, 1400, 1821, + 2234, 2594, 2863, 3006, 3006, 2863, 2594, 2234, + 1821, 1400, 1008, 674, 412, 224, 104, 39, +} }; + +// IFIR prototype filter: fs=384000, pass=3000, stop=45000, decim=8, fout=48000 +static constexpr std::array taps_6k0_decim_1 { { + -26, -63, -123, -195, -263, -295, -253, -99, + 199, 651, 1242, 1927, 2633, 3273, 3760, 4023, + 4023, 3760, 3273, 2633, 1927, 1242, 651, 199, + -99, -253, -295, -263, -195, -123, -63, -26, +} }; + +// Channel filter: fs=48000, pass=3000, stop=6700, decim=1, fout=48000 +static constexpr std::array taps_6k0_channel { { + 95, 178, 247, 208, -21, -474, -1080, -1640, + -1857, -1411, -83, 2134, 4978, 7946, 10413, 11815, + 11815, 10413, 7946, 4978, 2134, -83, -1411, -1857, + -1640, -1080, -474, -21, 208, 247, 178, 95, +} }; + +NarrowbandAMAudio::NarrowbandAMAudio() { + constexpr size_t baseband_fs = 3072000; + + constexpr size_t decim_0_input_fs = baseband_fs; + constexpr size_t decim_0_decimation_factor = 8; + constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0_decimation_factor; + + constexpr size_t decim_1_input_fs = decim_0_output_fs; + constexpr size_t decim_1_decimation_factor = 8; + constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1_decimation_factor; + + constexpr size_t channel_filter_input_fs = decim_1_output_fs; + constexpr size_t channel_filter_decimation_factor = 1; + constexpr size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor; + + decim_0.configure(taps_6k0_decim_0, 33554432); + decim_1.configure(taps_6k0_decim_1, 131072); + channel_filter.configure(taps_6k0_channel, 1); + channel_filter_pass_f = 3000; + channel_filter_stop_f = 6700; + + channel_spectrum.set_decimation_factor(std::floor((channel_filter_output_fs / 2) / ((channel_filter_pass_f + channel_filter_stop_f) / 2))); +} void NarrowbandAMAudio::execute(const buffer_c8_t& buffer) { - auto decimator_out = decimator.execute(buffer); - - const buffer_c16_t work_baseband_buffer { - (complex16_t*)decimator_out.p, - sizeof(*decimator_out.p) * decimator_out.count + const buffer_c16_t dst_buffer { + dst.data(), + dst.size() }; - /* 96kHz complex[64] - * -> FIR filter, 48kHz int16_t[32] */ - auto channel = channel_filter.execute(decimator_out, work_baseband_buffer); + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); + const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); // TODO: Feed channel_stats post-decimation data? - feed_channel_stats(channel); - channel_spectrum.feed( - channel, - decimator_out.sampling_rate * channel_filter_taps.pass_frequency_normalized, - decimator_out.sampling_rate * channel_filter_taps.stop_frequency_normalized - ); + feed_channel_stats(channel_out); + channel_spectrum.feed(channel_out, channel_filter_pass_f, channel_filter_stop_f); const buffer_s16_t work_audio_buffer { - (int16_t*)decimator_out.p, - sizeof(*decimator_out.p) * decimator_out.count + (int16_t*)dst.data(), + sizeof(*dst.data()) * dst.size() }; - /* 48kHz complex[32] - * -> AM demodulation - * -> 48kHz int16_t[32] */ - auto audio = demod.execute(channel, work_audio_buffer); + auto audio = demod.execute(channel_out, work_audio_buffer); audio_hpf.execute_in_place(audio); + fill_audio_buffer(audio); } diff --git a/firmware/baseband/proc_am_audio.hpp b/firmware/baseband/proc_am_audio.hpp index 283f0f7c..4779384b 100644 --- a/firmware/baseband/proc_am_audio.hpp +++ b/firmware/baseband/proc_am_audio.hpp @@ -33,22 +33,27 @@ #include "spectrum_collector.hpp" +#include +#include + class NarrowbandAMAudio : public BasebandProcessor { public: - NarrowbandAMAudio() { - decimator.set_decimation_factor(ChannelDecimator::DecimationFactor::By32); - channel_filter.configure(channel_filter_taps.taps, 2); - } - + NarrowbandAMAudio(); + void execute(const buffer_c8_t& buffer) override; void on_update_spectrum() override { channel_spectrum.update(); } private: - ChannelDecimator decimator; - const fir_taps_real<64>& channel_filter_taps = taps_64_lp_031_070_tfilter; + std::array dst; + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0; + dsp::decimate::FIRC16xR16x32Decim8 decim_1; dsp::decimate::FIRAndDecimateComplex channel_filter; + uint32_t channel_filter_pass_f; + uint32_t channel_filter_stop_f; + dsp::demodulate::AM demod; + IIRBiquadFilter audio_hpf { audio_hpf_config }; SpectrumCollector channel_spectrum; diff --git a/firmware/baseband/proc_nfm_audio.cpp b/firmware/baseband/proc_nfm_audio.cpp index 80a400d7..fe20a03e 100644 --- a/firmware/baseband/proc_nfm_audio.cpp +++ b/firmware/baseband/proc_nfm_audio.cpp @@ -24,44 +24,163 @@ #include #include +// NBFM 16K0F3E emission type ///////////////////////////////////////////// + +// IFIR image-reject filter: fs=3072000, pass=8000, stop=344000, decim=8, fout=384000 +static constexpr std::array taps_16k0_decim_0 { { + 1, 67, 165, 340, 599, 944, 1361, 1820, + 2278, 2684, 2988, 3152, 3152, 2988, 2684, 2278, + 1820, 1361, 944, 599, 340, 165, 67, 1, +} }; + +// IFIR prototype filter: fs=384000, pass=8000, stop=40000, decim=8, fout=48000 +static constexpr std::array taps_16k0_decim_1 { { + -26, -125, -180, -275, -342, -359, -286, -90, + 250, 733, 1337, 2011, 2688, 3289, 3740, 3982, + 3982, 3740, 3289, 2688, 2011, 1337, 733, 250, + -90, -286, -359, -342, -275, -180, -125, -26, +} }; + +// Channel filter: fs=48000, pass=8000, stop=12400, decim=1, fout=48000 +static constexpr std::array taps_16k0_channel { { + -73, -285, -376, -8, 609, 538, -584, -1387, + -148, 2173, 1959, -2146, -5267, -297, 12915, 24737, + 24737, 12915, -297, -5267, -2146, 1959, 2173, -148, + -1387, -584, 538, 609, -8, -376, -285, -73, +} }; + +// NBFM 11K0F3E emission type ///////////////////////////////////////////// + +// IFIR image-reject filter: fs=3072000, pass=5500, stop=341500, decim=8, fout=384000 +static constexpr std::array taps_11k0_decim_0 { { + 38, 102, 220, 406, 668, 1004, 1397, 1822, + 2238, 2603, 2875, 3020, 3020, 2875, 2603, 2238, + 1822, 1397, 1004, 668, 406, 220, 102, 38, +} }; + +// IFIR prototype filter: fs=384000, pass=5500, stop=42500, decim=8, fout=48000 +static constexpr std::array taps_11k0_decim_1 { { + -42, -87, -157, -234, -298, -318, -255, -75, + 246, 713, 1306, 1976, 2656, 3265, 3724, 3971, + 3971, 3724, 3265, 2656, 1976, 1306, 713, 246, + -75, -255, -318, -298, -234, -157, -87, -42, +} }; + +// Channel filter: fs=48000, pass=5500, stop=8900, decim=1, fout=48000 +static constexpr std::array taps_11k0_channel { { + -68, -345, -675, -867, -582, 247, 1222, 1562, + 634, -1379, -3219, -3068, 310, 6510, 13331, 17795, + 17795, 13331, 6510, 310, -3068, -3219, -1379, 634, + 1562, 1222, 247, -582, -867, -675, -345, -68, +} }; + +/// NBFM 8K50F3E emission type //////////////////////////////////////////// + +// IFIR image-reject filter: fs=3072000, pass=4250, stop=340250, decim=8, fout=384000 +static constexpr std::array taps_4k25_decim_0 { { + 38, 103, 222, 409, 671, 1006, 1399, 1821, + 2236, 2599, 2868, 3012, 3012, 2868, 2599, 2236, + 1821, 1399, 1006, 671, 409, 222, 103, 38, +} }; + +// IFIR prototype filter: fs=384000, pass=4250, stop=43750, decim=8, fout=48000 +static constexpr std::array taps_4k25_decim_1 { { + -33, -74, -139, -214, -280, -306, -254, -87, + 222, 682, 1274, 1951, 2644, 3268, 3741, 3996, + 3996, 3741, 3268, 2644, 1951, 1274, 682, 222, + -87, -254, -306, -280, -214, -139, -74, -33, +} }; + +// Channel filter: fs=48000, pass=4250, stop=7900, decim=1, fout=48000 +static constexpr std::array taps_4k25_channel { { + -58, -14, 153, 484, 871, 1063, 770, -141, + -1440, -2488, -2435, -614, 3035, 7771, 12226, 14927, + 14927, 12226, 7771, 3035, -614, -2435, -2488, -1440, + -141, 770, 1063, 871, 484, 153, -14, -58, +} }; + +NarrowbandFMAudio::NarrowbandFMAudio() { + set_mode(Mode::Deviation2K5Narrow); +} + +void NarrowbandFMAudio::set_mode(const Mode mode) { + constexpr size_t baseband_fs = 3072000; + + constexpr size_t decim_0_input_fs = baseband_fs; + constexpr size_t decim_0_decimation_factor = 8; + constexpr size_t decim_0_output_fs = decim_0_input_fs / decim_0_decimation_factor; + + constexpr size_t decim_1_input_fs = decim_0_output_fs; + constexpr size_t decim_1_decimation_factor = 8; + constexpr size_t decim_1_output_fs = decim_1_input_fs / decim_1_decimation_factor; + + constexpr size_t channel_filter_input_fs = decim_1_output_fs; + constexpr size_t channel_filter_decimation_factor = 1; + constexpr size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor; + + constexpr size_t demod_input_fs = channel_filter_output_fs; + + switch(mode) { + default: + case Mode::Deviation5K: + decim_0.configure(taps_16k0_decim_0, 33554432); + decim_1.configure(taps_16k0_decim_1, 131072); + channel_filter.configure(taps_16k0_channel, channel_filter_decimation_factor); + demod.configure(demod_input_fs, 5000); + channel_filter_pass_f = 8000; + channel_filter_stop_f = 12400; + break; + + case Mode::Deviation2K5Wide: + decim_0.configure(taps_11k0_decim_0, 33554432); + decim_1.configure(taps_11k0_decim_1, 131072); + channel_filter.configure(taps_11k0_channel, channel_filter_decimation_factor); + demod.configure(demod_input_fs, 2500); + channel_filter_pass_f = 5500; + channel_filter_stop_f = 8900; + break; + + case Mode::Deviation2K5Narrow: + decim_0.configure(taps_4k25_decim_0, 33554432); + decim_1.configure(taps_4k25_decim_1, 131072); + channel_filter.configure(taps_4k25_channel, channel_filter_decimation_factor); + demod.configure(demod_input_fs, 2500); + channel_filter_pass_f = 4250; + channel_filter_stop_f = 7900; + break; + } + + channel_spectrum.set_decimation_factor(std::floor((channel_filter_output_fs / 2) / ((channel_filter_pass_f + channel_filter_stop_f) / 2))); +} + void NarrowbandFMAudio::execute(const buffer_c8_t& buffer) { - /* Called every 2048/3072000 second -- 1500Hz. */ - - auto decimator_out = decimator.execute(buffer); - - const buffer_c16_t work_baseband_buffer { - (complex16_t*)decimator_out.p, - sizeof(*decimator_out.p) * decimator_out.count + const buffer_c16_t dst_buffer { + dst.data(), + dst.size() }; - /* 96kHz complex[64] - * -> FIR filter, <6kHz (0.063fs) pass, gain 1.0 - * -> 48kHz int16_t[32] */ - auto channel = channel_filter.execute(decimator_out, work_baseband_buffer); + const auto decim_0_out = decim_0.execute(buffer, dst_buffer); + const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); + const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer); - // TODO: Feed channel_stats post-decimation data? - feed_channel_stats(channel); - channel_spectrum.feed( - channel, - decimator_out.sampling_rate * channel_filter_taps.pass_frequency_normalized, - decimator_out.sampling_rate * channel_filter_taps.stop_frequency_normalized - ); + feed_channel_stats(channel_out); + channel_spectrum.feed(channel_out, channel_filter_pass_f, channel_filter_stop_f); const buffer_s16_t work_audio_buffer { - (int16_t*)decimator_out.p, - sizeof(*decimator_out.p) * decimator_out.count + (int16_t*)dst.data(), + sizeof(*dst.data()) * dst.size() }; - /* 48kHz complex[32] - * -> FM demodulation - * -> 48kHz int16_t[32] */ - auto audio = demod.execute(channel, work_audio_buffer); + auto audio = demod.execute(channel_out, work_audio_buffer); static uint64_t audio_present_history = 0; const auto audio_present_now = squelch.execute(audio); audio_present_history = (audio_present_history << 1) | (audio_present_now ? 1 : 0); const bool audio_present = (audio_present_history != 0); + audio_hpf.execute_in_place(audio); + audio_deemph.execute_in_place(audio); + if( !audio_present ) { // Zero audio buffer. for(size_t i=0; i& channel_filter_taps = taps_64_lp_042_078_tfilter; + std::array dst; + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0; + dsp::decimate::FIRC16xR16x32Decim8 decim_1; + dsp::decimate::FIRAndDecimateComplex channel_filter; - dsp::demodulate::FM demod { 48000, 7500 }; + uint32_t channel_filter_pass_f = 0; + uint32_t channel_filter_stop_f = 0; + + dsp::demodulate::FM demod; IIRBiquadFilter audio_hpf { audio_hpf_config }; + IIRBiquadFilter audio_deemph { audio_deemph_300_6_config }; FMSquelch squelch; SpectrumCollector channel_spectrum; diff --git a/firmware/baseband/proc_wideband_spectrum.hpp b/firmware/baseband/proc_wideband_spectrum.hpp index 283f40e3..b8311c69 100644 --- a/firmware/baseband/proc_wideband_spectrum.hpp +++ b/firmware/baseband/proc_wideband_spectrum.hpp @@ -38,7 +38,7 @@ public: private: size_t sample_count = 0; - SpectrumCollector channel_spectrum { 1 }; + SpectrumCollector channel_spectrum; std::array spectrum; }; diff --git a/firmware/baseband/spectrum_collector.cpp b/firmware/baseband/spectrum_collector.cpp index dacf45ab..2c604b2b 100644 --- a/firmware/baseband/spectrum_collector.cpp +++ b/firmware/baseband/spectrum_collector.cpp @@ -29,6 +29,12 @@ #include +void SpectrumCollector::set_decimation_factor( + const size_t decimation_factor +) { + channel_spectrum_decimator.set_factor(decimation_factor); +} + /* TODO: Refactor to register task with idle thread? * It's sad that the idle thread has to call all the way back here just to * perform the deferred task on the buffer of data we prepared. diff --git a/firmware/baseband/spectrum_collector.hpp b/firmware/baseband/spectrum_collector.hpp index 203f337a..52ef1d0e 100644 --- a/firmware/baseband/spectrum_collector.hpp +++ b/firmware/baseband/spectrum_collector.hpp @@ -33,11 +33,12 @@ class SpectrumCollector { public: constexpr SpectrumCollector( - const size_t decimation_factor = 4 - ) : channel_spectrum_decimator { decimation_factor } + ) : channel_spectrum_decimator { 1 } { } + void set_decimation_factor(const size_t decimation_factor); + void feed( const buffer_c16_t& channel, const uint32_t filter_pass_frequency,