From 4819a2f4e2f6167d5073f35fcb887d1c3b989097 Mon Sep 17 00:00:00 2001 From: Kyle Reed <3761006+kallanreed@users.noreply.github.com> Date: Sun, 3 Sep 2023 21:49:44 -0700 Subject: [PATCH] Decode status widget (#1431) * Initial cleanup of pocsag beta, using DSP filters * Better filter params * Better filter * Add signal diagnostics widgets * POCSAG procs sends stats messages * Only draw 32 bits * Add AudioNormalizer filter --- firmware/application/apps/pocsag_app.cpp | 28 ++- firmware/application/apps/pocsag_app.hpp | 53 +++++- firmware/baseband/CMakeLists.txt | 2 +- firmware/baseband/proc_pocsag.cpp | 11 ++ firmware/baseband/proc_pocsag.hpp | 6 + firmware/baseband/proc_pocsag2.cpp | 197 +++++++++----------- firmware/baseband/proc_pocsag2.hpp | 220 ++++++++++++----------- firmware/common/dsp_iir_config.hpp | 6 - firmware/common/message.hpp | 12 +- 9 files changed, 305 insertions(+), 230 deletions(-) diff --git a/firmware/application/apps/pocsag_app.cpp b/firmware/application/apps/pocsag_app.cpp index efa82a65..248b73ee 100644 --- a/firmware/application/apps/pocsag_app.cpp +++ b/firmware/application/apps/pocsag_app.cpp @@ -105,6 +105,8 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) &field_volume, &image_status, &text_packet_count, + &widget_bits, + &widget_frames, &button_ignore_last, &button_config, &console}); @@ -271,7 +273,31 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) { image_status.set_foreground(get_status_color(pocsag_state)); } -void POCSAGAppView::on_stats(const POCSAGStatsMessage*) { +void POCSAGAppView::on_stats(const POCSAGStatsMessage* stats) { + widget_bits.set_bits(stats->current_bits); + widget_frames.set_frames(stats->current_frames); + widget_frames.set_sync(stats->has_sync); +} + +void BitsIndicator::paint(Painter&) { + auto p = screen_pos(); + for (size_t i = 0; i < sizeof(bits_) * 8; ++i) { + auto is_set = ((bits_ >> i) & 0x1) == 1; + + int x = p.x() + (i / height); + int y = p.y() + (i % height); + display.draw_pixel({x, y}, is_set ? Color::white() : Color::black()); + } +} + +void FrameIndicator::paint(Painter& painter) { + auto p = screen_pos(); + painter.draw_rectangle({p, {2, height}}, has_sync_ ? Color::green() : Color::grey()); + + for (size_t i = 0; i < height; ++i) { + auto p2 = p + Point{2, 16 - (int)i}; + painter.draw_hline(p2, 2, i < frame_count_ ? Color::white() : Color::black()); + } } } /* namespace ui */ diff --git a/firmware/application/apps/pocsag_app.hpp b/firmware/application/apps/pocsag_app.hpp index 9cb88d65..d4f32a4f 100644 --- a/firmware/application/apps/pocsag_app.hpp +++ b/firmware/application/apps/pocsag_app.hpp @@ -52,6 +52,50 @@ class POCSAGLogger { namespace ui { +class BitsIndicator : public Widget { + public: + BitsIndicator(Point position) + : Widget{{position, {2, height}}} {} + + void paint(Painter& painter) override; + void set_bits(uint32_t bits) { + if (bits != bits_) { + bits_ = bits; + set_dirty(); + } + } + + private: + static constexpr uint8_t height = 16; + uint32_t bits_ = 0; +}; + +class FrameIndicator : public Widget { + public: + FrameIndicator(Point position) + : Widget{{position, {4, height}}} {} + + void paint(Painter& painter) override; + void set_frames(uint8_t frame_count) { + if (frame_count != frame_count_) { + frame_count_ = frame_count; + set_dirty(); + } + } + + void set_sync(bool has_sync) { + if (has_sync != has_sync_) { + has_sync_ = has_sync; + set_dirty(); + } + } + + private: + static constexpr uint8_t height = 16; + uint8_t frame_count_ = 0; + bool has_sync_ = false; +}; + struct POCSAGSettings { bool enable_small_font = false; bool enable_logging = false; @@ -187,7 +231,8 @@ class POCSAGAppView : public View { 2, {0, 99}, 1, - ' '}; + ' ', + true /*wrap*/}; AudioVolumeField field_volume{ {28 * 8, 0 * 16}}; @@ -201,6 +246,12 @@ class POCSAGAppView : public View { {3 * 8, 1 * 16 + 2, 5 * 8, 16}, "0"}; + BitsIndicator widget_bits{ + {9 * 7 + 6, 1 * 16 + 2}}; + + FrameIndicator widget_frames{ + {9 * 8, 1 * 16 + 2}}; + Button button_ignore_last{ {10 * 8, 1 * 16, 12 * 8, 20}, "Ignore Last"}; diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index 9dfcf086..85d689d5 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -449,7 +449,7 @@ DeclareTargets(PPOC pocsag) set(MODE_CPPSRC proc_pocsag2.cpp ) -DeclareTargets(PPOC pocsag2) +DeclareTargets(PPO2 pocsag2) ### RDS diff --git a/firmware/baseband/proc_pocsag.cpp b/firmware/baseband/proc_pocsag.cpp index 24878789..5a4ec3ac 100644 --- a/firmware/baseband/proc_pocsag.cpp +++ b/firmware/baseband/proc_pocsag.cpp @@ -56,6 +56,12 @@ void POCSAGProcessor::execute(const buffer_c8_t& buffer) { processDemodulatedSamples(audio.p, 16); extractFrames(); + samples_processed += buffer.count; + if (samples_processed >= stat_update_threshold) { + send_stats(); + samples_processed = 0; + } + // Clear the output before sending to audio chip. // Only clear the audio buffer when there hasn't been any audio for a while. if (squelch_.enabled() && squelch_history == 0) { @@ -137,6 +143,11 @@ void POCSAGProcessor::configure() { configured = true; } +void POCSAGProcessor::send_stats() const { + POCSAGStatsMessage message(m_fifo.codeword, m_numCode, m_gotSync); + shared_memory.application_queue.push(message); +} + // ----------------------------- // Frame extractraction methods // ----------------------------- diff --git a/firmware/baseband/proc_pocsag.hpp b/firmware/baseband/proc_pocsag.hpp index e520bea7..fd81adc1 100644 --- a/firmware/baseband/proc_pocsag.hpp +++ b/firmware/baseband/proc_pocsag.hpp @@ -137,6 +137,9 @@ class POCSAGProcessor : public BasebandProcessor { private: static constexpr size_t baseband_fs = 3072000; + static constexpr uint8_t stat_update_interval = 10; + static constexpr uint32_t stat_update_threshold = + baseband_fs / stat_update_interval; std::array dst{}; const buffer_c16_t dst_buffer{ @@ -158,7 +161,10 @@ class POCSAGProcessor : public BasebandProcessor { bool configured = false; pocsag::POCSAGPacket packet{}; + uint32_t samples_processed = 0; + void configure(); + void send_stats() const; // ---------------------------------------- // Frame extractraction methods and members diff --git a/firmware/baseband/proc_pocsag2.cpp b/firmware/baseband/proc_pocsag2.cpp index 9615fe78..a2f5a078 100644 --- a/firmware/baseband/proc_pocsag2.cpp +++ b/firmware/baseband/proc_pocsag2.cpp @@ -25,7 +25,6 @@ #include "proc_pocsag2.hpp" -#include "dsp_iir_config.hpp" #include "event_m4.hpp" #include @@ -33,50 +32,106 @@ #include #include +using namespace std; + void POCSAGProcessor::execute(const buffer_c8_t& buffer) { if (!configured) return; + // buffer has 2048 samples + // decim0 out: 2048/8 = 256 samples + // decim1 out: 256/8 = 32 samples + // channel out: 32/2 = 16 samples + // Get 24kHz audio 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 channel_out = channel_filter.execute(decim_1_out, dst_buffer); auto audio = demod.execute(channel_out, audio_buffer); - // If squelching, check for audio before smoothing because smoothing - // causes the squelch noise detection to fail. Likely because squelch - // looks for HF noise and smoothing is basically a lowpass filter. - // NB: Squelch in this processor is only for the the audio output. - // Squelching will likely drop data "noise" and break processing. - if (squelch_.enabled()) { - bool has_audio = squelch_.execute(audio); - squelch_history = (squelch_history << 1) | (has_audio ? 1 : 0); + // Check if there's any signal in the audio buffer. + bool has_audio = squelch.execute(audio); + squelch_history = (squelch_history << 1) | (has_audio ? 1 : 0); + + // Has there been any signal? + if (squelch_history == 0) { + // No signal for a while, flush and reset. + if (!has_been_reset) { + OnDataFrame(m_numCode, getRate()); + resetVals(); + send_stats(); + } + + // Clear the audio stream before sending. + for (size_t i = 0; i < audio.count; ++i) + audio.p[i] = 0.0; + + audio_output.write(audio); + return; } - smooth.Process(audio.p, audio.count); + // Filter out high-frequency noise. TODO: compensate gain? + lpf.execute_in_place(audio); + normalizer.execute_in_place(audio); + audio_output.write(audio); + processDemodulatedSamples(audio.p, 16); extractFrames(); - // Clear the output before sending to audio chip. - if (squelch_.enabled() && squelch_history == 0) { - for (size_t i = 0; i < audio.count; ++i) { - audio.p[i] = 0.0; - } + samples_processed += buffer.count; + if (samples_processed >= stat_update_threshold) { + send_stats(); + samples_processed = 0; } - - audio_output.write(audio); } -// ==================================================================== -// -// ==================================================================== +void POCSAGProcessor::on_message(const Message* const message) { + switch (message->id) { + case Message::ID::POCSAGConfigure: + configure(); + break; + + case Message::ID::NBFMConfigure: { + auto config = reinterpret_cast(message); + squelch.set_threshold(config->squelch_level / 99.0); + break; + } + + default: + break; + } +} + +void POCSAGProcessor::configure() { + constexpr size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor; + constexpr size_t decim_1_output_fs = decim_0_output_fs / decim_1.decimation_factor; + const size_t channel_filter_output_fs = decim_1_output_fs / 2; + const size_t demod_input_fs = channel_filter_output_fs; + + decim_0.configure(taps_11k0_decim_0.taps); + decim_1.configure(taps_11k0_decim_1.taps); + channel_filter.configure(taps_11k0_channel.taps, 2); + demod.configure(demod_input_fs, 4'500); // FSK +/- 4k5Hz. + + // Don't process the audio stream. + audio_output.configure(false); + + // Set up the frame extraction, limits of baud. + setFrameExtractParams(demod_input_fs, 4000, 300, 32); + + // Set ready to process data. + configured = true; +} + +void POCSAGProcessor::send_stats() const { + POCSAGStatsMessage message(m_fifo.codeword, m_numCode, m_gotSync); + shared_memory.application_queue.push(message); +} + int POCSAGProcessor::OnDataWord(uint32_t word, int pos) { packet.set(pos, word); return 0; } -// ==================================================================== -// -// ==================================================================== int POCSAGProcessor::OnDataFrame(int len, int baud) { if (len > 0) { packet.set_bitrate(baud); @@ -88,57 +143,6 @@ int POCSAGProcessor::OnDataFrame(int len, int baud) { return 0; } -void POCSAGProcessor::on_message(const Message* const message) { - switch (message->id) { - case Message::ID::POCSAGConfigure: - configure(); - break; - - case Message::ID::NBFMConfigure: { - auto config = reinterpret_cast(message); - squelch_.set_threshold(config->squelch_level / 100.0); - break; - } - - default: - break; - } -} - -void POCSAGProcessor::configure() { - 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 channel_filter_input_fs = decim_1_output_fs; - const size_t channel_filter_output_fs = channel_filter_input_fs / 2; - - const size_t demod_input_fs = channel_filter_output_fs; - - decim_0.configure(taps_11k0_decim_0.taps); - decim_1.configure(taps_11k0_decim_1.taps); - channel_filter.configure(taps_11k0_channel.taps, 2); - demod.configure(demod_input_fs, 4'500); // FSK +/- 4k5Hz. - - // Smoothing should be roughly sample rate over max baud - // 24k / 3.2k = 7.5 - smooth.SetSize(8); - - // Don't have audio process the stream. - audio_output.configure(false); - - // Set up the frame extraction, limits of baud - setFrameExtractParams(demod_input_fs, 4000, 300, 32); - - // Mark the class as ready to accept data - configured = true; -} - -// ----------------------------- -// Frame extractraction methods -// ----------------------------- #define BAUD_STABLE (104) #define MAX_CONSEC_SAME (32) #define MAX_WITHOUT_SINGLE (64) @@ -149,9 +153,6 @@ void POCSAGProcessor::configure() { #define M_IDLE (0x7a89c197) -// ==================================================================== -// -// ==================================================================== inline int bitsDiff(unsigned long left, unsigned long right) { unsigned long xord = left ^ right; int count = 0; @@ -162,9 +163,6 @@ inline int bitsDiff(unsigned long left, unsigned long right) { return (count); } -// ==================================================================== -// -// ==================================================================== void POCSAGProcessor::initFrameExtraction() { m_averageSymbolLen_1024 = m_maxSymSamples_1024; m_lastStableSymbolLen_1024 = m_minSymSamples_1024; @@ -177,12 +175,10 @@ void POCSAGProcessor::initFrameExtraction() { resetVals(); } -// ==================================================================== -// -// ==================================================================== void POCSAGProcessor::resetVals() { + if (has_been_reset) return; + // Reset the parameters - // -------------------- m_goodTransitions = 0; m_badTransitions = 0; m_averageSymbolLen_1024 = m_maxSymSamples_1024; @@ -190,7 +186,6 @@ void POCSAGProcessor::resetVals() { m_valMid = 0; // And reset the counts - // -------------------- m_lastTransPos_1024 = 0; m_lastBitPos_1024 = 0; m_lastSample = 0; @@ -200,13 +195,14 @@ void POCSAGProcessor::resetVals() { // Extraction m_fifo.numBits = 0; + m_fifo.codeword = 0; m_gotSync = false; m_numCode = 0; + + has_been_reset = true; + samples_processed = 0; } -// ==================================================================== -// -// ==================================================================== void POCSAGProcessor::setFrameExtractParams(long a_samplesPerSec, long a_maxBaud, long a_minBaud, long maxRunOfSameValue) { m_samplesPerSec = a_samplesPerSec; m_minSymSamples_1024 = (uint32_t)(1024.0f * (float)a_samplesPerSec / (float)a_maxBaud); @@ -223,14 +219,13 @@ void POCSAGProcessor::setFrameExtractParams(long a_samplesPerSec, long a_maxBaud initFrameExtraction(); } -// ==================================================================== -// -// ==================================================================== int POCSAGProcessor::processDemodulatedSamples(float* sampleBuff, int noOfSamples) { bool transition = false; uint32_t samplePos_1024 = 0; uint32_t len_1024 = 0; + has_been_reset = false; + // Loop through the block of data // ------------------------------ for (int pos = 0; pos < noOfSamples; ++pos) { @@ -399,9 +394,6 @@ int POCSAGProcessor::processDemodulatedSamples(float* sampleBuff, int noOfSample return getNoOfBits(); } -// ==================================================================== -// -// ==================================================================== void POCSAGProcessor::storeBit() { if (++m_bitsStart >= BIT_BUF_SIZE) { m_bitsStart = 0; @@ -429,9 +421,6 @@ void POCSAGProcessor::storeBit() { } } -// ==================================================================== -// -// ==================================================================== int POCSAGProcessor::extractFrames() { int msgCnt = 0; // While there is unread data in the bits buffer @@ -471,7 +460,7 @@ int POCSAGProcessor::extractFrames() { // If at the end of a 16 word block // -------------------------------- if (m_numCode >= 15) { - msgCnt += OnDataFrame(m_numCode + 1, (m_samplesPerSec << 10) / m_lastStableSymbolLen_1024); + msgCnt += OnDataFrame(m_numCode + 1, getRate()); m_gotSync = false; m_numCode = -1; } @@ -482,9 +471,6 @@ int POCSAGProcessor::extractFrames() { return msgCnt; } // extractFrames -// ==================================================================== -// -// ==================================================================== short POCSAGProcessor::getBit() { if (m_bitsEnd != m_bitsStart) { if (++m_bitsEnd >= BIT_BUF_SIZE) { @@ -496,9 +482,6 @@ short POCSAGProcessor::getBit() { } } -// ==================================================================== -// -// ==================================================================== int POCSAGProcessor::getNoOfBits() { int bits = m_bitsEnd - m_bitsStart; if (bits < 0) { @@ -507,16 +490,10 @@ int POCSAGProcessor::getNoOfBits() { return bits; } -// ==================================================================== -// -// ==================================================================== uint32_t POCSAGProcessor::getRate() { return ((m_samplesPerSec << 10) + 512) / m_lastStableSymbolLen_1024; } -// ==================================================================== -// -// ==================================================================== int main() { EventDispatcher event_dispatcher{std::make_unique()}; event_dispatcher.run(); diff --git a/firmware/baseband/proc_pocsag2.hpp b/firmware/baseband/proc_pocsag2.hpp index 609d4541..10b71c28 100644 --- a/firmware/baseband/proc_pocsag2.hpp +++ b/firmware/baseband/proc_pocsag2.hpp @@ -26,107 +26,87 @@ #ifndef __PROC_POCSAG2_H__ #define __PROC_POCSAG2_H__ +#include "audio_output.hpp" #include "baseband_processor.hpp" #include "baseband_thread.hpp" -#include "rssi_thread.hpp" - #include "dsp_decimate.hpp" #include "dsp_demodulate.hpp" - -#include "pocsag_packet.hpp" - -#include "pocsag.hpp" +#include "dsp_iir_config.hpp" #include "message.hpp" -#include "audio_output.hpp" +#include "pocsag.hpp" +#include "pocsag_packet.hpp" #include "portapack_shared_memory.hpp" +#include "rssi_thread.hpp" #include -#include -using namespace std; - -// Class used to smooth demodulated waveform prior to decoding -// ----------------------------------------------------------- -template -class SmoothVals { - protected: - ValType* m_lastVals; // Previous N values - int m_size; // The size N - CalcType m_sumVal; // Running sum of lastVals - int m_pos; // Current position in last vals ring buffer - int m_count; // +/* Takes audio stream and automatically normalizes it to +/-1.0f */ +class AudioNormalizer { public: - SmoothVals() - : m_lastVals(NULL), m_size(1), m_sumVal(0), m_pos(0), m_count(0) { - m_lastVals = new ValType[m_size]; - } - - // -------------------------------------------------- - // -------------------------------------------------- - virtual ~SmoothVals() { - delete[] m_lastVals; - } - - SmoothVals(const SmoothVals&) { - } - - SmoothVals& operator=(const SmoothVals&) { - return *this; - } - - // -------------------------------------------------- - // Set size of smoothing - // -------------------------------------------------- - void SetSize(int size) { - m_size = std::max(size, 1); - m_pos = 0; - delete[] m_lastVals; - m_lastVals = new ValType[m_size]; - m_sumVal = 0; - } - - // -------------------------------------------------- - // Get size of smoothing - // -------------------------------------------------- - int Size() { return m_size; } - - // -------------------------------------------------- - // In place processing - // -------------------------------------------------- - void Process(ValType* valBuff, int numVals) { - ValType tmpVal; - - if (m_count > (1024 * 10)) { - // Recalculate the sum value occasionaly, stops accumulated errors when using float - m_count = 0; - m_sumVal = 0; - for (int i = 0; i < m_size; ++i) { - m_sumVal += (CalcType)m_lastVals[i]; - } + void execute_in_place(const buffer_f32_t& audio) { + // Decay min/max every second (@24kHz). + if (counter_ >= 24'000) { + // 90% decay factor seems to work well. + // This keeps large transients from wrecking the filter. + max_ *= 0.9f; + min_ *= 0.9f; + counter_ = 0; + calculate_thresholds(); } - // Use a rolling smoothed value while processing the buffer - for (int buffPos = 0; buffPos < numVals; ++buffPos) { - m_pos++; - if (m_pos >= m_size) { - m_pos = 0; + counter_ += audio.count; + + for (size_t i = 0; i < audio.count; ++i) { + auto& val = audio.p[i]; + + if (val > max_) { + max_ = val; + calculate_thresholds(); + } + if (val < min_) { + min_ = val; + calculate_thresholds(); } - m_sumVal -= (CalcType)m_lastVals[m_pos]; // Subtract the oldest value - m_lastVals[m_pos] = valBuff[buffPos]; // Store the new value - m_sumVal += (CalcType)m_lastVals[m_pos]; // Add on the new value - - tmpVal = (ValType)(m_sumVal / m_size); // Scale by number of values smoothed - valBuff[buffPos] = tmpVal; + if (val >= t_hi_) + val = 1.0f; + else if (val <= t_lo_) + val = -1.0f; + else + val = 0.0; } - - m_count += numVals; } + + private: + void calculate_thresholds() { + auto center = (max_ + min_) / 2.0f; + auto range = (max_ - min_) / 2.0f; + + // 10% off center force either +/-1.0f. + // Higher == larger dead zone. + // Lower == more false positives. + auto threshold = range * 0.1; + t_hi_ = center + threshold; + t_lo_ = center - threshold; + } + + uint32_t counter_ = 0; + float min_ = 99.0f; + float max_ = -99.0f; + float t_hi_ = 1.0; + float t_lo_ = 1.0; }; -// -------------------------------------------------- -// Class to process base band data to pocsag frames -// -------------------------------------------------- +// How to detect clock signal across baud rates? +// Maybe have a bit extraction state machine that reset +// then watches for the clocks, but there are multiple +// clock and the last one is the right one. +// So keep updating clock until a sync? + +class BitExtractor {}; + +class WordExtractor {}; + class POCSAGProcessor : public BasebandProcessor { public: void execute(const buffer_c8_t& buffer) override; @@ -137,28 +117,54 @@ class POCSAGProcessor : public BasebandProcessor { private: static constexpr size_t baseband_fs = 3072000; - - std::array dst{}; - const buffer_c16_t dst_buffer{ - dst.data(), - dst.size()}; - std::array audio{}; - const buffer_f32_t audio_buffer{ - audio.data(), - audio.size()}; - - dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; - dsp::decimate::FIRC16xR16x32Decim8 decim_1{}; - dsp::decimate::FIRAndDecimateComplex channel_filter{}; - dsp::demodulate::FM demod{}; - SmoothVals smooth = {}; - - AudioOutput audio_output{}; - - bool configured = false; - pocsag::POCSAGPacket packet{}; + static constexpr uint8_t stat_update_interval = 10; + static constexpr uint32_t stat_update_threshold = + baseband_fs / stat_update_interval; void configure(); + void send_stats() const; + + // Set once app is ready to receive messages. + bool configured = false; + + // Buffer for decimated IQ data. + std::array dst{}; + const buffer_c16_t dst_buffer{dst.data(), dst.size()}; + + // Buffer for demodulated audio. + std::array audio{}; + const buffer_f32_t audio_buffer{audio.data(), audio.size()}; + + // Decimate to 48kHz. + dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0{}; + dsp::decimate::FIRC16xR16x32Decim8 decim_1{}; + + // Filter to 24kHz and demodulate. + dsp::decimate::FIRAndDecimateComplex channel_filter{}; + dsp::demodulate::FM demod{}; + + // LPF to reduce noise. + // scipy.signal.butter(2, 1800, "lowpass", fs=24000, analog=False) + IIRBiquadFilter lpf{{{0.04125354f, 0.082507070f, 0.04125354f}, + {1.00000000f, -1.34896775f, 0.51398189f}}}; + + // Squelch to ignore noise. + FMSquelch squelch{}; + uint64_t squelch_history = 0; + + // Attempts to de-noise signal and normalize to +/- 1.0f. + AudioNormalizer normalizer{}; + + // Handles writing audio stream to hardware. + AudioOutput audio_output{}; + + // Holds the data sent to the app. + pocsag::POCSAGPacket packet{}; + + bool has_been_reset = true; + uint32_t samples_processed = 0; + + //-------------------------------------------------- // ---------------------------------------- // Frame extractraction methods and members @@ -169,8 +175,6 @@ class POCSAGProcessor : public BasebandProcessor { int numBits; }; -#define BIT_BUF_SIZE (64) - void resetVals(); void setFrameExtractParams(long a_samplesPerSec, long a_maxBaud = 8000, long a_minBaud = 200, long maxRunOfSameValue = 32); @@ -207,7 +211,8 @@ class POCSAGProcessor : public BasebandProcessor { uint32_t m_maxSymSamples_1024{0}; uint32_t m_maxRunOfSameValue{0}; - bitset<(size_t)BIT_BUF_SIZE> m_bits{0}; + static constexpr long BIT_BUF_SIZE = 64; + std::bitset<64> m_bits{0}; long m_bitsStart{0}; long m_bitsEnd{0}; @@ -216,8 +221,7 @@ class POCSAGProcessor : public BasebandProcessor { int m_numCode{0}; bool m_inverted{false}; - FMSquelch squelch_{}; - uint64_t squelch_history = 0; + //-------------------------------------------------- /* NB: Threads should be the last members in the class definition. */ BasebandThread baseband_thread{baseband_fs, this, baseband::Direction::Receive}; diff --git a/firmware/common/dsp_iir_config.hpp b/firmware/common/dsp_iir_config.hpp index 36133517..177475df 100644 --- a/firmware/common/dsp_iir_config.hpp +++ b/firmware/common/dsp_iir_config.hpp @@ -77,12 +77,6 @@ constexpr iir_biquad_config_t audio_24k_deemph_300_6_config{ {0.03780475f, 0.03780475f, 0.00000000f}, {1.00000000f, -0.92439049f, 0.00000000f}}; -// scipy.signal.butter(1, 2400 / 12000.0, 'lowpass', analog=False) -// NOTE: Technically, order-1 filter, b[2] = a[2] = 0. -constexpr iir_biquad_config_t audio_24k_lpf_2400hz_config{ - {0.03780475f, 0.03780475f, 0.00000000f}, - {1.00000000f, -0.92439049f, 0.00000000f}}; - // scipy.signal.butter(1, 300 / 8000.0, 'lowpass', analog=False) // NOTE: Technically, order-1 filter, b[2] = a[2] = 0. constexpr iir_biquad_config_t audio_16k_deemph_300_6_config{ diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index 625747be..04680d39 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -344,12 +344,18 @@ class POCSAGPacketMessage : public Message { class POCSAGStatsMessage : public Message { public: constexpr POCSAGStatsMessage( - uint16_t baud_rate) + uint32_t current_bits, + uint8_t current_frames, + bool has_sync) : Message{ID::POCSAGStats}, - baud_rate_{baud_rate} { + current_bits{current_bits}, + current_frames{current_frames}, + has_sync{has_sync} { } - uint16_t baud_rate_; + uint32_t current_bits = 0; + uint8_t current_frames = 0; + bool has_sync = false; }; class ACARSPacketMessage : public Message {