diff --git a/firmware/application/freqman_db.cpp b/firmware/application/freqman_db.cpp index 77f1b41a8..5d2d0bf20 100644 --- a/firmware/application/freqman_db.cpp +++ b/firmware/application/freqman_db.cpp @@ -78,6 +78,7 @@ options_t freqman_bandwidths[4] = { {"16k", 16000}, {"25k", 25000}, {"50k", 50000}, + {"75k", 75000}, {"100k", 100000}, {"150k", 150000}, {"250k", 250000}, diff --git a/firmware/application/ui/ui_spectrum.cpp b/firmware/application/ui/ui_spectrum.cpp index f3163f948..319915b10 100644 --- a/firmware/application/ui/ui_spectrum.cpp +++ b/firmware/application/ui/ui_spectrum.cpp @@ -383,7 +383,7 @@ void WaterfallView::on_audio_spectrum() { // TODO: Comments below refer to a fixed oversample rate (8x), cleanup. uint32_t filter_bandwidth_for_sampling_rate(int32_t sampling_rate) { switch (sampling_rate) { // Use the var fs (sampling_rate) to set up BPF aprox < fs_max / 2 by Nyquist theorem. - case 0 ... 3'000'000: // BW Captured range (0 <= 250kHz max) fs = 8 x 250 kHz., 16 x 150 khz, 32 x 75 khz, , 64 x 40 khz + case 0 ... 3'500'000: // BW Captured range (0 <= 250kHz max) fs = 8x250 kHz =2000., 16x150 khz =2400, 32x100 khz =3200, (32x75k = 2400), (future 64x40 khz =2400) return 1'750'000; // Minimum BPF MAX2837 for all those lower BW options. case 4'000'000 ... 6'000'000: // BW capture range (500k...750kHz max) fs_max = 8 x 750kHz = 6Mhz diff --git a/firmware/application/ui_record_view.cpp b/firmware/application/ui_record_view.cpp index fd3d578d3..b5dbc0f9d 100644 --- a/firmware/application/ui_record_view.cpp +++ b/firmware/application/ui_record_view.cpp @@ -145,11 +145,11 @@ OversampleRate RecordView::get_oversample_rate(uint32_t sample_rate) { auto rate = ::get_oversample_rate(sample_rate); - // Currently proc_capture only supports x8 and x16 for decimation. - if (rate < OversampleRate::x8) + // Currently proc_capture only supports x8, x16, x32 for decimation. + if (rate < OversampleRate::x8) // clipping while x4 is not implemented yet. rate = OversampleRate::x8; - else if (rate > OversampleRate::x16) - rate = OversampleRate::x16; + else if (rate > OversampleRate::x32) // clipping while x64 is not implemented yet . + rate = OversampleRate::x32; return rate; } diff --git a/firmware/baseband/proc_capture.cpp b/firmware/baseband/proc_capture.cpp index 8659605c0..20bee0103 100644 --- a/firmware/baseband/proc_capture.cpp +++ b/firmware/baseband/proc_capture.cpp @@ -31,7 +31,8 @@ CaptureProcessor::CaptureProcessor() { decim_0_8.configure(taps_200k_decim_0.taps, 33554432); // to be used with decim1 (/2), then total two stages decim (/16) decim_0_8_180k.configure(taps_180k_wfm_decim_0.taps, 33554432); // to be used alone - no additional decim1 (/2), then total single stage decim (/8) - decim_1.configure(taps_200k_decim_1.taps, 131072); + decim_1_2.configure(taps_200k_decim_1.taps, 131072); + decim_1_8.configure(taps_16k0_decim_1.taps, 131072); // tentative decim1 /8 and taps, pending to be optimized. channel_spectrum.set_decimation_factor(1); baseband_thread.start(); @@ -39,11 +40,16 @@ CaptureProcessor::CaptureProcessor() { void CaptureProcessor::execute(const buffer_c8_t& buffer) { /* 2.4576MHz, 2048 samples */ - const auto decim_0_out = decim_0_execute(buffer, dst_buffer); // selectable 3 possible decim_0, (/4. /8 200k soft transition filter , /8 180k sharp ) + const auto decim_0_out = decim_0_execute(buffer, dst_buffer); // selectable 3 possible decim_0, (/4. /8 200k soft filter , /8 180k sharp ) + const auto decim_1_out = decim_1_execute(decim_0_out, dst_buffer); // selectable 3 possible decim_1, (/8. /2 200k or bypassed /1 ) + /* this code was valid when we had only 2 decim1 cases. const auto decim_1_out = baseband_fs < 4800'000 - ? decim_1.execute(decim_0_out, dst_buffer) // < 600khz double decim. stage , means 500khz and lower bit rates. - : decim_0_out; // >= 600khz single decim. stage , means 600khz and upper bit rates. + ? decim_1_2.execute(decim_0_out, dst_buffer) // < 600khz double decim. stage , means 500khz and lower bit rates. + // ? decim_1_8.execute(decim_0_out, dst_buffer) // < 600khz double decim. stage , means 500khz and lower bit rates. + : decim_0_out; // >= 600khz single decim. stage , means 600khz and upper bit rates. + + } */ const auto& decimator_out = decim_1_out; const auto& channel = decimator_out; @@ -91,13 +97,44 @@ void CaptureProcessor::sample_rate_config(const SampleRateConfigMessage& message baseband_thread.set_sampling_rate(baseband_fs); + // Current fw , we are using only 2 decim_0 modes, /4 , /8 auto decim_0_factor = oversample_rate == OversampleRate::x8 ? decim_0_4.decimation_factor : decim_0_8.decimation_factor; - size_t decim_0_output_fs = baseband_fs / decim_0_factor; + size_t decim_0_output_fs = baseband_fs / decim_0_factor; size_t decim_1_input_fs = decim_0_output_fs; - size_t decim_1_output_fs = decim_1_input_fs / decim_1.decimation_factor; + + size_t decim_1_factor; + switch (oversample_rate) { // we are using 3 decim_1 modes, /1 , /2 , /8 + case OversampleRate::x8: + if (baseband_fs < 4800'000) { + decim_1_factor = decim_1_2.decimation_factor; // /8 = /4x2 + } else { + decim_1_factor = 2 * 1; // 600khz and onwards, single decim /8 = /8x1 (we applied additional *2 correction to speed up waterfall, no effect to scale spectrum) + } + break; + + case OversampleRate::x16: + decim_1_factor = 2 * decim_1_2.decimation_factor; // /16 = /8x2 (we applied additional *2 correction to increase waterfall spped >=600k and smooth & avoid abnormal motion >1M5 ) + break; + + case OversampleRate::x32: + decim_1_factor = 2 * decim_1_8.decimation_factor; // /x32 = /4x8 (we applied additional *2 correction to speed up waterfall, no effect to scale spectrum) + break; + + default: + decim_1_factor = 2; // just default initial value to remove compile warning. + break; + } + + /* + auto decim_1_factor = oversample_rate == OversampleRate::x32 // that was ok, when we had only 2 oversampling x8 , x16 + ? decim_1_8.decimation_factor + : decim_1_2.decimation_factor; + + */ + size_t decim_1_output_fs = decim_1_input_fs / decim_1_factor; channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * decim_1_input_fs; channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * decim_1_input_fs; @@ -117,15 +154,39 @@ void CaptureProcessor::capture_config(const CaptureConfigMessage& message) { buffer_c16_t CaptureProcessor::decim_0_execute(const buffer_c8_t& src, const buffer_c16_t& dst) { switch (oversample_rate) { - case OversampleRate::x8: // we can get /8 by two means , decim0 (:4) + decim1 (:2) . or just decim0 (;8) + case OversampleRate::x8: // we can get /8 by two means , decim0 (/4) + decim1 (/2) . or just decim0 (/8) if (baseband_fs < 4800'000) { // 600khz (600k x 8) - return decim_0_4.execute(src, dst); // decim_0 /4 with double decim stage + return decim_0_4.execute(src, dst); // decim_0 , /4 with double decim stage } else { return decim_0_8_180k.execute(src, dst); // decim_0 /8 with single decim stage } case OversampleRate::x16: - return decim_0_8.execute(src, dst); + return decim_0_8.execute(src, dst); // decim_0 , /8 with double decim stage + + case OversampleRate::x32: + return decim_0_4.execute(src, dst); // decim_0 , /4 with double decim stage + + default: + chDbgPanic("Unhandled OversampleRate"); + return {}; + } +} + +buffer_c16_t CaptureProcessor::decim_1_execute(const buffer_c16_t& src, const buffer_c16_t& dst) { + switch (oversample_rate) { + case OversampleRate::x8: // we can get /8 by two means , decim0 (/4) + decim1 (/2) . or just decim0 (/8) + if (baseband_fs < 4800'000) { // 600khz (600k x 8) + return decim_1_2.execute(src, dst); + } else { + return src; + } + + case OversampleRate::x16: + return decim_1_2.execute(src, dst); // total decim /16 = /8x2, applied to 150khz + + case OversampleRate::x32: + return decim_1_8.execute(src, dst); // total decim /32 = /4x8, appled to <= 100khz , 75k with margin ,(50k, 25k, 12k5 now also) ... default: chDbgPanic("Unhandled OversampleRate"); diff --git a/firmware/baseband/proc_capture.hpp b/firmware/baseband/proc_capture.hpp index 2ee71b726..38cf206d5 100644 --- a/firmware/baseband/proc_capture.hpp +++ b/firmware/baseband/proc_capture.hpp @@ -57,7 +57,8 @@ class CaptureProcessor : public BasebandProcessor { dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0_4{}; dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0_8{}; dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0_8_180k{}; - dsp::decimate::FIRC16xR16x16Decim2 decim_1{}; + dsp::decimate::FIRC16xR16x16Decim2 decim_1_2{}; + dsp::decimate::FIRC16xR16x32Decim8 decim_1_8{}; int32_t channel_filter_low_f = 0; int32_t channel_filter_high_f = 0; @@ -80,6 +81,9 @@ class CaptureProcessor : public BasebandProcessor { /* Dispatch to the correct decim_0 based on oversample rate. */ buffer_c16_t decim_0_execute(const buffer_c8_t& src, const buffer_c16_t& dst); + + /* Dispatch to the correct decim_1 based on oversample rate. */ + buffer_c16_t decim_1_execute(const buffer_c16_t& src, const buffer_c16_t& dst); }; #endif /*__PROC_CAPTURE_HPP__*/ diff --git a/firmware/common/oversample.hpp b/firmware/common/oversample.hpp index 2be56df92..afe1e9272 100644 --- a/firmware/common/oversample.hpp +++ b/firmware/common/oversample.hpp @@ -40,17 +40,29 @@ * The baseband needs to know how to correctly decimate (or interpolate) so * the set of allowed scalars is fixed (See OversampleRate enum). * In testing, a minimum rate of 400kHz seems to the functional minimum. + * + * There are several different concepts or terms related to Capture and Replay, + * (1) oversampling (x8, x16 ,...) / decimation (/8, /16...) + * In Capture App , when ADC can not handle directly a requiered low sample rates , + * we need to apply oversampling (x8. x16 ex) , getting more real samples than needed) by "x_number" , + * and later to write it to SD card with the proper needed real sample rate , we apply Decimation , "/ number" . + * + * (2) up-sampling or re-escale or interpolation. (x8, x16, ...) + * In Replay-list App, when we got too low bit rate data for Hackrf , + * we need to upsampling or interpolate or resampling to increase those low bit rates + * to a proper sample rate higher than the min. to be able to be transmitted by Hackrf. */ /* Gets the oversample rate for a given sample rate. * The oversample rate is used to increase the sample rate to improve SNR and quality. * This is also used as the interpolation rate when replaying captures. */ inline OversampleRate get_oversample_rate(uint32_t sample_rate) { - if (sample_rate < 50'000) return OversampleRate::x128; // 25kk..12k5, prepared for future, OVS ok, but decim. :128 still not implemented. - if (sample_rate < 100'000) return OversampleRate::x64; // 50k, prepared for future, OVS ok, but decim. :64 still not implemented. - if (sample_rate < 250'000) return OversampleRate::x16; // Now tentatively applied to 150k..100k - but as soon as we got decim :32, (150k x16, and 100k x32) + if (sample_rate < 50'000) return OversampleRate::x128; // 25k..12k5, prepared for future, OVS ok, but decim. x128 still not implemented. + if (sample_rate < 100'000) return OversampleRate::x64; // 50k, prepared for future, OVS ok, but decim. x64 still not implemented. + if (sample_rate < 150'000) return OversampleRate::x32; // 100k needs x32 + if (sample_rate < 250'000) return OversampleRate::x16; // 150k needs x16 - return OversampleRate::x8; // 250k .. 1Mhz, that decim :8 , is already applied.(OVS and decim OK) + return OversampleRate::x8; // 250k .. 1Mhz, that decim x8 , is already applied.(OVerSampling and decim OK) } /* Gets the actual sample rate for a given sample rate.