Support for 1.25MHz capture (#1418)

* Advanced draft decim /4 just waterfall ok

* apply some Kall's corrections + formatting

* Tidy up both decim_factors

* New refine optimizations

* Format issues

* more format issues ...mmmm

* comments update

* WIP Cleanup

* WIP

* WIP - add variant

* Use std::visit to dispatch MultiDecimator -- fluent API

* Clean up comments

* Merge next and fix compilation

* Fix odd loop in BlockDecimator

* Clean up spectrum math

* Descibe spectrum update math better, more clear math.

* Apply spectrum interval correction at 1.5M

* Increase replay buffer to handle x4 ovs

---------

Co-authored-by: Brumi-2021 <ea3hqj@gmail.com>
This commit is contained in:
Kyle Reed 2023-08-29 10:26:38 -07:00 committed by GitHub
parent e7e1bedcad
commit de81156223
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 170 additions and 189 deletions

View File

@ -83,16 +83,22 @@ options_t freqman_bandwidths[4] = {
{"100k", 100000},
{"150k", 150000},
{"250k", 250000},
{"500k", 500000}, /* Previous Limit bandwith Option with perfect micro SD write .C16 format operaton.*/
{"600k", 600000}, /* We doubled x2 previous REC BW limit , now extended BW from 600k to 1M with fast enough SD card in C16 or C8 format .*/
{"650k", 650000},
{"500k", 500000},
{"600k", 600000},
{"750k", 750000},
{"1000k", 1000000}, /* New limit bandwith option for recording in C16 (in fast SD card) or in C8 */
{"1500k", 1500000}, /* From this BW onwards, the LCD is ok, but M4 CPU is having periodical sample rec dropps, (not real file size, accelerated replay) */
{"1000k", 1000000}, // Max bandwith for recording in C16 (with fast SD card).
{"1250k", 1250000},
{"1500k", 1500000},
{"1750k", 1750000},
{"2000k", 2000000},
{"2500k", 2500000},
{"2750k", 2750000}, // That is our max Capture option, to keep using later / 8 decimation (22Mhz sampling ADC)
{"2250k", 2250000}, // Max bandwith for recording in C8 (with fast SD card).
{"2500k", 2500000}, // Here and up, LCD is ok, but M4 CPU drops samples.
{"3000k", 3000000},
{"3500k", 3500000},
{"4000k", 4000000},
{"4500k", 4500000},
{"5000k", 5500000},
{"5500k", 5500000}, // Max capture, needs /4 decimation, (22Mhz sampling ADC).
},
};

View File

@ -383,33 +383,33 @@ 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'500'000: // BW Captured range (0 <= 250kHz max) fs = 8x250 kHz =2000., 16x150 khz =2400, 32x100 khz =3200, (32x75k = 2400), (future 64x40 khz =2400)
case 0 ... 3'500'000: // BW Captured range (0 <= 250kHz max) fs = 8x250k = 2000, 16x150k = 2400, 32x100k = 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
case 4'000'000 ... 7'000'000: // OVS x8, BW capture range (500k...750kHz max) fs_max = 8 x 750k = 6Mhz
// BW 500k...750kHz, ex. 500kHz (fs = 8 x BW = 4Mhz), BW 600kHz (fs = 4,8Mhz), BW 750 kHz (fs = 6Mhz).
return 2'500'000; // In some IC, MAX2837 appears as 2250000, but both work similarly.
case 8'000'000: // BW capture 1Mhz fs = 8 x 1Mhz = 8Mhz. (1Mhz showed slightly higher noise background).
return 3'500'000; // some low SD cards, if not showing avg. writting speed >4MB/sec, they will produce sammples drop at REC with 1MB and C16 format.
case 7'000'001 ... 10'000'000: // OVS x8 and x4, BW capture 1Mhz fs = 8 x 1Mhz = 8Mhz. (1Mhz showed slightly higher noise background).
return 3'500'000; // some low SD cards, if not showing avg. writing speed >4MB/sec, they will produce sammples drop at REC with 1MB and C16 format.
case 12'000'000: // BW capture 1,5Mhz, fs = 8 x 1,5Mhz = 12Mhz
// Good BPF, good matching, we have some periodical M4 % samples drop.
case 12'000'000 ... 14'000'000: // OVS x4, BW capture 3Mhz, fs = 4 x 3Mhz = 12Mhz
// Good BPF, good matching, we have some periodical M4 % samples drop.
return 5'000'000;
case 14'000'000: // BW capture 1,75Mhz, fs = 8 x 1,75Mhz = 14Mhz
case 16'000'000: // OVS x4, BW capture 4Mhz, fs = 4 x 4Mhz = 16Mhz
// Good BPF, good matching, we have some periodical M4 % samples drop.
return 5'000'000;
return 5'500'000;
case 16'000'000: // BW capture 2Mhz, fs = 8 x 2Mhz = 16Mhz
case 18'000'000: // OVS x4, BW capture 4,5Mhz, fs = 4 x 4,5Mhz = 18Mhz
// Good BPF, good matching, we have some periodical M4 % samples drop.
return 6'000'000;
case 20'000'000: // BW capture 2,5Mhz, fs = 8 x 2,5 Mhz = 20Mhz
case 20'000'000: // OVS x4, BW capture 5Mhz, fs = 4 x 5Mhz = 20Mhz
// Good BPF, good matching, we have some periodical M4 % samples drop.
return 7'000'000;
default: // BW capture 2,75Mhz, fs = 8 x 2,75Mhz = 22Mhz max ADC sampling and others.
default: // BW capture 5,5Mhz, fs = 4 x 5,5Mhz = 22Mhz max ADC sampling and others.
// We tested also 9Mhz FPB slightly too much noise floor, better at 8Mhz.
return 8'000'000;
}

