Support squelch in pocsag (#1415)

* Support squelch in pocsag

* Revert smooth threshold
This commit is contained in:
Kyle Reed 2023-08-27 15:56:40 -07:00 committed by GitHub
parent d8930db8af
commit e7e1bedcad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 100 additions and 78 deletions

View File

@ -95,6 +95,7 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav)
&field_lna,
&field_vga,
&field_frequency,
&field_squelch,
&field_volume,
&image_status,
&text_packet_count,
@ -111,6 +112,11 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav)
logger.append(LOG_ROOT_DIR "/POCSAG.TXT");
field_squelch.set_value(receiver_model.squelch_level());
field_squelch.on_change = [this](int32_t v) {
receiver_model.set_squelch_level(v);
};
button_ignore_last.on_select = [this](Button&) {
settings_.enable_ignore = true;
settings_.address_to_ignore = last_address;
@ -122,8 +128,8 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav)
};
refresh_ui();
receiver_model.enable();
audio::output::start();
receiver_model.enable();
baseband::set_pocsag();
}

View File

@ -136,10 +136,7 @@ class POCSAGAppView : public View {
bool hide_addr_only() const { return settings_.hide_addr_only; };
NavigationView& nav_;
RxRadioState radio_state_{
12'500, // POCSAG is FSK +/- 4.5MHz, 12k5 is a good filter.
3'072'000, // Match baseband_fs in proc_pocsag.
};
RxRadioState radio_state_{};
// Settings
POCSAGSettings settings_{};
@ -161,24 +158,33 @@ class POCSAGAppView : public View {
void on_stats(const POCSAGStatsMessage* stats);
uint32_t last_address = 0xFFFFFFFF;
pocsag::POCSAGState pocsag_state{};
pocsag::EccContainer ecc{};
pocsag::POCSAGState pocsag_state{&ecc};
POCSAGLogger logger{};
uint16_t packet_count = 0;
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 3, 6 * 8, 4}};
Audio audio{
{21 * 8, 8, 6 * 8, 4}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 8},
nav_};
RFAmpField field_rf_amp{
{11 * 8, 0 * 16}};
LNAGainField field_lna{
{13 * 8, 0 * 16}};
VGAGainField field_vga{
{16 * 8, 0 * 16}};
RSSI rssi{
{19 * 8 - 4, 3, 6 * 8, 4}};
Audio audio{
{19 * 8 - 4, 8, 6 * 8, 4}};
NumberField field_squelch{
{25 * 8, 0 * 16},
2,
{0, 99},
1,
' '};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};

View File

@ -24,12 +24,13 @@
#include "proc_pocsag.hpp"
#include "dsp_iir_config.hpp"
#include "event_m4.hpp"
#include <cstdint>
#include <cstddef>
#include <algorithm>
#include <cmath>
#include <cstdint>
#include <cstddef>
void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
// This is called at 1500Hz
@ -41,9 +42,13 @@ void POCSAGProcessor::execute(const buffer_c8_t& 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);
smooth.Process(audio.p, audio.count); // Smooth the data to make decoding more accurate
// Output audio pre-smoothing so squelch actually works.
// NB: It's useful to output *after* when debugging the smoothing filter.
audio_output.write(audio);
// Smooth the data to make decoding more accurate.
smooth.Process(audio.p, audio.count);
processDemodulatedSamples(audio.p, 16);
extractFrames();
}
@ -71,8 +76,23 @@ int POCSAGProcessor::OnDataFrame(int len, int baud) {
}
void POCSAGProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::POCSAGConfigure)
configure();
switch (message->id) {
case Message::ID::POCSAGConfigure:
configure();
break;
case Message::ID::NBFMConfigure: {
auto config = reinterpret_cast<const NBFMConfigureMessage*>(message);
audio_output.configure(
audio_24k_hpf_300hz_config,
audio_8k_deemph_300_6_config,
config->squelch_level / 100.0);
break;
}
default:
break;
}
}
void POCSAGProcessor::configure() {
@ -91,13 +111,10 @@ void POCSAGProcessor::configure() {
decim_1.configure(taps_11k0_decim_1.taps, 131072);
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 is 7.5
smooth.SetSize(8);
// TODO: support squelch?
// audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, (float)message.squelch_level / 100.0);
audio_output.configure(false);
// Smoothing should be roughly sample rate over max baud
// 24k / 3.2k = 7.5
smooth.SetSize(8);
// Set up the frame extraction, limits of baud
setFrameExtractParams(demod_input_fs, 4000, 300, 32);

View File

@ -106,10 +106,10 @@ class SmoothVals {
// Use a rolling smoothed value while processing the buffer
for (int buffPos = 0; buffPos < numVals; ++buffPos) {
m_pos = (m_pos + 1); // Increment the position in the stored values
m_pos++;
if (m_pos >= m_size) {
m_pos = 0;
} // loop if reached the end of the stored values
}
m_sumVal -= (CalcType)m_lastVals[m_pos]; // Subtract the oldest value
m_lastVals[m_pos] = valBuff[buffPos]; // Store the new value

View File

@ -38,9 +38,7 @@ constexpr iir_biquad_config_t audio_48k_hpf_300hz_config{
// scipy.signal.butter(2, 300 / 12000.0, 'highpass', analog=False)
constexpr iir_biquad_config_t audio_24k_hpf_300hz_config{
{0.94597686f, -1.89195371f, 0.94597686f},
{1.00000000f, -1.88903308f, 0.89487434f}
};
{1.00000000f, -1.88903308f, 0.89487434f}};
// scipy.signal.butter(2, 30 / 12000.0, 'highpass', analog=False)
constexpr iir_biquad_config_t audio_24k_hpf_30hz_config{

View File

@ -225,30 +225,14 @@ void pocsag_encode(const MessageType type, BCHCode& BCH_code, const uint32_t fun
} while (char_idx < message_size);
}
// -------------------------------------------------------------------------------
// Get the number of bits that differ between the two values.
// -------------------------------------------------------------------------------
inline uint8_t bitsDiff(unsigned long left, unsigned long right) {
unsigned long xord = left ^ right;
uint8_t count = 0;
for (int i = 0; i < 32; i++) {
if ((xord & 0x01) == 1) ++count;
xord = xord >> 1;
}
return (count);
// ----------------------------------------------------------------------------
// EccContainer
// ----------------------------------------------------------------------------
EccContainer::EccContainer() {
setup_ecc();
}
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
static uint32_t ecs[32]; /* error correction sequence */
static uint32_t bch[1025];
static int eccSetup = 0;
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
void setupecc() {
void EccContainer::setup_ecc() {
unsigned int srr = 0x3b4;
unsigned int i, n, j, k;
@ -311,23 +295,12 @@ void setupecc() {
}
}
// -------------------------------------------------------------------------------
// -------------------------------------------------------------------------------
inline int errorCorrection(uint32_t& val) {
// Set up the tables the first time
if (eccSetup == 0) {
setupecc();
eccSetup = 1;
}
int EccContainer::error_correct(uint32_t& val) {
int i, synd, errl, acc, pari, ecc, b1, b2;
errl = 0;
pari = 0;
/* run through error detection and correction routine */
// for (i=0; i<=20; i++)
ecc = 0;
for (i = 31; i >= 11; --i) {
if (val & (1 << i)) {
@ -336,7 +309,6 @@ inline int errorCorrection(uint32_t& val) {
}
}
// for (i=21; i<=30; i++)
acc = 0;
for (i = 10; i >= 1; --i) {
acc = acc << 1;
@ -390,8 +362,8 @@ bool pocsag_decode_batch(const POCSAGPacket& batch, POCSAGState& state) {
// Error correct twice. First time to fix any errors it can,
// second time to count number of errors that couldn't be fixed.
errorCorrection(codeword);
auto error_count = errorCorrection(codeword);
state.ecc->error_correct(codeword);
auto error_count = state.ecc->error_correct(codeword);
switch (state.mode) {
case STATE_CLEAR:

View File

@ -56,23 +56,46 @@ enum MessageType : uint32_t {
ALPHANUMERIC
};
/* Holds error correction arrays. This allows the arrays to
* be freed when POCSAG is not running, saving ~4kb of RAM. */
class EccContainer {
public:
EccContainer();
EccContainer(const EccContainer&) = delete;
EccContainer(EccContainer&&) = delete;
EccContainer& operator=(const EccContainer&) = delete;
EccContainer& operator=(EccContainer&&) = delete;
int error_correct(uint32_t& val);
private:
void setup_ecc();
/* error correction sequence */
uint32_t ecs[32];
uint32_t bch[1025];
};
struct POCSAGState {
uint8_t codeword_index;
uint32_t function;
uint32_t address;
EccContainer* ecc = nullptr;
uint8_t codeword_index = 0;
uint32_t function = 0;
uint32_t address = 0;
Mode mode = STATE_CLEAR;
OutputType out_type = EMPTY;
uint32_t ascii_data;
uint32_t ascii_idx;
uint32_t errors;
std::string output;
uint32_t ascii_data = 0;
uint32_t ascii_idx = 0;
uint32_t errors = 0;
std::string output{};
};
const pocsag::BitRate pocsag_bitrates[4] = {
pocsag::BitRate::FSK512,
pocsag::BitRate::FSK1200,
pocsag::BitRate::FSK2400,
pocsag::BitRate::FSK3200};
pocsag::BitRate::FSK3200,
};
std::string bitrate_str(BitRate bitrate);
std::string flag_str(PacketFlag packetflag);