Isolate filtered spectrum collection from WFM

This commit is contained in:
Belousov Oleg
2026-07-27 21:45:10 +03:00
parent ec495b49f6
commit e5a3e8326f
9 changed files with 144 additions and 79 deletions
+1
View File
@@ -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
@@ -0,0 +1,84 @@
/*
* 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);
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__*/
+2 -2
View File
@@ -51,12 +51,12 @@ void NarrowbandAMAudio::execute(const buffer_c8_t& buffer) {
if (!spectrum_capture_active &&
spectrum_samples >= spectrum_interval_samples) {
spectrum_samples -= spectrum_interval_samples;
channel_spectrum.start_filtered_capture(spectrum_zoom_x2 ? 4 : 2);
channel_spectrum.start_capture(spectrum_zoom_x2 ? 4 : 2);
spectrum_capture_active = true;
}
if (spectrum_capture_active &&
channel_spectrum.feed_filtered(
channel_spectrum.feed(
audio_decim_0_out,
channel_filter_low_f,
channel_filter_high_f,
+2 -2
View File
@@ -32,7 +32,7 @@
#include "audio_compressor.hpp"
#include "audio_output.hpp"
#include "spectrum_collector.hpp"
#include "filtered_spectrum_collector.hpp"
#include <cstdint>
@@ -80,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
+2 -2
View File
@@ -57,12 +57,12 @@ void NarrowbandFMAudio::execute(const buffer_c8_t& buffer) {
if (!spectrum_capture_active &&
spectrum_samples >= spectrum_interval_samples) {
spectrum_samples -= spectrum_interval_samples;
channel_spectrum.start_filtered_capture(2);
channel_spectrum.start_capture(2);
spectrum_capture_active = true;
}
if (spectrum_capture_active &&
channel_spectrum.feed_filtered(
channel_spectrum.feed(
audio_decim_0_out,
channel_filter_low_f,
channel_filter_high_f,
+2 -2
View File
@@ -33,7 +33,7 @@
#include "dsp_iir.hpp"
#include "audio_output.hpp"
#include "spectrum_collector.hpp"
#include "filtered_spectrum_collector.hpp"
#include <cstdint>
@@ -87,7 +87,7 @@ 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};
+5 -57
View File
@@ -22,7 +22,6 @@
#include "spectrum_collector.hpp"
#include "dsp_fft.hpp"
#include "dsp_fir_taps.hpp"
#include "utility.hpp"
#include "event_m4.hpp"
@@ -80,9 +79,10 @@ bool SpectrumCollector::feed(
const int32_t filter_high_frequency,
const int32_t filter_transition) {
// Called from baseband processing thread.
channel_filter_low_frequency = filter_low_frequency;
channel_filter_high_frequency = filter_high_frequency;
channel_filter_transition = filter_transition;
set_filter(
filter_low_frequency,
filter_high_frequency,
filter_transition);
bool block_completed = false;
channel_spectrum_decimator.feed(
@@ -94,40 +94,13 @@ bool SpectrumCollector::feed(
return block_completed;
}
void SpectrumCollector::start_filtered_capture(
const size_t decimation_factor) {
filtered_capture_decimation_ = decimation_factor;
filtered_capture_count_ = 0;
filtered_capture_ready_ = false;
}
bool SpectrumCollector::feed_filtered(
const buffer_c16_t& channel,
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;
const size_t required_samples = 256 * filtered_capture_decimation_;
const size_t copy_count = std::min(
channel.count, required_samples - filtered_capture_count_);
std::copy_n(
channel.p,
copy_count,
filtered_capture_.begin() + filtered_capture_count_);
filtered_capture_count_ += copy_count;
if (filtered_capture_count_ == required_samples) {
channel_spectrum_sampling_rate =
channel.sampling_rate / filtered_capture_decimation_;
filtered_capture_ready_ = true;
channel_spectrum_request_update = true;
EventDispatcher::events_flag(EVT_MASK_SPECTRUM);
return true;
}
return false;
}
void SpectrumCollector::post_message(const buffer_c16_t& data) {
@@ -171,31 +144,6 @@ static typename T::value_type spectrum_window_blackman_3(const T& s, const size_
void SpectrumCollector::update() {
// Called from idle thread (after EVT_MASK_SPECTRUM is flagged)
if (streaming && channel_spectrum_request_update) {
if (filtered_capture_ready_) {
filtered_decim_0_.configure(taps_audio_spectrum_halfband.taps);
const buffer_c16_t capture{
filtered_capture_.data(),
256 * filtered_capture_decimation_,
channel_spectrum_sampling_rate * filtered_capture_decimation_};
const buffer_c16_t stage_0{
filtered_stage_0_.data(),
filtered_stage_0_.size()};
const auto filtered = filtered_decim_0_.execute(capture, stage_0);
if (filtered_capture_decimation_ == 4) {
filtered_decim_1_.configure(taps_audio_spectrum_halfband.taps);
const buffer_c16_t stage_1{
filtered_stage_1_.data(),
filtered_stage_1_.size()};
const auto zoom_filtered =
filtered_decim_1_.execute(filtered, stage_1);
fft_swap(zoom_filtered, channel_spectrum);
} else {
fft_swap(filtered, channel_spectrum);
}
filtered_capture_ready_ = false;
}
/* Decimated buffer is full. Compute spectrum. */
fft_c_preswapped(channel_spectrum, 0, 8);
+3 -14
View File
@@ -29,7 +29,6 @@
#include "complex.hpp"
#include "block_decimator.hpp"
#include "dsp_decimate.hpp"
#include <cstdint>
#include <array>
@@ -51,12 +50,12 @@ class SpectrumCollector {
const int32_t filter_high_frequency,
const int32_t filter_transition);
void start_filtered_capture(const size_t decimation_factor);
bool feed_filtered(
const buffer_c16_t& channel,
protected:
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};
@@ -66,22 +65,12 @@ class SpectrumCollector {
volatile bool channel_spectrum_request_update{false};
bool streaming{false};
std::array<std::complex<float>, 256> channel_spectrum{};
std::array<complex16_t, 1024> filtered_capture_{};
std::array<complex16_t, 512> filtered_stage_0_{};
std::array<complex16_t, 256> filtered_stage_1_{};
dsp::decimate::FIRC16xR16x63HalfbandDecim2 filtered_decim_0_{};
dsp::decimate::FIRC16xR16x63HalfbandDecim2 filtered_decim_1_{};
size_t filtered_capture_count_{0};
size_t filtered_capture_decimation_{1};
bool filtered_capture_ready_{false};
uint32_t channel_spectrum_sampling_rate{0};
int32_t channel_filter_low_frequency{0};
int32_t channel_filter_high_frequency{0};
int32_t channel_filter_transition{0};
int32_t channel_filter_offset{0};
void post_message(const buffer_c16_t& data);
void set_state(const SpectrumStreamingConfigMessage& message);
void start();
void stop();