mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-08-12 16:35:43 -04:00
Explain and clean up decimator scalars (#1422)
This commit is contained in:
parent
4bc752b7a8
commit
f46e20c977
16 changed files with 51 additions and 73 deletions
|
@ -23,19 +23,26 @@
|
||||||
#define __DSP_DECIMATE_H__
|
#define __DSP_DECIMATE_H__
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
#include "utility.hpp"
|
|
||||||
|
|
||||||
#include "dsp_types.hpp"
|
#include "dsp_types.hpp"
|
||||||
|
|
||||||
#include "simd.hpp"
|
#include "simd.hpp"
|
||||||
|
#include "utility.hpp"
|
||||||
|
|
||||||
namespace dsp {
|
namespace dsp {
|
||||||
namespace decimate {
|
namespace decimate {
|
||||||
|
|
||||||
|
/* "Saturating" scalars used by decimators to scale either
|
||||||
|
* 8 or 16 bit complex values into 32 bit complex values.
|
||||||
|
* Some of the decimators accept a scale factor as part of
|
||||||
|
* configuration, which is then passed to scale_round_and_pack. */
|
||||||
|
// c8_to_c32_sat_scalar == 2^25. 2^25 * 2^7 (signed C8 Max) == 2^32.
|
||||||
|
constexpr int32_t c8_to_c32_sat_scalar = 0x2000000;
|
||||||
|
// c16_to_c32_sat_scalar == 2^17. 2^17 * 2^15 (signed C16 Max) == 2^32.
|
||||||
|
constexpr int32_t c16_to_c32_sat_scalar = 0x20000;
|
||||||
|
|
||||||
class Complex8DecimateBy2CIC3 {
|
class Complex8DecimateBy2CIC3 {
|
||||||
public:
|
public:
|
||||||
buffer_c16_t execute(
|
buffer_c16_t execute(
|
||||||
|
@ -100,7 +107,7 @@ class FIRC8xR16x24FS4Decim4 {
|
||||||
|
|
||||||
void configure(
|
void configure(
|
||||||
const std::array<tap_t, taps_count>& taps,
|
const std::array<tap_t, taps_count>& taps,
|
||||||
const int32_t scale,
|
const int32_t scale = c8_to_c32_sat_scalar,
|
||||||
const Shift shift = Shift::Down);
|
const Shift shift = Shift::Down);
|
||||||
|
|
||||||
buffer_c16_t execute(
|
buffer_c16_t execute(
|
||||||
|
@ -128,7 +135,7 @@ class FIRC8xR16x24FS4Decim8 {
|
||||||
|
|
||||||
void configure(
|
void configure(
|
||||||
const std::array<tap_t, taps_count>& taps,
|
const std::array<tap_t, taps_count>& taps,
|
||||||
const int32_t scale,
|
const int32_t scale = c8_to_c32_sat_scalar,
|
||||||
const Shift shift = Shift::Down);
|
const Shift shift = Shift::Down);
|
||||||
|
|
||||||
buffer_c16_t execute(
|
buffer_c16_t execute(
|
||||||
|
@ -151,7 +158,7 @@ class FIRC16xR16x16Decim2 {
|
||||||
|
|
||||||
void configure(
|
void configure(
|
||||||
const std::array<tap_t, taps_count>& taps,
|
const std::array<tap_t, taps_count>& taps,
|
||||||
const int32_t scale);
|
const int32_t scale = c16_to_c32_sat_scalar);
|
||||||
|
|
||||||
buffer_c16_t execute(
|
buffer_c16_t execute(
|
||||||
const buffer_c16_t& src,
|
const buffer_c16_t& src,
|
||||||
|
@ -173,7 +180,7 @@ class FIRC16xR16x32Decim8 {
|
||||||
|
|
||||||
void configure(
|
void configure(
|
||||||
const std::array<tap_t, taps_count>& taps,
|
const std::array<tap_t, taps_count>& taps,
|
||||||
const int32_t scale);
|
const int32_t scale = c16_to_c32_sat_scalar);
|
||||||
|
|
||||||
buffer_c16_t execute(
|
buffer_c16_t execute(
|
||||||
const buffer_c16_t& src,
|
const buffer_c16_t& src,
|
||||||
|
|
|
@ -29,8 +29,8 @@
|
||||||
#include "event_m4.hpp"
|
#include "event_m4.hpp"
|
||||||
|
|
||||||
ACARSProcessor::ACARSProcessor() {
|
ACARSProcessor::ACARSProcessor() {
|
||||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
decim_0.configure(taps_11k0_decim_0.taps);
|
||||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
decim_1.configure(taps_11k0_decim_1.taps);
|
||||||
packet.clear();
|
packet.clear();
|
||||||
baseband_thread.start();
|
baseband_thread.start();
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,8 +154,8 @@ void AFSKRxProcessor::configure(const AFSKRxConfigureMessage& message) {
|
||||||
|
|
||||||
const size_t demod_input_fs = channel_filter_output_fs;*/
|
const size_t demod_input_fs = channel_filter_output_fs;*/
|
||||||
|
|
||||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
decim_0.configure(taps_11k0_decim_0.taps);
|
||||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
decim_1.configure(taps_11k0_decim_1.taps);
|
||||||
channel_filter.configure(taps_11k0_channel.taps, 2);
|
channel_filter.configure(taps_11k0_channel.taps, 2);
|
||||||
demod.configure(audio_fs, 5000);
|
demod.configure(audio_fs, 5000);
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,8 @@
|
||||||
#include "event_m4.hpp"
|
#include "event_m4.hpp"
|
||||||
|
|
||||||
AISProcessor::AISProcessor() {
|
AISProcessor::AISProcessor() {
|
||||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
decim_0.configure(taps_11k0_decim_0.taps);
|
||||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
decim_1.configure(taps_11k0_decim_1.taps);
|
||||||
baseband_thread.start();
|
baseband_thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -89,8 +89,8 @@ void NarrowbandAMAudio::configure(const AMConfigureMessage& message) {
|
||||||
constexpr size_t channel_filter_input_fs = decim_2_output_fs;
|
constexpr size_t channel_filter_input_fs = decim_2_output_fs;
|
||||||
// const size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor;
|
// const size_t channel_filter_output_fs = channel_filter_input_fs / channel_filter_decimation_factor;
|
||||||
|
|
||||||
decim_0.configure(message.decim_0_filter.taps, 33554432);
|
decim_0.configure(message.decim_0_filter.taps);
|
||||||
decim_1.configure(message.decim_1_filter.taps, 131072);
|
decim_1.configure(message.decim_1_filter.taps);
|
||||||
decim_2.configure(message.decim_2_filter.taps, decim_2_decimation_factor);
|
decim_2.configure(message.decim_2_filter.taps, decim_2_decimation_factor);
|
||||||
channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor);
|
channel_filter.configure(message.channel_filter.taps, channel_filter_decimation_factor);
|
||||||
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
|
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
|
||||||
|
|
|
@ -223,8 +223,8 @@ void APRSRxProcessor::capture_config(const CaptureConfigMessage& message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void APRSRxProcessor::configure(const APRSRxConfigureMessage& message) {
|
void APRSRxProcessor::configure(const APRSRxConfigureMessage& message) {
|
||||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
decim_0.configure(taps_11k0_decim_0.taps);
|
||||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
decim_1.configure(taps_11k0_decim_1.taps);
|
||||||
channel_filter.configure(taps_11k0_channel.taps, 2);
|
channel_filter.configure(taps_11k0_channel.taps, 2);
|
||||||
demod.configure(audio_fs, 5000);
|
demod.configure(audio_fs, 5000);
|
||||||
|
|
||||||
|
|
|
@ -276,8 +276,8 @@ void BTLERxProcessor::on_message(const Message* const message) {
|
||||||
|
|
||||||
void BTLERxProcessor::configure(const BTLERxConfigureMessage& message) {
|
void BTLERxProcessor::configure(const BTLERxConfigureMessage& message) {
|
||||||
(void)message; // avoid warning
|
(void)message; // avoid warning
|
||||||
decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
|
decim_0.configure(taps_200k_wfm_decim_0.taps);
|
||||||
decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
|
decim_1.configure(taps_200k_wfm_decim_1.taps);
|
||||||
demod.configure(audio_fs, 5000);
|
demod.configure(audio_fs, 5000);
|
||||||
|
|
||||||
configured = true;
|
configured = true;
|
||||||
|
|
|
@ -104,43 +104,38 @@ void CaptureProcessor::sample_rate_config(const SampleRateConfigMessage& message
|
||||||
if (sample_rate >= 1'500'000)
|
if (sample_rate >= 1'500'000)
|
||||||
spectrum_interval_samples /= (sample_rate / 750'000);
|
spectrum_interval_samples /= (sample_rate / 750'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) {
|
switch (message.oversample_rate) {
|
||||||
case OversampleRate::x4:
|
case OversampleRate::x4:
|
||||||
// M4 can't handle 2 decimation passes for sample rates needing 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_0.set<FIRC8xR16x24FS4Decim4>().configure(taps_200k_decim_0.taps);
|
||||||
decim_1.set<NoopDecim>();
|
decim_1.set<NoopDecim>();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OversampleRate::x8:
|
case OversampleRate::x8:
|
||||||
// M4 can't handle 2 decimation passes for sample rates <= 600k.
|
// M4 can't handle 2 decimation passes for sample rates <= 600k.
|
||||||
if (message.sample_rate < 600'000) {
|
if (message.sample_rate < 600'000) {
|
||||||
decim_0.set<FIRC8xR16x24FS4Decim4>().configure(taps_200k_decim_0.taps, decim_0_scale);
|
decim_0.set<FIRC8xR16x24FS4Decim4>().configure(taps_200k_decim_0.taps);
|
||||||
decim_1.set<FIRC16xR16x16Decim2>().configure(taps_200k_decim_1.taps, decim_1_scale);
|
decim_1.set<FIRC16xR16x16Decim2>().configure(taps_200k_decim_1.taps);
|
||||||
} else {
|
} else {
|
||||||
// Using 180k taps to provide better filtering with a single pass.
|
// 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_0.set<FIRC8xR16x24FS4Decim8>().configure(taps_180k_wfm_decim_0.taps);
|
||||||
decim_1.set<NoopDecim>();
|
decim_1.set<NoopDecim>();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OversampleRate::x16:
|
case OversampleRate::x16:
|
||||||
decim_0.set<FIRC8xR16x24FS4Decim8>().configure(taps_200k_decim_0.taps, decim_0_scale);
|
decim_0.set<FIRC8xR16x24FS4Decim8>().configure(taps_200k_decim_0.taps);
|
||||||
decim_1.set<FIRC16xR16x16Decim2>().configure(taps_200k_decim_1.taps, decim_1_scale);
|
decim_1.set<FIRC16xR16x16Decim2>().configure(taps_200k_decim_1.taps);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OversampleRate::x32:
|
case OversampleRate::x32:
|
||||||
decim_0.set<FIRC8xR16x24FS4Decim4>().configure(taps_200k_decim_0.taps, decim_0_scale);
|
decim_0.set<FIRC8xR16x24FS4Decim4>().configure(taps_200k_decim_0.taps);
|
||||||
decim_1.set<FIRC16xR16x32Decim8>().configure(taps_16k0_decim_1.taps, decim_1_scale);
|
decim_1.set<FIRC16xR16x32Decim8>().configure(taps_16k0_decim_1.taps);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OversampleRate::x64:
|
case OversampleRate::x64:
|
||||||
decim_0.set<FIRC8xR16x24FS4Decim8>().configure(taps_200k_decim_0.taps, decim_0_scale);
|
decim_0.set<FIRC8xR16x24FS4Decim8>().configure(taps_200k_decim_0.taps);
|
||||||
decim_1.set<FIRC16xR16x32Decim8>().configure(taps_16k0_decim_1.taps, decim_1_scale);
|
decim_1.set<FIRC16xR16x32Decim8>().configure(taps_16k0_decim_1.taps);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -144,8 +144,8 @@ void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) {
|
||||||
|
|
||||||
const size_t demod_input_fs = channel_filter_output_fs;
|
const size_t demod_input_fs = channel_filter_output_fs;
|
||||||
|
|
||||||
decim_0.configure(message.decim_0_filter.taps, 33554432);
|
decim_0.configure(message.decim_0_filter.taps);
|
||||||
decim_1.configure(message.decim_1_filter.taps, 131072);
|
decim_1.configure(message.decim_1_filter.taps);
|
||||||
channel_filter.configure(message.channel_filter.taps, message.channel_decimation);
|
channel_filter.configure(message.channel_filter.taps, message.channel_decimation);
|
||||||
demod.configure(demod_input_fs, message.deviation);
|
demod.configure(demod_input_fs, message.deviation);
|
||||||
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
|
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
|
||||||
|
|
|
@ -243,8 +243,8 @@ void NRFRxProcessor::on_message(const Message* const message) {
|
||||||
|
|
||||||
void NRFRxProcessor::configure(const NRFRxConfigureMessage& message) {
|
void NRFRxProcessor::configure(const NRFRxConfigureMessage& message) {
|
||||||
(void)message; // avoir unused warning
|
(void)message; // avoir unused warning
|
||||||
decim_0.configure(taps_200k_wfm_decim_0.taps, 33554432);
|
decim_0.configure(taps_200k_wfm_decim_0.taps);
|
||||||
decim_1.configure(taps_200k_wfm_decim_1.taps, 131072);
|
decim_1.configure(taps_200k_wfm_decim_1.taps);
|
||||||
demod.configure(audio_fs, 5000);
|
demod.configure(audio_fs, 5000);
|
||||||
|
|
||||||
configured = true;
|
configured = true;
|
||||||
|
|
|
@ -107,8 +107,8 @@ void POCSAGProcessor::configure() {
|
||||||
|
|
||||||
const size_t demod_input_fs = channel_filter_output_fs;
|
const size_t demod_input_fs = channel_filter_output_fs;
|
||||||
|
|
||||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
decim_0.configure(taps_11k0_decim_0.taps);
|
||||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
decim_1.configure(taps_11k0_decim_1.taps);
|
||||||
channel_filter.configure(taps_11k0_channel.taps, 2);
|
channel_filter.configure(taps_11k0_channel.taps, 2);
|
||||||
demod.configure(demod_input_fs, 4'500); // FSK +/- 4k5Hz.
|
demod.configure(demod_input_fs, 4'500); // FSK +/- 4k5Hz.
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,8 @@
|
||||||
#include "audio_output.hpp"
|
#include "audio_output.hpp"
|
||||||
|
|
||||||
SondeProcessor::SondeProcessor() {
|
SondeProcessor::SondeProcessor() {
|
||||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
decim_0.configure(taps_11k0_decim_0.taps);
|
||||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
decim_1.configure(taps_11k0_decim_1.taps);
|
||||||
|
|
||||||
audio_output.configure(false);
|
audio_output.configure(false);
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
#include "event_m4.hpp"
|
#include "event_m4.hpp"
|
||||||
|
|
||||||
TestProcessor::TestProcessor() {
|
TestProcessor::TestProcessor() {
|
||||||
decim_0.configure(taps_11k0_decim_0.taps, 33554432);
|
decim_0.configure(taps_11k0_decim_0.taps);
|
||||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
decim_1.configure(taps_11k0_decim_1.taps);
|
||||||
baseband_thread.start();
|
baseband_thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,8 @@
|
||||||
#include "event_m4.hpp"
|
#include "event_m4.hpp"
|
||||||
|
|
||||||
TPMSProcessor::TPMSProcessor() {
|
TPMSProcessor::TPMSProcessor() {
|
||||||
decim_0.configure(taps_200k_decim_0.taps, 33554432);
|
decim_0.configure(taps_200k_decim_0.taps);
|
||||||
decim_1.configure(taps_200k_decim_1.taps, 131072);
|
decim_1.configure(taps_200k_decim_1.taps);
|
||||||
baseband_thread.start();
|
baseband_thread.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -165,8 +165,8 @@ void WidebandFMAudio::configure(const WFMConfigureMessage& message) {
|
||||||
spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz;
|
spectrum_interval_samples = decim_1_output_fs / spectrum_rate_hz;
|
||||||
spectrum_samples = 0;
|
spectrum_samples = 0;
|
||||||
|
|
||||||
decim_0.configure(message.decim_0_filter.taps, 33554432);
|
decim_0.configure(message.decim_0_filter.taps);
|
||||||
decim_1.configure(message.decim_1_filter.taps, 131072);
|
decim_1.configure(message.decim_1_filter.taps);
|
||||||
channel_filter_low_f = message.decim_1_filter.low_frequency_normalized * decim_1_input_fs;
|
channel_filter_low_f = message.decim_1_filter.low_frequency_normalized * decim_1_input_fs;
|
||||||
channel_filter_high_f = message.decim_1_filter.high_frequency_normalized * decim_1_input_fs;
|
channel_filter_high_f = message.decim_1_filter.high_frequency_normalized * decim_1_input_fs;
|
||||||
channel_filter_transition = message.decim_1_filter.transition_normalized * decim_1_input_fs;
|
channel_filter_transition = message.decim_1_filter.transition_normalized * decim_1_input_fs;
|
||||||
|
|
|
@ -36,24 +36,12 @@ struct complex<int8_t> {
|
||||||
typedef int8_t value_type;
|
typedef int8_t value_type;
|
||||||
typedef uint16_t rep_type;
|
typedef uint16_t rep_type;
|
||||||
|
|
||||||
// constexpr complex(
|
|
||||||
// rep_type r
|
|
||||||
// ) : _rep { r }
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
|
|
||||||
constexpr complex(
|
constexpr complex(
|
||||||
int8_t re = 0,
|
int8_t re = 0,
|
||||||
int8_t im = 0)
|
int8_t im = 0)
|
||||||
: _v{re, im} {
|
: _v{re, im} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// constexpr complex(
|
|
||||||
// const complex& o
|
|
||||||
// ) : _rep { o._rep }
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
|
|
||||||
constexpr int8_t real() const { return _v[0]; }
|
constexpr int8_t real() const { return _v[0]; }
|
||||||
constexpr int8_t imag() const { return _v[1]; }
|
constexpr int8_t imag() const { return _v[1]; }
|
||||||
|
|
||||||
|
@ -77,24 +65,12 @@ struct complex<int16_t> {
|
||||||
typedef int16_t value_type;
|
typedef int16_t value_type;
|
||||||
typedef uint32_t rep_type;
|
typedef uint32_t rep_type;
|
||||||
|
|
||||||
// constexpr complex(
|
|
||||||
// rep_type r
|
|
||||||
// ) : _rep { r }
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
|
|
||||||
constexpr complex(
|
constexpr complex(
|
||||||
int16_t re = 0,
|
int16_t re = 0,
|
||||||
int16_t im = 0)
|
int16_t im = 0)
|
||||||
: _v{re, im} {
|
: _v{re, im} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// constexpr complex(
|
|
||||||
// const complex& o
|
|
||||||
// ) : _rep { o._rep }
|
|
||||||
// {
|
|
||||||
// }
|
|
||||||
|
|
||||||
constexpr int16_t real() const { return _v[0]; }
|
constexpr int16_t real() const { return _v[0]; }
|
||||||
constexpr int16_t imag() const { return _v[1]; }
|
constexpr int16_t imag() const { return _v[1]; }
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue