Extend REC low bit rate bandwith options in Capture App till 75khz (#1380)

* working_x32_Capture_till_75Khz

* format issues

* apply better accurate comments

* auto format issues
This commit is contained in:
Brumi-2021 2023-08-16 23:26:59 +02:00 committed by GitHub
parent e1cc0b1ad0
commit 12dbf957b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 97 additions and 19 deletions

View File

@ -78,6 +78,7 @@ options_t freqman_bandwidths[4] = {
{"16k", 16000}, {"16k", 16000},
{"25k", 25000}, {"25k", 25000},
{"50k", 50000}, {"50k", 50000},
{"75k", 75000},
{"100k", 100000}, {"100k", 100000},
{"150k", 150000}, {"150k", 150000},
{"250k", 250000}, {"250k", 250000},

View File

@ -383,7 +383,7 @@ void WaterfallView::on_audio_spectrum() {
// TODO: Comments below refer to a fixed oversample rate (8x), cleanup. // TODO: Comments below refer to a fixed oversample rate (8x), cleanup.
uint32_t filter_bandwidth_for_sampling_rate(int32_t sampling_rate) { 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. 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. 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 case 4'000'000 ... 6'000'000: // BW capture range (500k...750kHz max) fs_max = 8 x 750kHz = 6Mhz

View File

@ -145,11 +145,11 @@ OversampleRate RecordView::get_oversample_rate(uint32_t sample_rate) {
auto rate = ::get_oversample_rate(sample_rate); auto rate = ::get_oversample_rate(sample_rate);
// Currently proc_capture only supports x8 and x16 for decimation. // Currently proc_capture only supports x8, x16, x32 for decimation.
if (rate < OversampleRate::x8) if (rate < OversampleRate::x8) // clipping while x4 is not implemented yet.
rate = OversampleRate::x8; rate = OversampleRate::x8;
else if (rate > OversampleRate::x16) else if (rate > OversampleRate::x32) // clipping while x64 is not implemented yet .
rate = OversampleRate::x16; rate = OversampleRate::x32;
return rate; return rate;
} }

View File

@ -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.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_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); channel_spectrum.set_decimation_factor(1);
baseband_thread.start(); baseband_thread.start();
@ -39,11 +40,16 @@ CaptureProcessor::CaptureProcessor() {
void CaptureProcessor::execute(const buffer_c8_t& buffer) { void CaptureProcessor::execute(const buffer_c8_t& buffer) {
/* 2.4576MHz, 2048 samples */ /* 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 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_1_2.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_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& decimator_out = decim_1_out;
const auto& channel = decimator_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); 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 auto decim_0_factor = oversample_rate == OversampleRate::x8
? decim_0_4.decimation_factor ? decim_0_4.decimation_factor
: decim_0_8.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_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_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; 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) { buffer_c16_t CaptureProcessor::decim_0_execute(const buffer_c8_t& src, const buffer_c16_t& dst) {
switch (oversample_rate) { 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) 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 { } else {
return decim_0_8_180k.execute(src, dst); // decim_0 /8 with single decim stage return decim_0_8_180k.execute(src, dst); // decim_0 /8 with single decim stage
} }
case OversampleRate::x16: 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: default:
chDbgPanic("Unhandled OversampleRate"); chDbgPanic("Unhandled OversampleRate");

View File

@ -57,7 +57,8 @@ class CaptureProcessor : public BasebandProcessor {
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0_4{}; dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0_4{};
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0_8{}; dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0_8{};
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0_8_180k{}; 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_low_f = 0;
int32_t channel_filter_high_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. */ /* 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); 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__*/ #endif /*__PROC_CAPTURE_HPP__*/

View File

@ -40,17 +40,29 @@
* The baseband needs to know how to correctly decimate (or interpolate) so * The baseband needs to know how to correctly decimate (or interpolate) so
* the set of allowed scalars is fixed (See OversampleRate enum). * the set of allowed scalars is fixed (See OversampleRate enum).
* In testing, a minimum rate of 400kHz seems to the functional minimum. * 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. /* Gets the oversample rate for a given sample rate.
* The oversample rate is used to increase the sample rate to improve SNR and quality. * 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. */ * This is also used as the interpolation rate when replaying captures. */
inline OversampleRate get_oversample_rate(uint32_t sample_rate) { 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 < 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. :64 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 < 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 < 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. /* Gets the actual sample rate for a given sample rate.