mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-10 08:39:29 +00:00
Feature/sliding freq (#3280)
* Add sliding-frequency audio receiver tuning * Move ADS-B receiver to external app * Move AIS receiver to external app * Move APRS receiver to external app * Move APRS transmitter to external app * Isolate filtered spectrum collection from WFM * Fixed the issues #3280 * fpga_bridge.c: init registers per reference fpga_init, add quarter-shift mode setter * radio.hpp: expose cached FPGA quarter-rate shift * radio.cpp: program FPGA quarter-rate shift together with tuning offset * tuning.hpp: pass AFE rate and direction to tuning config, add quarter_shift field * tuning.cpp: import full PRALINE RX/TX tuning tables with quarter-shift offsets * clock_manager: stop writing bogus RX digital gain, keep quarter shift across rate changes * receiver_model: port LPF bandwidth from reference auto_bandwidth, use cached quarter shift * adsb_rx: enable RF amp by default on first run * ui_geomap.hpp: add hemisphere fields, degrees become magnitude * ui_geomap: use hemisphere selector for lat/lon sign, fix minute/second wrap carry * ui_menu: guard select against empty menu * waterfall_designer: header updates for profile file handling * waterfall_designer: exception-free profile parsing, CRLF handling, deferred nav callbacks, backup cleanup * external.ld: scope app section globs to their own object directories * CMakeLists: relink when external.ld changes * tools: add external app symbol placement checker * tools: add guru meditation address lookup script * tools: add per-function stack usage report script Co-authored-by: gullradriel <gullradriel@users.noreply.github.com>
This commit is contained in:
@@ -124,6 +124,7 @@ set(CPPSRC
|
||||
dsp_goertzel.cpp
|
||||
matched_filter.cpp
|
||||
spectrum_collector.cpp
|
||||
filtered_spectrum_collector.cpp
|
||||
tv_collector.cpp
|
||||
stream_input.cpp
|
||||
stream_output.cpp
|
||||
|
||||
@@ -337,6 +337,66 @@ buffer_c16_t FIRC16xR16x16Decim2::execute(
|
||||
src.sampling_rate / decimation_factor};
|
||||
}
|
||||
|
||||
// FIRC16xR16x63HalfbandDecim2 ////////////////////////////////////////////
|
||||
|
||||
void FIRC16xR16x63HalfbandDecim2::configure(
|
||||
const std::array<int16_t, taps_count>& taps) {
|
||||
std::copy(taps.cbegin(), taps.cend(), taps_.begin());
|
||||
reset();
|
||||
}
|
||||
|
||||
void FIRC16xR16x63HalfbandDecim2::reset() {
|
||||
samples_.fill({});
|
||||
samples_head_ = 0;
|
||||
}
|
||||
|
||||
buffer_c16_t FIRC16xR16x63HalfbandDecim2::execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst) {
|
||||
auto* dst_p = reinterpret_cast<uint32_t*>(dst.p);
|
||||
|
||||
for (size_t output = 0; output < src.count / decimation_factor; ++output) {
|
||||
for (size_t i = 0; i < decimation_factor; ++i) {
|
||||
const auto sample = src.p[output * decimation_factor + i];
|
||||
samples_[samples_head_] = sample;
|
||||
samples_[samples_head_ + taps_count] = sample;
|
||||
if (++samples_head_ == taps_count)
|
||||
samples_head_ = 0;
|
||||
}
|
||||
|
||||
int64_t real = 0;
|
||||
int64_t imag = 0;
|
||||
for (size_t tap = 0; tap < taps_count; tap += 4) {
|
||||
const auto sample_0 =
|
||||
*reinterpret_cast<const uint32_t*>(&samples_[samples_head_ + tap]);
|
||||
const auto sample_1 =
|
||||
*reinterpret_cast<const uint32_t*>(&samples_[samples_head_ + tap + 2]);
|
||||
const auto real_pair = __PKHBT(sample_0, sample_1, 16);
|
||||
const auto imag_pair = __PKHTB(sample_1, sample_0, 16);
|
||||
const auto taps_pair = uint32_t(uint16_t(taps_[tap])) |
|
||||
(uint32_t(uint16_t(taps_[tap + 2])) << 16);
|
||||
real = __SMLALD(real_pair, taps_pair, real);
|
||||
imag = __SMLALD(imag_pair, taps_pair, imag);
|
||||
}
|
||||
|
||||
const auto center =
|
||||
*reinterpret_cast<const uint32_t*>(&samples_[samples_head_ + taps_count / 2]);
|
||||
real += int16_t(center) * taps_[taps_count / 2];
|
||||
imag += int16_t(center >> 16) * taps_[taps_count / 2];
|
||||
|
||||
const auto real_s16 =
|
||||
__SSAT((real + (real >= 0 ? 32768 : -32768)) / 65536, 16);
|
||||
const auto imag_s16 =
|
||||
__SSAT((imag + (imag >= 0 ? 32768 : -32768)) / 65536, 16);
|
||||
*(dst_p++) = __PKHBT(real_s16, imag_s16, 16);
|
||||
}
|
||||
|
||||
return {
|
||||
dst.p,
|
||||
src.count / decimation_factor,
|
||||
src.sampling_rate / decimation_factor};
|
||||
}
|
||||
|
||||
// FIRC16xR16x32Decim8 ////////////////////////////////////////////////////
|
||||
|
||||
void FIRC16xR16x32Decim8::configure(
|
||||
@@ -625,10 +685,13 @@ buffer_s16_t FIR64AndDecimateBy2Real::execute(
|
||||
void FIRAndDecimateComplex::configure_common(
|
||||
const size_t taps_count,
|
||||
const size_t decimation_factor) {
|
||||
samples_ = std::make_unique<samples_t>(taps_count);
|
||||
/* Mirror the delay line so a convolution always sees one contiguous
|
||||
* taps_count window, even when the logical head wraps. */
|
||||
samples_ = std::make_unique<samples_t>(taps_count * 2);
|
||||
taps_reversed_ = std::make_unique<taps_t>(taps_count);
|
||||
taps_count_ = taps_count;
|
||||
decimation_factor_ = decimation_factor;
|
||||
samples_head_ = 0;
|
||||
}
|
||||
|
||||
buffer_c16_t FIRAndDecimateComplex::execute(
|
||||
@@ -647,15 +710,18 @@ buffer_c16_t FIRAndDecimateComplex::execute(
|
||||
const void* src_p = src.p;
|
||||
size_t outer_count = output_samples;
|
||||
while (outer_count > 0) {
|
||||
/* Put new samples into delay buffer */
|
||||
void* z_new_p = &samples_[taps_count_ - decimation_factor_];
|
||||
/* Put new samples into both halves of the mirrored ring. */
|
||||
for (size_t i = 0; i < decimation_factor_; i++) {
|
||||
*__SIMD32(z_new_p)++ = *__SIMD32(src_p)++;
|
||||
const uint32_t sample = *__SIMD32(src_p)++;
|
||||
*reinterpret_cast<uint32_t*>(&samples_[samples_head_]) = sample;
|
||||
*reinterpret_cast<uint32_t*>(&samples_[samples_head_ + taps_count_]) = sample;
|
||||
if (++samples_head_ == taps_count_)
|
||||
samples_head_ = 0;
|
||||
}
|
||||
|
||||
size_t loop_count = taps_count_ / 8;
|
||||
void* t_p = &taps_reversed_[0];
|
||||
void* z_p = &samples_[0];
|
||||
void* z_p = &samples_[samples_head_];
|
||||
|
||||
int64_t t_real = 0;
|
||||
int64_t t_imag = 0;
|
||||
@@ -712,27 +778,6 @@ buffer_c16_t FIRAndDecimateComplex::execute(
|
||||
i_sat,
|
||||
16);
|
||||
|
||||
/* Shift sample buffer left/down by decimation factor. */
|
||||
const size_t unroll_factor = 4;
|
||||
size_t shift_count = (taps_count_ - decimation_factor_) / unroll_factor;
|
||||
|
||||
void* t = &samples_[0];
|
||||
const void* s = &samples_[decimation_factor_];
|
||||
|
||||
while (shift_count > 0) {
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
shift_count--;
|
||||
}
|
||||
|
||||
shift_count = (taps_count_ - decimation_factor_) % unroll_factor;
|
||||
while (shift_count > 0) {
|
||||
*__SIMD32(t)++ = *__SIMD32(s)++;
|
||||
shift_count--;
|
||||
}
|
||||
|
||||
outer_count--;
|
||||
}
|
||||
|
||||
|
||||
@@ -170,6 +170,24 @@ class FIRC16xR16x16Decim2 {
|
||||
int32_t output_scale = 0;
|
||||
};
|
||||
|
||||
class FIRC16xR16x63HalfbandDecim2 {
|
||||
public:
|
||||
static constexpr size_t taps_count = 63;
|
||||
static constexpr size_t decimation_factor = 2;
|
||||
|
||||
void configure(const std::array<int16_t, taps_count>& taps);
|
||||
void reset();
|
||||
|
||||
buffer_c16_t execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst);
|
||||
|
||||
private:
|
||||
alignas(4) std::array<complex16_t, taps_count * 2> samples_{};
|
||||
alignas(4) std::array<int16_t, taps_count> taps_{};
|
||||
size_t samples_head_{0};
|
||||
};
|
||||
|
||||
class FIRC16xR16x32Decim8 {
|
||||
public:
|
||||
static constexpr size_t taps_count = 32;
|
||||
@@ -210,6 +228,23 @@ class FIRAndDecimateComplex {
|
||||
configure(taps.data(), taps.size(), decimation_factor);
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void configure(
|
||||
const std::array<int16_t, N>& taps,
|
||||
const size_t decimation_factor) {
|
||||
configure_common(N, decimation_factor);
|
||||
for (size_t i = 0; i < N; ++i) {
|
||||
taps_reversed_[i] = {taps[N - 1 - i], 0};
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void set_taps(const std::array<complex16_t, N>& taps) {
|
||||
if (N == taps_count_) {
|
||||
std::reverse_copy(taps.begin(), taps.end(), &taps_reversed_[0]);
|
||||
}
|
||||
}
|
||||
|
||||
buffer_c16_t execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst);
|
||||
@@ -221,6 +256,7 @@ class FIRAndDecimateComplex {
|
||||
std::unique_ptr<taps_t> taps_reversed_{};
|
||||
size_t taps_count_{0};
|
||||
size_t decimation_factor_{1};
|
||||
size_t samples_head_{0};
|
||||
|
||||
template <typename T>
|
||||
void configure(
|
||||
|
||||
@@ -0,0 +1,472 @@
|
||||
/*
|
||||
* Copyright (C) 2026
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*/
|
||||
|
||||
#ifndef __DSP_FREQUENCY_XLATOR_H__
|
||||
#define __DSP_FREQUENCY_XLATOR_H__
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_types.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
|
||||
namespace dsp {
|
||||
|
||||
/* Fixed-point complex mixer for the Audio RX channelizer. */
|
||||
class FrequencyTranslator {
|
||||
public:
|
||||
FrequencyTranslator() {
|
||||
for (size_t i = 0; i < oscillator_q15_.size(); ++i) {
|
||||
oscillator_q15_[i] =
|
||||
static_cast<uint16_t>(sine_q15_[static_cast<uint8_t>(i + 64)]) |
|
||||
(static_cast<uint32_t>(
|
||||
static_cast<uint16_t>(sine_q15_[i]))
|
||||
<< 16);
|
||||
}
|
||||
}
|
||||
|
||||
void set_sample_rate(const uint32_t sampling_rate) {
|
||||
sampling_rate_ = sampling_rate;
|
||||
update_phase_increment();
|
||||
}
|
||||
|
||||
void set_frequency(const int32_t frequency) {
|
||||
frequency_ = frequency;
|
||||
update_phase_increment();
|
||||
}
|
||||
|
||||
buffer_c16_t execute(const buffer_c16_t& src, const buffer_c16_t& dst) {
|
||||
auto phase = phase_;
|
||||
for (size_t i = 0; i < src.count; ++i) {
|
||||
const uint8_t index = phase >> 24;
|
||||
const uint8_t next = index + 1;
|
||||
const int32_t fraction = (phase >> 16) & 0xff;
|
||||
const uint32_t oscillator_first = oscillator_q15_[index];
|
||||
const uint32_t oscillator_next = oscillator_q15_[next];
|
||||
const int32_t sine_first =
|
||||
static_cast<int16_t>(oscillator_first >> 16);
|
||||
const int32_t cosine_first =
|
||||
static_cast<int16_t>(oscillator_first);
|
||||
const int32_t sine =
|
||||
sine_first +
|
||||
(((static_cast<int16_t>(oscillator_next >> 16) -
|
||||
sine_first) *
|
||||
fraction) >>
|
||||
8);
|
||||
const int32_t cosine =
|
||||
cosine_first +
|
||||
(((static_cast<int16_t>(oscillator_next) -
|
||||
cosine_first) *
|
||||
fraction) >>
|
||||
8);
|
||||
const uint32_t oscillator =
|
||||
static_cast<uint16_t>(cosine) |
|
||||
(static_cast<uint32_t>(
|
||||
static_cast<uint16_t>(sine))
|
||||
<< 16);
|
||||
const uint32_t sample =
|
||||
*reinterpret_cast<const uint32_t*>(&src.p[i]);
|
||||
|
||||
/* Two packed dual-16-bit multiplies implement
|
||||
* (I+jQ) * (cos-j sin). */
|
||||
const int32_t out_i =
|
||||
rounded_shift(__SMUAD(sample, oscillator), 15);
|
||||
const int32_t out_q =
|
||||
rounded_shift(__SMUSDX(oscillator, sample), 15);
|
||||
*reinterpret_cast<uint32_t*>(&dst.p[i]) =
|
||||
__PKHBT(__SSAT(out_i, 16), __SSAT(out_q, 16), 16);
|
||||
phase += phase_increment_;
|
||||
}
|
||||
phase_ = phase;
|
||||
return {dst.p, src.count, src.sampling_rate};
|
||||
}
|
||||
|
||||
private:
|
||||
friend class FrequencyTranslatingDecimator32By8;
|
||||
|
||||
static constexpr int32_t rounded_shift(
|
||||
const int32_t value,
|
||||
const uint32_t bits) {
|
||||
const int32_t rounding = int32_t{1} << (bits - 1);
|
||||
return value >= 0
|
||||
? (value + rounding) >> bits
|
||||
: -((-value + rounding) >> bits);
|
||||
}
|
||||
|
||||
void update_phase_increment() {
|
||||
if (sampling_rate_) {
|
||||
phase_increment_ = static_cast<uint32_t>(
|
||||
(static_cast<int64_t>(frequency_) * (int64_t{1} << 32)) /
|
||||
sampling_rate_);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr std::array<int16_t, 256> sine_q15_{{
|
||||
0,
|
||||
804,
|
||||
1608,
|
||||
2410,
|
||||
3212,
|
||||
4011,
|
||||
4808,
|
||||
5602,
|
||||
6393,
|
||||
7179,
|
||||
7962,
|
||||
8739,
|
||||
9512,
|
||||
10278,
|
||||
11039,
|
||||
11793,
|
||||
12539,
|
||||
13279,
|
||||
14010,
|
||||
14732,
|
||||
15446,
|
||||
16151,
|
||||
16846,
|
||||
17530,
|
||||
18204,
|
||||
18868,
|
||||
19519,
|
||||
20159,
|
||||
20787,
|
||||
21403,
|
||||
22005,
|
||||
22594,
|
||||
23170,
|
||||
23731,
|
||||
24279,
|
||||
24811,
|
||||
25329,
|
||||
25832,
|
||||
26319,
|
||||
26790,
|
||||
27245,
|
||||
27683,
|
||||
28105,
|
||||
28510,
|
||||
28898,
|
||||
29268,
|
||||
29621,
|
||||
29956,
|
||||
30273,
|
||||
30571,
|
||||
30852,
|
||||
31113,
|
||||
31356,
|
||||
31580,
|
||||
31785,
|
||||
31971,
|
||||
32137,
|
||||
32285,
|
||||
32412,
|
||||
32521,
|
||||
32609,
|
||||
32678,
|
||||
32728,
|
||||
32757,
|
||||
32767,
|
||||
32757,
|
||||
32728,
|
||||
32678,
|
||||
32609,
|
||||
32521,
|
||||
32412,
|
||||
32285,
|
||||
32137,
|
||||
31971,
|
||||
31785,
|
||||
31580,
|
||||
31356,
|
||||
31113,
|
||||
30852,
|
||||
30571,
|
||||
30273,
|
||||
29956,
|
||||
29621,
|
||||
29268,
|
||||
28898,
|
||||
28510,
|
||||
28105,
|
||||
27683,
|
||||
27245,
|
||||
26790,
|
||||
26319,
|
||||
25832,
|
||||
25329,
|
||||
24811,
|
||||
24279,
|
||||
23731,
|
||||
23170,
|
||||
22594,
|
||||
22005,
|
||||
21403,
|
||||
20787,
|
||||
20159,
|
||||
19519,
|
||||
18868,
|
||||
18204,
|
||||
17530,
|
||||
16846,
|
||||
16151,
|
||||
15446,
|
||||
14732,
|
||||
14010,
|
||||
13279,
|
||||
12539,
|
||||
11793,
|
||||
11039,
|
||||
10278,
|
||||
9512,
|
||||
8739,
|
||||
7962,
|
||||
7179,
|
||||
6393,
|
||||
5602,
|
||||
4808,
|
||||
4011,
|
||||
3212,
|
||||
2410,
|
||||
1608,
|
||||
804,
|
||||
0,
|
||||
-804,
|
||||
-1608,
|
||||
-2410,
|
||||
-3212,
|
||||
-4011,
|
||||
-4808,
|
||||
-5602,
|
||||
-6393,
|
||||
-7179,
|
||||
-7962,
|
||||
-8739,
|
||||
-9512,
|
||||
-10278,
|
||||
-11039,
|
||||
-11793,
|
||||
-12539,
|
||||
-13279,
|
||||
-14010,
|
||||
-14732,
|
||||
-15446,
|
||||
-16151,
|
||||
-16846,
|
||||
-17530,
|
||||
-18204,
|
||||
-18868,
|
||||
-19519,
|
||||
-20159,
|
||||
-20787,
|
||||
-21403,
|
||||
-22005,
|
||||
-22594,
|
||||
-23170,
|
||||
-23731,
|
||||
-24279,
|
||||
-24811,
|
||||
-25329,
|
||||
-25832,
|
||||
-26319,
|
||||
-26790,
|
||||
-27245,
|
||||
-27683,
|
||||
-28105,
|
||||
-28510,
|
||||
-28898,
|
||||
-29268,
|
||||
-29621,
|
||||
-29956,
|
||||
-30273,
|
||||
-30571,
|
||||
-30852,
|
||||
-31113,
|
||||
-31356,
|
||||
-31580,
|
||||
-31785,
|
||||
-31971,
|
||||
-32137,
|
||||
-32285,
|
||||
-32412,
|
||||
-32521,
|
||||
-32609,
|
||||
-32678,
|
||||
-32728,
|
||||
-32757,
|
||||
-32767,
|
||||
-32757,
|
||||
-32728,
|
||||
-32678,
|
||||
-32609,
|
||||
-32521,
|
||||
-32412,
|
||||
-32285,
|
||||
-32137,
|
||||
-31971,
|
||||
-31785,
|
||||
-31580,
|
||||
-31356,
|
||||
-31113,
|
||||
-30852,
|
||||
-30571,
|
||||
-30273,
|
||||
-29956,
|
||||
-29621,
|
||||
-29268,
|
||||
-28898,
|
||||
-28510,
|
||||
-28105,
|
||||
-27683,
|
||||
-27245,
|
||||
-26790,
|
||||
-26319,
|
||||
-25832,
|
||||
-25329,
|
||||
-24811,
|
||||
-24279,
|
||||
-23731,
|
||||
-23170,
|
||||
-22594,
|
||||
-22005,
|
||||
-21403,
|
||||
-20787,
|
||||
-20159,
|
||||
-19519,
|
||||
-18868,
|
||||
-18204,
|
||||
-17530,
|
||||
-16846,
|
||||
-16151,
|
||||
-15446,
|
||||
-14732,
|
||||
-14010,
|
||||
-13279,
|
||||
-12539,
|
||||
-11793,
|
||||
-11039,
|
||||
-10278,
|
||||
-9512,
|
||||
-8739,
|
||||
-7962,
|
||||
-7179,
|
||||
-6393,
|
||||
-5602,
|
||||
-4808,
|
||||
-4011,
|
||||
-3212,
|
||||
-2410,
|
||||
-1608,
|
||||
-804,
|
||||
}};
|
||||
|
||||
std::array<uint32_t, 256> oscillator_q15_{};
|
||||
uint32_t phase_{0};
|
||||
uint32_t phase_increment_{0};
|
||||
uint32_t sampling_rate_{192000};
|
||||
int32_t frequency_{0};
|
||||
};
|
||||
|
||||
/*
|
||||
* Frequency-translating 32-tap FIR decimator. Frequency translation is
|
||||
* split between coefficients modulated when tuning changes and a cheap
|
||||
* output-rate phase rotation. This avoids running an NCO at the 384kHz
|
||||
* input rate.
|
||||
*/
|
||||
class FrequencyTranslatingDecimator32By8 {
|
||||
public:
|
||||
static constexpr size_t decimation_factor = 8;
|
||||
static constexpr size_t taps_count = 32;
|
||||
|
||||
void configure(
|
||||
const std::array<int16_t, taps_count>& taps,
|
||||
const uint32_t input_sampling_rate) {
|
||||
taps_ = taps;
|
||||
input_sampling_rate_ = input_sampling_rate;
|
||||
decimator_.configure(complex_taps_, decimation_factor);
|
||||
output_xlator_.set_sample_rate(input_sampling_rate / decimation_factor);
|
||||
update_taps();
|
||||
}
|
||||
|
||||
void set_frequency(const int32_t frequency) {
|
||||
frequency_ = frequency;
|
||||
output_xlator_.set_frequency(frequency);
|
||||
update_taps();
|
||||
}
|
||||
|
||||
buffer_c16_t execute(
|
||||
const buffer_c16_t& src,
|
||||
const buffer_c16_t& dst) {
|
||||
const auto filtered = decimator_.execute(src, dst);
|
||||
return output_xlator_.execute(filtered, dst);
|
||||
}
|
||||
|
||||
private:
|
||||
static void oscillator(
|
||||
const uint32_t phase,
|
||||
int32_t& sine,
|
||||
int32_t& cosine) {
|
||||
const uint8_t index = phase >> 24;
|
||||
const uint8_t next = index + 1;
|
||||
const uint8_t cosine_index = index + 64;
|
||||
const uint8_t cosine_next = cosine_index + 1;
|
||||
const int32_t fraction = (phase >> 16) & 0xff;
|
||||
const int32_t sine_first = FrequencyTranslator::sine_q15_[index];
|
||||
const int32_t cosine_first = FrequencyTranslator::sine_q15_[cosine_index];
|
||||
sine = sine_first +
|
||||
(((FrequencyTranslator::sine_q15_[next] - sine_first) * fraction) >> 8);
|
||||
cosine = cosine_first +
|
||||
(((FrequencyTranslator::sine_q15_[cosine_next] - cosine_first) * fraction) >> 8);
|
||||
}
|
||||
|
||||
void update_taps() {
|
||||
if (!input_sampling_rate_)
|
||||
return;
|
||||
|
||||
const uint32_t tap_phase_increment = static_cast<uint32_t>(
|
||||
(static_cast<int64_t>(frequency_) * (int64_t{1} << 32)) /
|
||||
input_sampling_rate_);
|
||||
|
||||
/* Centre the modulation on the FIR midpoint. Besides changing
|
||||
* only a constant output phase, this makes the two coefficients
|
||||
* in each symmetric pair complex conjugates. That property is
|
||||
* important after quantization: starting at tap zero accumulated
|
||||
* a one-sided phase and rounding error across the whole filter. */
|
||||
uint32_t phase = static_cast<uint32_t>(
|
||||
-((static_cast<int64_t>(
|
||||
static_cast<int32_t>(tap_phase_increment)) *
|
||||
static_cast<int64_t>(taps_count - 1)) /
|
||||
2));
|
||||
for (size_t i = 0; i < taps_count; ++i) {
|
||||
int32_t sine;
|
||||
int32_t cosine;
|
||||
oscillator(phase, sine, cosine);
|
||||
/* FIRAndDecimateComplex uses Q16 coefficients; the source
|
||||
* real-tap filters use Q15 coefficients. */
|
||||
const int32_t tap = taps_[i];
|
||||
complex_taps_[i] = {
|
||||
static_cast<int16_t>(
|
||||
FrequencyTranslator::rounded_shift(
|
||||
tap * cosine, 14)),
|
||||
static_cast<int16_t>(
|
||||
FrequencyTranslator::rounded_shift(
|
||||
tap * sine, 14))};
|
||||
phase += tap_phase_increment;
|
||||
}
|
||||
decimator_.set_taps(complex_taps_);
|
||||
}
|
||||
|
||||
std::array<int16_t, taps_count> taps_{};
|
||||
std::array<complex16_t, taps_count> complex_taps_{};
|
||||
decimate::FIRAndDecimateComplex decimator_{};
|
||||
FrequencyTranslator output_xlator_{};
|
||||
uint32_t input_sampling_rate_{0};
|
||||
int32_t frequency_{0};
|
||||
};
|
||||
|
||||
} /* namespace dsp */
|
||||
|
||||
#endif /*__DSP_FREQUENCY_XLATOR_H__*/
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#include "filtered_spectrum_collector.hpp"
|
||||
|
||||
#include "dsp_fir_taps.hpp"
|
||||
#include "event_m4.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
void FilteredSpectrumCollector::on_message(const Message* const message) {
|
||||
if (message->id == Message::ID::UpdateSpectrum) {
|
||||
update();
|
||||
}
|
||||
SpectrumCollector::on_message(message);
|
||||
}
|
||||
|
||||
void FilteredSpectrumCollector::start_capture(
|
||||
const size_t decimation_factor) {
|
||||
capture_decimation_ = decimation_factor;
|
||||
capture_count_ = 0;
|
||||
capture_ready_ = false;
|
||||
}
|
||||
|
||||
bool FilteredSpectrumCollector::feed(
|
||||
const buffer_c16_t& channel,
|
||||
const int32_t filter_low_frequency,
|
||||
const int32_t filter_high_frequency,
|
||||
const int32_t filter_transition) {
|
||||
set_filter(
|
||||
filter_low_frequency,
|
||||
filter_high_frequency,
|
||||
filter_transition);
|
||||
|
||||
const size_t required_samples = 256 * capture_decimation_;
|
||||
const size_t copy_count = std::min(
|
||||
channel.count, required_samples - capture_count_);
|
||||
std::copy_n(
|
||||
channel.p,
|
||||
copy_count,
|
||||
capture_.begin() + capture_count_);
|
||||
capture_count_ += copy_count;
|
||||
|
||||
if (capture_count_ == required_samples) {
|
||||
sampling_rate_ = channel.sampling_rate / capture_decimation_;
|
||||
capture_ready_ = true;
|
||||
EventDispatcher::events_flag(EVT_MASK_SPECTRUM);
|
||||
if (is_streaming()) {
|
||||
capture_ready_ = true;
|
||||
EventDispatcher::events_flag(EVT_MASK_SPECTRUM);
|
||||
} else {
|
||||
capture_ready_ = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FilteredSpectrumCollector::update() {
|
||||
if (!capture_ready_) {
|
||||
return;
|
||||
}
|
||||
|
||||
decim_0_.configure(taps_audio_spectrum_halfband.taps);
|
||||
const buffer_c16_t capture{
|
||||
capture_.data(),
|
||||
256 * capture_decimation_,
|
||||
sampling_rate_ * capture_decimation_};
|
||||
const buffer_c16_t stage_0{
|
||||
stage_0_.data(),
|
||||
stage_0_.size()};
|
||||
const auto filtered = decim_0_.execute(capture, stage_0);
|
||||
|
||||
if (capture_decimation_ == 4) {
|
||||
decim_1_.configure(taps_audio_spectrum_halfband.taps);
|
||||
const buffer_c16_t stage_1{
|
||||
stage_1_.data(),
|
||||
stage_1_.size()};
|
||||
post_message(decim_1_.execute(filtered, stage_1));
|
||||
} else {
|
||||
post_message(filtered);
|
||||
}
|
||||
capture_ready_ = false;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*/
|
||||
|
||||
#ifndef __FILTERED_SPECTRUM_COLLECTOR_H__
|
||||
#define __FILTERED_SPECTRUM_COLLECTOR_H__
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "spectrum_collector.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
class FilteredSpectrumCollector : public SpectrumCollector {
|
||||
public:
|
||||
void on_message(const Message* const message);
|
||||
|
||||
void start_capture(const size_t decimation_factor);
|
||||
bool feed(
|
||||
const buffer_c16_t& channel,
|
||||
const int32_t filter_low_frequency,
|
||||
const int32_t filter_high_frequency,
|
||||
const int32_t filter_transition);
|
||||
|
||||
private:
|
||||
std::array<complex16_t, 1024> capture_{};
|
||||
std::array<complex16_t, 512> stage_0_{};
|
||||
std::array<complex16_t, 256> stage_1_{};
|
||||
dsp::decimate::FIRC16xR16x63HalfbandDecim2 decim_0_{};
|
||||
dsp::decimate::FIRC16xR16x63HalfbandDecim2 decim_1_{};
|
||||
size_t capture_count_{0};
|
||||
size_t capture_decimation_{1};
|
||||
uint32_t sampling_rate_{0};
|
||||
bool capture_ready_{false};
|
||||
|
||||
void update();
|
||||
};
|
||||
|
||||
#endif /*__FILTERED_SPECTRUM_COLLECTOR_H__*/
|
||||
@@ -45,10 +45,26 @@ void NarrowbandAMAudio::execute(const buffer_c8_t& 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 audio_decim_0_out = audio_decim_0.execute(decim_0_out, dst_buffer);
|
||||
|
||||
channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
|
||||
spectrum_samples += decim_0_out.count;
|
||||
if (!spectrum_capture_active &&
|
||||
spectrum_samples >= spectrum_interval_samples) {
|
||||
spectrum_samples -= spectrum_interval_samples;
|
||||
channel_spectrum.start_capture(spectrum_zoom_x2 ? 4 : 2);
|
||||
spectrum_capture_active = true;
|
||||
}
|
||||
|
||||
if (spectrum_capture_active &&
|
||||
channel_spectrum.feed(
|
||||
audio_decim_0_out,
|
||||
channel_filter_low_f,
|
||||
channel_filter_high_f,
|
||||
channel_filter_transition)) {
|
||||
spectrum_capture_active = false;
|
||||
}
|
||||
|
||||
const auto decim_1_out = translating_decim_1.execute(audio_decim_0_out, dst_buffer);
|
||||
const auto decim_2_out = decim_2.execute(decim_1_out, dst_buffer);
|
||||
const auto channel_out = channel_filter.execute(decim_2_out, dst_buffer);
|
||||
|
||||
@@ -97,6 +113,10 @@ void NarrowbandAMAudio::on_message(const Message* const message) {
|
||||
capture_config(*reinterpret_cast<const CaptureConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::AudioDDCConfig:
|
||||
ddc_config(*reinterpret_cast<const AudioDDCConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -106,17 +126,19 @@ void NarrowbandAMAudio::configure(const AMConfigureMessage& message) {
|
||||
constexpr size_t decim_0_input_fs = baseband_fs;
|
||||
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_output_fs = decim_1_input_fs / decim_1.decimation_factor;
|
||||
|
||||
constexpr size_t audio_decim_0_output_fs = decim_0_output_fs / 2;
|
||||
constexpr size_t decim_1_output_fs =
|
||||
audio_decim_0_output_fs / translating_decim_1.decimation_factor;
|
||||
constexpr size_t decim_2_input_fs = decim_1_output_fs;
|
||||
constexpr size_t decim_2_output_fs = decim_2_input_fs / decim_2_decimation_factor;
|
||||
|
||||
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;
|
||||
|
||||
decim_0.configure(message.decim_0_filter.taps);
|
||||
decim_1.configure(message.decim_1_filter.taps);
|
||||
decim_0.configure(message.decim_0_filter.taps, 33554432);
|
||||
audio_decim_0.configure(taps_audio_wide_halfband_0.taps);
|
||||
translating_decim_1.configure(
|
||||
message.decim_1_filter.taps, audio_decim_0_output_fs);
|
||||
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_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
|
||||
@@ -124,12 +146,20 @@ void NarrowbandAMAudio::configure(const AMConfigureMessage& message) {
|
||||
channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs;
|
||||
|
||||
modulation_ssb = (int)message.modulation; // now sending by message , 3 types of AM demod : enum class Modulation : int32_t {DSB = 0, SSB = 1, SSB_FM = 2}
|
||||
channel_spectrum.set_decimation_factor(message.channel_spectrum_decimation_factor);
|
||||
spectrum_zoom_x2 = message.channel_spectrum_decimation_factor == 2;
|
||||
channel_spectrum.set_decimation_factor(1);
|
||||
spectrum_interval_samples =
|
||||
decim_0_output_fs / spectrum_rate_hz;
|
||||
audio_output.configure(message.audio_hpf_lpf_config); // hpf in all AM demod modes (AM-6K/9K, USB/LSB,DSB), except Wefax (lpf there).
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
||||
void NarrowbandAMAudio::ddc_config(const AudioDDCConfigMessage& message) {
|
||||
translating_decim_1.set_frequency(message.frequency);
|
||||
channel_spectrum.set_channel_filter_offset(message.frequency);
|
||||
}
|
||||
|
||||
void NarrowbandAMAudio::capture_config(const CaptureConfigMessage& message) {
|
||||
if (message.config) {
|
||||
audio_output.set_stream(std::make_unique<StreamInput>(message.config));
|
||||
|
||||
@@ -28,10 +28,11 @@
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "dsp_frequency_xlator.hpp"
|
||||
#include "audio_compressor.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
#include "spectrum_collector.hpp"
|
||||
#include "filtered_spectrum_collector.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -44,6 +45,7 @@ class NarrowbandAMAudio : public BasebandProcessor {
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 3072000;
|
||||
static constexpr auto spectrum_rate_hz = 30.0f;
|
||||
static constexpr size_t decim_2_decimation_factor = 4;
|
||||
static constexpr size_t channel_filter_decimation_factor = 1;
|
||||
|
||||
@@ -56,14 +58,19 @@ class NarrowbandAMAudio : public BasebandProcessor {
|
||||
audio.data(),
|
||||
audio.size()};
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{};
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1{};
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{};
|
||||
dsp::decimate::FIRC16xR16x16Decim2 audio_decim_0{};
|
||||
dsp::FrequencyTranslatingDecimator32By8 translating_decim_1{};
|
||||
dsp::decimate::FIRAndDecimateComplex decim_2{};
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter{};
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
int32_t channel_filter_transition = 0;
|
||||
bool configured{false};
|
||||
size_t spectrum_interval_samples{0};
|
||||
size_t spectrum_samples{0};
|
||||
bool spectrum_capture_active{false};
|
||||
bool spectrum_zoom_x2{false};
|
||||
|
||||
// bool modulation_ssb = false; // Origianlly we only had 2 AM demod types {DSB = 0, SSB = 1} , and we could handle it with bool var , 1 bit.
|
||||
int8_t modulation_ssb = 0; // Now we have 3 AM demod types we will send now index integer {DSB = 0, SSB = 1, SSB_FM = 2}
|
||||
@@ -73,7 +80,7 @@ class NarrowbandAMAudio : public BasebandProcessor {
|
||||
FeedForwardCompressor audio_compressor{};
|
||||
AudioOutput audio_output{};
|
||||
|
||||
SpectrumCollector channel_spectrum{};
|
||||
FilteredSpectrumCollector channel_spectrum{};
|
||||
|
||||
/* NB: Threads should be the last members in the class definition. */
|
||||
#ifdef PRALINE
|
||||
@@ -86,6 +93,7 @@ class NarrowbandAMAudio : public BasebandProcessor {
|
||||
#endif
|
||||
|
||||
void configure(const AMConfigureMessage& message);
|
||||
void ddc_config(const AudioDDCConfigMessage& message);
|
||||
void capture_config(const CaptureConfigMessage& message);
|
||||
|
||||
buffer_f32_t demodulate(const buffer_c16_t& channel);
|
||||
|
||||
@@ -51,10 +51,26 @@ void NarrowbandFMAudio::execute(const buffer_c8_t& 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 audio_decim_0_out = audio_decim_0.execute(decim_0_out, dst_buffer);
|
||||
|
||||
channel_spectrum.feed(decim_1_out, channel_filter_low_f, channel_filter_high_f, channel_filter_transition);
|
||||
spectrum_samples += decim_0_out.count;
|
||||
if (!spectrum_capture_active &&
|
||||
spectrum_samples >= spectrum_interval_samples) {
|
||||
spectrum_samples -= spectrum_interval_samples;
|
||||
channel_spectrum.start_capture(2);
|
||||
spectrum_capture_active = true;
|
||||
}
|
||||
|
||||
if (spectrum_capture_active &&
|
||||
channel_spectrum.feed(
|
||||
audio_decim_0_out,
|
||||
channel_filter_low_f,
|
||||
channel_filter_high_f,
|
||||
channel_filter_transition)) {
|
||||
spectrum_capture_active = false;
|
||||
}
|
||||
|
||||
const auto decim_1_out = translating_decim_1.execute(audio_decim_0_out, dst_buffer);
|
||||
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
|
||||
|
||||
feed_channel_stats(channel_out);
|
||||
@@ -141,6 +157,10 @@ void NarrowbandFMAudio::on_message(const Message* const message) {
|
||||
pitch_rssi_config(*reinterpret_cast<const PitchRSSIConfigureMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::AudioDDCConfig:
|
||||
ddc_config(*reinterpret_cast<const AudioDDCConfigMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -150,22 +170,26 @@ void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) {
|
||||
constexpr size_t decim_0_input_fs = baseband_fs;
|
||||
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_output_fs = decim_1_input_fs / decim_1.decimation_factor;
|
||||
|
||||
constexpr size_t audio_decim_0_output_fs = decim_0_output_fs / 2;
|
||||
constexpr size_t decim_1_output_fs =
|
||||
audio_decim_0_output_fs / translating_decim_1.decimation_factor;
|
||||
constexpr size_t channel_filter_input_fs = decim_1_output_fs;
|
||||
const size_t channel_filter_output_fs = channel_filter_input_fs / message.channel_decimation;
|
||||
|
||||
const size_t demod_input_fs = channel_filter_output_fs;
|
||||
|
||||
decim_0.configure(message.decim_0_filter.taps);
|
||||
decim_1.configure(message.decim_1_filter.taps);
|
||||
decim_0.configure(message.decim_0_filter.taps, 33554432);
|
||||
audio_decim_0.configure(taps_audio_wide_halfband_0.taps);
|
||||
translating_decim_1.configure(
|
||||
message.decim_1_filter.taps, audio_decim_0_output_fs);
|
||||
channel_filter.configure(message.channel_filter.taps, message.channel_decimation);
|
||||
demod.configure(demod_input_fs, message.deviation);
|
||||
channel_filter_low_f = message.channel_filter.low_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_high_f = message.channel_filter.high_frequency_normalized * channel_filter_input_fs;
|
||||
channel_filter_transition = message.channel_filter.transition_normalized * channel_filter_input_fs;
|
||||
channel_spectrum.set_decimation_factor(1.0f);
|
||||
channel_spectrum.set_decimation_factor(1);
|
||||
spectrum_interval_samples =
|
||||
decim_0_output_fs / spectrum_rate_hz;
|
||||
audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, (float)message.squelch_level / 100.0);
|
||||
|
||||
hpf.configure(audio_24k_hpf_30hz_config);
|
||||
@@ -174,6 +198,11 @@ void NarrowbandFMAudio::configure(const NBFMConfigureMessage& message) {
|
||||
configured = true;
|
||||
}
|
||||
|
||||
void NarrowbandFMAudio::ddc_config(const AudioDDCConfigMessage& message) {
|
||||
translating_decim_1.set_frequency(message.frequency);
|
||||
channel_spectrum.set_channel_filter_offset(message.frequency);
|
||||
}
|
||||
|
||||
void NarrowbandFMAudio::pitch_rssi_config(const PitchRSSIConfigureMessage& message) {
|
||||
pitch_rssi_enabled = message.enabled;
|
||||
tone_delta = (message.rssi + 1000) * ((1ULL << 32) / 24000);
|
||||
|
||||
@@ -29,10 +29,11 @@
|
||||
|
||||
#include "dsp_decimate.hpp"
|
||||
#include "dsp_demodulate.hpp"
|
||||
#include "dsp_frequency_xlator.hpp"
|
||||
#include "dsp_iir.hpp"
|
||||
|
||||
#include "audio_output.hpp"
|
||||
#include "spectrum_collector.hpp"
|
||||
#include "filtered_spectrum_collector.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -50,6 +51,7 @@ class NarrowbandFMAudio : public BasebandProcessor {
|
||||
|
||||
private:
|
||||
static constexpr size_t baseband_fs = 3072000;
|
||||
static constexpr auto spectrum_rate_hz = 30.0f;
|
||||
|
||||
std::array<complex16_t, 512> dst{};
|
||||
const buffer_c16_t dst_buffer{
|
||||
@@ -69,8 +71,9 @@ class NarrowbandFMAudio : public BasebandProcessor {
|
||||
(int16_t*)tone.data(),
|
||||
sizeof(tone) / sizeof(int16_t)};
|
||||
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{};
|
||||
dsp::decimate::FIRC16xR16x32Decim8 decim_1{};
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{};
|
||||
dsp::decimate::FIRC16xR16x16Decim2 audio_decim_0{};
|
||||
dsp::FrequencyTranslatingDecimator32By8 translating_decim_1{};
|
||||
dsp::decimate::FIRAndDecimateComplex channel_filter{};
|
||||
int32_t channel_filter_low_f = 0;
|
||||
int32_t channel_filter_high_f = 0;
|
||||
@@ -84,7 +87,10 @@ class NarrowbandFMAudio : public BasebandProcessor {
|
||||
|
||||
AudioOutput audio_output{};
|
||||
|
||||
SpectrumCollector channel_spectrum{};
|
||||
FilteredSpectrumCollector channel_spectrum{};
|
||||
size_t spectrum_interval_samples{0};
|
||||
size_t spectrum_samples{0};
|
||||
bool spectrum_capture_active{false};
|
||||
|
||||
uint32_t tone_phase{0};
|
||||
uint32_t tone_delta{0};
|
||||
@@ -113,6 +119,7 @@ class NarrowbandFMAudio : public BasebandProcessor {
|
||||
void pitch_rssi_config(const PitchRSSIConfigureMessage& message);
|
||||
void configure(const NBFMConfigureMessage& message);
|
||||
void capture_config(const CaptureConfigMessage& message);
|
||||
void ddc_config(const AudioDDCConfigMessage& message);
|
||||
};
|
||||
|
||||
#endif /*__PROC_NFM_AUDIO_H__*/
|
||||
|
||||
@@ -73,21 +73,34 @@ void SpectrumCollector::set_decimation_factor(
|
||||
* perform the deferred task on the buffer of data we prepared.
|
||||
*/
|
||||
|
||||
void SpectrumCollector::feed(
|
||||
bool SpectrumCollector::feed(
|
||||
const buffer_c16_t& channel,
|
||||
const int32_t filter_low_frequency,
|
||||
const int32_t filter_high_frequency,
|
||||
const int32_t filter_transition) {
|
||||
// Called from baseband processing thread.
|
||||
set_filter(
|
||||
filter_low_frequency,
|
||||
filter_high_frequency,
|
||||
filter_transition);
|
||||
|
||||
bool block_completed = false;
|
||||
channel_spectrum_decimator.feed(
|
||||
channel,
|
||||
[this, &block_completed](const buffer_c16_t& data) {
|
||||
this->post_message(data);
|
||||
block_completed = true;
|
||||
});
|
||||
return block_completed;
|
||||
}
|
||||
|
||||
void SpectrumCollector::set_filter(
|
||||
const int32_t filter_low_frequency,
|
||||
const int32_t filter_high_frequency,
|
||||
const int32_t filter_transition) {
|
||||
channel_filter_low_frequency = filter_low_frequency;
|
||||
channel_filter_high_frequency = filter_high_frequency;
|
||||
channel_filter_transition = filter_transition;
|
||||
|
||||
channel_spectrum_decimator.feed(
|
||||
channel,
|
||||
[this](const buffer_c16_t& data) {
|
||||
this->post_message(data);
|
||||
});
|
||||
}
|
||||
|
||||
void SpectrumCollector::post_message(const buffer_c16_t& data) {
|
||||
@@ -136,6 +149,7 @@ void SpectrumCollector::update() {
|
||||
|
||||
ChannelSpectrum spectrum;
|
||||
spectrum.sampling_rate = channel_spectrum_sampling_rate;
|
||||
spectrum.channel_filter_offset = channel_filter_offset;
|
||||
spectrum.channel_filter_low_frequency = channel_filter_low_frequency;
|
||||
spectrum.channel_filter_high_frequency = channel_filter_high_frequency;
|
||||
spectrum.channel_filter_transition = channel_filter_transition;
|
||||
|
||||
@@ -40,13 +40,24 @@ class SpectrumCollector {
|
||||
void on_message(const Message* const message);
|
||||
|
||||
void set_decimation_factor(const size_t decimation_factor);
|
||||
void set_channel_filter_offset(const int32_t offset) {
|
||||
channel_filter_offset = offset;
|
||||
}
|
||||
|
||||
void feed(
|
||||
bool feed(
|
||||
const buffer_c16_t& channel,
|
||||
const int32_t filter_low_frequency,
|
||||
const int32_t filter_high_frequency,
|
||||
const int32_t filter_transition);
|
||||
|
||||
protected:
|
||||
bool is_streaming() const { return streaming; }
|
||||
void set_filter(
|
||||
const int32_t filter_low_frequency,
|
||||
const int32_t filter_high_frequency,
|
||||
const int32_t filter_transition);
|
||||
void post_message(const buffer_c16_t& data);
|
||||
|
||||
private:
|
||||
BlockDecimator<complex16_t, 256> channel_spectrum_decimator{1};
|
||||
ChannelSpectrum fifo_data[1 << ChannelSpectrumConfigMessage::fifo_k]{};
|
||||
@@ -59,8 +70,7 @@ class SpectrumCollector {
|
||||
int32_t channel_filter_low_frequency{0};
|
||||
int32_t channel_filter_high_frequency{0};
|
||||
int32_t channel_filter_transition{0};
|
||||
|
||||
void post_message(const buffer_c16_t& data);
|
||||
int32_t channel_filter_offset{0};
|
||||
|
||||
void set_state(const SpectrumStreamingConfigMessage& message);
|
||||
void start();
|
||||
|
||||
Reference in New Issue
Block a user