View File

@ -107,13 +107,9 @@ uint32_t RecordView::set_sampling_rate(uint32_t new_sampling_rate) {
auto oversample_rate = get_oversample_rate(new_sampling_rate);
auto actual_sampling_rate = new_sampling_rate * toUType(oversample_rate);
/* We are changing "REC" icon background to yellow in BW rec Options >1Mhz
* > 1Mhz BW options , we are NOT recording full IQ .C16 files (those files has some periodical missing dropped samples).
* Those recorded files, has not the full IQ samples information, looks like decimated in file size.
* They are ok as recorded spectrum indication, but they should not be used by Replay app. (the voice speed will be accelerated)
* We keep original black background in all the correct IQ .C16 files BW's Options. */
if (actual_sampling_rate > 8'000'000) { // yellow REC button means not ok for REC, BW >1Mhz (BW from 12k5 till 1Mhz OK for REC and Replay)
// Change the "REC" icon background to yellow when the selected rate exceeds hardware limits.
// Above this threshold, samples will be dropped resulting incomplete capture files.
if (new_sampling_rate > 1'250'000) {
button_record.set_background(ui::Color::yellow());
} else {
button_record.set_background(ui::Color::black());
@ -142,15 +138,7 @@ OversampleRate RecordView::get_oversample_rate(uint32_t sample_rate) {
if (file_type == FileType::WAV)
return OversampleRate::None;
auto rate = ::get_oversample_rate(sample_rate);
// Currently proc_capture only supports /8, /16, /32 for decimation.
if (rate < OversampleRate::x8) // clipping while /4 is not implemented yet (it will be used >1Mhz onwards when available)
rate = OversampleRate::x8;
else if (rate > OversampleRate::x64) // clipping while /128 is not implemented yet , (but it is not necessary for 12k5)
rate = OversampleRate::x64;
return rate;
return ::get_oversample_rate(sample_rate);
}
// Setter for datetime and frequency filename

View File

@ -69,29 +69,22 @@ class BlockDecimator {
set_input_sampling_rate(src.sampling_rate);
while (src_i < src.count) {
for (size_t src_i = 0; src_i < src.count; src_i += factor()) {
buffer[dst_i++] = src.p[src_i];
if (dst_i == buffer.size()) {
callback({buffer.data(), buffer.size(), output_sampling_rate()});
reset_state();
dst_i = 0;
}
src_i += factor();
}
src_i -= src.count;
}
private:
std::array<T, N> buffer{};
uint32_t input_sampling_rate_{0};
size_t factor_{1};
size_t src_i{0};
size_t dst_i{0};
void reset_state() {
src_i = 0;
dst_i = 0;
}
};

View File

@ -202,8 +202,8 @@ buffer_c16_t FIRC8xR16x24FS4Decim4::execute(
uint32_t* const d = static_cast<uint32_t*>(__builtin_assume_aligned(dst.p, 4));
const auto k = output_scale;
const size_t count = src.count / decimation_factor;
for (size_t i = 0; i < count; i++) {
const vec4_s8* const in = static_cast<const vec4_s8*>(__builtin_assume_aligned(&src.p[i * decimation_factor], 4));

View File

@ -26,48 +26,32 @@
#include "event_m4.hpp"
#include "utility.hpp"
using namespace dsp::decimate;
CaptureProcessor::CaptureProcessor() {
decim_0_4.configure(taps_200k_decim_0.taps, 33554432); // to be used with decim1 (/2), then total two stages decim (/8)
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_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();
}
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 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_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;
auto decim_0_out = decim_0.execute(buffer, dst_buffer);
auto out_buffer = decim_1.execute(decim_0_out, dst_buffer);
if (stream) {
const size_t bytes_to_write = sizeof(*decimator_out.p) * decimator_out.count;
const size_t written = stream->write(decimator_out.p, bytes_to_write);
const size_t bytes_to_write = sizeof(*out_buffer.p) * out_buffer.count;
const size_t written = stream->write(out_buffer.p, bytes_to_write);
if (written != bytes_to_write) {
// TODO eventually report error somewhere
// TODO: Send an error message to the app?
}
}
feed_channel_stats(channel);
feed_channel_stats(out_buffer);
spectrum_samples += channel.count;
spectrum_samples += out_buffer.count;
if (spectrum_samples >= spectrum_interval_samples) {
spectrum_samples -= spectrum_interval_samples;
channel_spectrum.feed(channel, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
channel_spectrum.feed(out_buffer, channel_filter_low_f,
channel_filter_high_f, channel_filter_transition);
}
}
@ -92,120 +76,88 @@ void CaptureProcessor::on_message(const Message* const message) {
}
void CaptureProcessor::sample_rate_config(const SampleRateConfigMessage& message) {
baseband_fs = message.sample_rate * toUType(message.oversample_rate);
oversample_rate = message.oversample_rate;
const auto sample_rate = message.sample_rate;
// The actual sample rate is the requested rate * the oversample rate.
// See oversample.hpp for more details on oversampling.
baseband_fs = sample_rate * toUType(message.oversample_rate);
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;
// TODO: Do we need to use the taps that the decimators get configured with?
channel_filter_low_f = taps_200k_decim_1.low_frequency_normalized * sample_rate;
channel_filter_high_f = taps_200k_decim_1.high_frequency_normalized * sample_rate;
channel_filter_transition = taps_200k_decim_1.transition_normalized * sample_rate;
size_t decim_0_output_fs = baseband_fs / decim_0_factor;
size_t decim_1_input_fs = decim_0_output_fs;
// Compute the scalar that corrects the oversample_rate to be x8 when computing
// the spectrum update interval. The original implementation only supported x8.
// TODO: Why is this needed here but not in proc_replay? There must be some other
// assumption about x8 oversampling in some component that makes this necessary.
const auto oversample_correction = toUType(message.oversample_rate) / 8.0;
// The spectrum update interval controls how often the waterfall is fed new samples.
spectrum_interval_samples = sample_rate / (spectrum_rate_hz * oversample_correction);
spectrum_samples = 0;
// For high sample rates, the M4 is busy collecting samples so the
// waterfall runs slower. Reduce the update interval so it runs faster.
// NB: Trade off: looks nicer, but more frequent updates == more CPU.
if (sample_rate >= 1'500'000)
spectrum_interval_samples /= (sample_rate / 500'000);
// Mystery scalars for decimator configuration.
// TODO: figure these out and add a real comment.
constexpr int decim_0_scale = 0x2000000;
constexpr int decim_1_scale = 0x20000;
switch (message.oversample_rate) {
case OversampleRate::x4:
// M4 can't handle 2 decimation passes for sample rates needing x4.
decim_0.set<FIRC8xR16x24FS4Decim4>().configure(taps_200k_decim_0.taps, decim_0_scale);
decim_1.set<NoopDecim>();
break;
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
// M4 can't handle 2 decimation passes for sample rates <= 600k.
if (message.sample_rate < 600'000) {
decim_0.set<FIRC8xR16x24FS4Decim4>().configure(taps_200k_decim_0.taps, decim_0_scale);
decim_1.set<FIRC16xR16x16Decim2>().configure(taps_200k_decim_1.taps, decim_1_scale);
} 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)
// Using 180k taps to provide better filtering with a single pass.
decim_0.set<FIRC8xR16x24FS4Decim8>().configure(taps_180k_wfm_decim_0.taps, decim_0_scale);
decim_1.set<NoopDecim>();
}
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 )
decim_0.set<FIRC8xR16x24FS4Decim8>().configure(taps_200k_decim_0.taps, decim_0_scale);
decim_1.set<FIRC16xR16x16Decim2>().configure(taps_200k_decim_1.taps, decim_1_scale);
break;
case OversampleRate::x32:
decim_1_factor = 2 * decim_1_8.decimation_factor; // /32 = /4x8 (we applied additional *2 correction to speed up waterfall, no effect to scale spectrum)
decim_0.set<FIRC8xR16x24FS4Decim4>().configure(taps_200k_decim_0.taps, decim_0_scale);
decim_1.set<FIRC16xR16x32Decim8>().configure(taps_16k0_decim_1.taps, decim_1_scale);
break;
case OversampleRate::x64:
decim_1_factor = 8 * decim_1_8.decimation_factor; // /64 = /8x8 (we applied additional *8 correction to speed up waterfall, no effect to scale spectrum)
decim_0.set<FIRC8xR16x24FS4Decim8>().configure(taps_200k_decim_0.taps, decim_0_scale);
decim_1.set<FIRC16xR16x32Decim8>().configure(taps_16k0_decim_1.taps, decim_1_scale);
break;
default:
decim_1_factor = 2; // just default initial value to remove compile warning.
chDbgPanic("Unhandled OversampleRate");
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;
channel_filter_transition = taps_200k_decim_1.transition_normalized * decim_1_input_fs;
spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz;
spectrum_samples = 0;
}
void CaptureProcessor::capture_config(const CaptureConfigMessage& message) {
if (message.config) {
if (message.config)
stream = std::make_unique<StreamInput>(message.config);
} else {
else
stream.reset();
}
}
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)
if (baseband_fs < 4800'000) { // 600khz (600k x 8)
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); // decim_0 , /8 with double decim stage
case OversampleRate::x32:
return decim_0_4.execute(src, dst); // decim_0 , /4 with double decim stage
case OversampleRate::x64:
return decim_0_8.execute(src, dst); // decim_0 , /8 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 100khz and 150khz
case OversampleRate::x32:
return decim_1_8.execute(src, dst); // total decim /32 = /4x8, appled to 75k , 50k, 32k
case OversampleRate::x64:
return decim_1_8.execute(src, dst); // total decim /64 = /8x8, appled to 16k and 12k5
default:
chDbgPanic("Unhandled OversampleRate");
return {};
}
}
int main() {
EventDispatcher event_dispatcher{std::make_unique<CaptureProcessor>()};
event_dispatcher.run();
return 0;
}
}

View File

@ -33,6 +33,56 @@
#include <array>
#include <memory>
#include <tuple>
#include <variant>
/* A decimator that just returns the source buffer. */
class NoopDecim {
public:
static constexpr int decimation_factor = 1;
template <typename Buffer>
Buffer execute(const Buffer& src, const Buffer&) {
// TODO: should this copy to 'dst'?
return {src.p, src.count, src.sampling_rate};
}
};
/* Decimator wrapper that can hold one of a set of decimators and dispatch at runtime. */
template <typename... Args>
class MultiDecimator {
public:
/* Dispatches to the underlying type's execute. */
template <typename Source, typename Destination>
Destination execute(
const Source& src,
const Destination& dst) {
return std::visit(
[&src, &dst](auto&& arg) -> Destination {
return arg.execute(src, dst);
},
decimator_);
}
size_t decimation_factor() const {
return std::visit(
[](auto&& arg) -> size_t {
return arg.decimation_factor;
},
decimator_);
}
/* Sets this decimator to a new instance of the specified decimator type.
* NB: The instance is returned by-ref so 'configure' can easily be called. */
template <typename Decimator>
Decimator& set() {
decimator_ = Decimator{};
return std::get<Decimator>(decimator_);
}
private:
std::variant<Args...> decimator_{};
};
class CaptureProcessor : public BasebandProcessor {
public:
@ -50,15 +100,16 @@ class CaptureProcessor : public BasebandProcessor {
dst.data(),
dst.size()};
/* NB: There are two decimation passes: 0 and 1. In pass 0, one of
* the following will be selected based on the oversample rate.
* use decim_0_4 for an overall decimation factor of 8.
* use decim_0_8 for an overall decimation factor of 16. */
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0_4{};
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0_8{};
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0_8_180k{};
dsp::decimate::FIRC16xR16x16Decim2 decim_1_2{};
dsp::decimate::FIRC16xR16x32Decim8 decim_1_8{};
/* The actual type will be configured depending on the sample rate. */
MultiDecimator<
dsp::decimate::FIRC8xR16x24FS4Decim4,
dsp::decimate::FIRC8xR16x24FS4Decim8>
decim_0{};
MultiDecimator<
dsp::decimate::FIRC16xR16x16Decim2,
dsp::decimate::FIRC16xR16x32Decim8,
NoopDecim>
decim_1{};
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;
@ -69,7 +120,6 @@ class CaptureProcessor : public BasebandProcessor {
SpectrumCollector channel_spectrum{};
size_t spectrum_interval_samples = 0;
size_t spectrum_samples = 0;
OversampleRate oversample_rate{OversampleRate::x8};
/* NB: Threads should be the last members in the class definition. */
BasebandThread baseband_thread{
@ -78,12 +128,6 @@ class CaptureProcessor : public BasebandProcessor {
void sample_rate_config(const SampleRateConfigMessage& message);
void capture_config(const CaptureConfigMessage& message);
/* 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__*/

View File

@ -69,7 +69,7 @@ void ReplayProcessor::execute(const buffer_c8_t& buffer) {
chDbgPanic("Output not div.");
// Is the input smaple buffer big enough?
if (samples_to_read > iq_buffer.size())
if (samples_to_read > iq_buffer.count)
chDbgPanic("IQ buf ovf.");
#endif

View File

@ -45,7 +45,7 @@ class ReplayProcessor : public BasebandProcessor {
static constexpr auto spectrum_rate_hz = 50.0f;
// Holds the read IQ data chunk from the file to send.
std::array<complex16_t, 256> iq{};
std::array<complex16_t, 512> iq{};
int32_t channel_filter_low_f = 0;
int32_t channel_filter_high_f = 0;

View File

@ -816,8 +816,8 @@ enum class OversampleRate : uint8_t {
None = 1,
x1 = None,
// 4x would make sense to have, but need to ensure it doesn't
// overrun the IQ read buffer in proc_replay.
/* Oversample rate of 4 times the sample rate. */
x4 = 4,
/* Oversample rate of 8 times the sample rate. */
x8 = 8,
@ -830,9 +830,6 @@ enum class OversampleRate : uint8_t {
/* Oversample rate of 64 times the sample rate. */
x64 = 64,
/* Oversample rate of 128 times the sample rate. */
x128 = 128,
};
class SampleRateConfigMessage : public Message {

View File

@ -42,26 +42,27 @@
* 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" .
* (1) Oversampling (x8, x16, ...) and Decimation (/8, /16...)
* In Capture App, the ADC can not directly handle low sample rates.
* We need to apply oversampling (x8, x16, ...), collecting more "x number" additional real samples.
* To write it to SD card with the desired 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.
* (2) Up-sampling or re-scale or interpolation. (x8, x16, ...).
* In Replay-list App, when the bit rate data is too low for HackRF.
* We need to upsample or interpolate to increase those low bit rates
* to a rate higher than the hardware nminimum 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 < 30'000) return OversampleRate::x64; // 25k, 16k, 12k5.
if (sample_rate < 80'000) return OversampleRate::x32; // 75k, 50k, 32k.
if (sample_rate < 250'000) return OversampleRate::x16; // 100k and 150k.
if (sample_rate < 30'000) return OversampleRate::x64; // 25k, 16k, 12k5.
if (sample_rate < 80'000) return OversampleRate::x32; // 75k, 50k, 32k.
if (sample_rate < 250'000) return OversampleRate::x16; // 100k, 150k.
if (sample_rate < 1'250'000) return OversampleRate::x8; // 250k, 500k, 600k, 650k, 750k, 1Mhz.
return OversampleRate::x8; // 250k .. 1Mhz, that decim x8 , is already applied.(OVerSampling and decim OK)
return OversampleRate::x4; // Top range (1.25Mhz ... 5.5Mhz).
}
/* Gets the actual sample rate for a given sample rate.