mirror of
https://github.com/eried/portapack-mayhem.git
synced 2025-08-09 07:02:26 -04:00
Support squelch in pocsag (#1415)
* Support squelch in pocsag * Revert smooth threshold
This commit is contained in:
parent
d8930db8af
commit
e7e1bedcad
7 changed files with 100 additions and 78 deletions
|
@ -95,6 +95,7 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav)
|
||||||
&field_lna,
|
&field_lna,
|
||||||
&field_vga,
|
&field_vga,
|
||||||
&field_frequency,
|
&field_frequency,
|
||||||
|
&field_squelch,
|
||||||
&field_volume,
|
&field_volume,
|
||||||
&image_status,
|
&image_status,
|
||||||
&text_packet_count,
|
&text_packet_count,
|
||||||
|
@ -111,6 +112,11 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav)
|
||||||
|
|
||||||
logger.append(LOG_ROOT_DIR "/POCSAG.TXT");
|
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&) {
|
button_ignore_last.on_select = [this](Button&) {
|
||||||
settings_.enable_ignore = true;
|
settings_.enable_ignore = true;
|
||||||
settings_.address_to_ignore = last_address;
|
settings_.address_to_ignore = last_address;
|
||||||
|
@ -122,8 +128,8 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav)
|
||||||
};
|
};
|
||||||
|
|
||||||
refresh_ui();
|
refresh_ui();
|
||||||
receiver_model.enable();
|
|
||||||
audio::output::start();
|
audio::output::start();
|
||||||
|
receiver_model.enable();
|
||||||
baseband::set_pocsag();
|
baseband::set_pocsag();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,10 +136,7 @@ class POCSAGAppView : public View {
|
||||||
bool hide_addr_only() const { return settings_.hide_addr_only; };
|
bool hide_addr_only() const { return settings_.hide_addr_only; };
|
||||||
|
|
||||||
NavigationView& nav_;
|
NavigationView& nav_;
|
||||||
RxRadioState radio_state_{
|
RxRadioState radio_state_{};
|
||||||
12'500, // POCSAG is FSK +/- 4.5MHz, 12k5 is a good filter.
|
|
||||||
3'072'000, // Match baseband_fs in proc_pocsag.
|
|
||||||
};
|
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
POCSAGSettings settings_{};
|
POCSAGSettings settings_{};
|
||||||
|
@ -161,24 +158,33 @@ class POCSAGAppView : public View {
|
||||||
void on_stats(const POCSAGStatsMessage* stats);
|
void on_stats(const POCSAGStatsMessage* stats);
|
||||||
|
|
||||||
uint32_t last_address = 0xFFFFFFFF;
|
uint32_t last_address = 0xFFFFFFFF;
|
||||||
pocsag::POCSAGState pocsag_state{};
|
pocsag::EccContainer ecc{};
|
||||||
|
pocsag::POCSAGState pocsag_state{&ecc};
|
||||||
POCSAGLogger logger{};
|
POCSAGLogger logger{};
|
||||||
uint16_t packet_count = 0;
|
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{
|
RxFrequencyField field_frequency{
|
||||||
{0 * 8, 0 * 8},
|
{0 * 8, 0 * 8},
|
||||||
nav_};
|
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{
|
AudioVolumeField field_volume{
|
||||||
{28 * 8, 0 * 16}};
|
{28 * 8, 0 * 16}};
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,13 @@
|
||||||
|
|
||||||
#include "proc_pocsag.hpp"
|
#include "proc_pocsag.hpp"
|
||||||
|
|
||||||
|
#include "dsp_iir_config.hpp"
|
||||||
#include "event_m4.hpp"
|
#include "event_m4.hpp"
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
|
void POCSAGProcessor::execute(const buffer_c8_t& buffer) {
|
||||||
// This is called at 1500Hz
|
// 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 decim_1_out = decim_1.execute(decim_0_out, dst_buffer);
|
||||||
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
|
const auto channel_out = channel_filter.execute(decim_1_out, dst_buffer);
|
||||||
auto audio = demod.execute(channel_out, audio_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);
|
audio_output.write(audio);
|
||||||
|
|
||||||
|
// Smooth the data to make decoding more accurate.
|
||||||
|
smooth.Process(audio.p, audio.count);
|
||||||
processDemodulatedSamples(audio.p, 16);
|
processDemodulatedSamples(audio.p, 16);
|
||||||
extractFrames();
|
extractFrames();
|
||||||
}
|
}
|
||||||
|
@ -71,8 +76,23 @@ int POCSAGProcessor::OnDataFrame(int len, int baud) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void POCSAGProcessor::on_message(const Message* const message) {
|
void POCSAGProcessor::on_message(const Message* const message) {
|
||||||
if (message->id == Message::ID::POCSAGConfigure)
|
switch (message->id) {
|
||||||
configure();
|
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() {
|
void POCSAGProcessor::configure() {
|
||||||
|
@ -91,13 +111,10 @@ void POCSAGProcessor::configure() {
|
||||||
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
decim_1.configure(taps_11k0_decim_1.taps, 131072);
|
||||||
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.
|
||||||
// Smoothing should be roughly sample rate over max baud
|
|
||||||
// 24k / 3.2k is 7.5
|
|
||||||
smooth.SetSize(8);
|
|
||||||
|
|
||||||
// TODO: support squelch?
|
// Smoothing should be roughly sample rate over max baud
|
||||||
// audio_output.configure(message.audio_hpf_config, message.audio_deemph_config, (float)message.squelch_level / 100.0);
|
// 24k / 3.2k = 7.5
|
||||||
audio_output.configure(false);
|
smooth.SetSize(8);
|
||||||
|
|
||||||
// Set up the frame extraction, limits of baud
|
// Set up the frame extraction, limits of baud
|
||||||
setFrameExtractParams(demod_input_fs, 4000, 300, 32);
|
setFrameExtractParams(demod_input_fs, 4000, 300, 32);
|
||||||
|
|
|
@ -106,10 +106,10 @@ class SmoothVals {
|
||||||
|
|
||||||
// Use a rolling smoothed value while processing the buffer
|
// Use a rolling smoothed value while processing the buffer
|
||||||
for (int buffPos = 0; buffPos < numVals; ++buffPos) {
|
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) {
|
if (m_pos >= m_size) {
|
||||||
m_pos = 0;
|
m_pos = 0;
|
||||||
} // loop if reached the end of the stored values
|
}
|
||||||
|
|
||||||
m_sumVal -= (CalcType)m_lastVals[m_pos]; // Subtract the oldest value
|
m_sumVal -= (CalcType)m_lastVals[m_pos]; // Subtract the oldest value
|
||||||
m_lastVals[m_pos] = valBuff[buffPos]; // Store the new value
|
m_lastVals[m_pos] = valBuff[buffPos]; // Store the new value
|
||||||
|
|
|
@ -38,9 +38,7 @@ constexpr iir_biquad_config_t audio_48k_hpf_300hz_config{
|
||||||
// scipy.signal.butter(2, 300 / 12000.0, 'highpass', analog=False)
|
// scipy.signal.butter(2, 300 / 12000.0, 'highpass', analog=False)
|
||||||
constexpr iir_biquad_config_t audio_24k_hpf_300hz_config{
|
constexpr iir_biquad_config_t audio_24k_hpf_300hz_config{
|
||||||
{0.94597686f, -1.89195371f, 0.94597686f},
|
{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)
|
// scipy.signal.butter(2, 30 / 12000.0, 'highpass', analog=False)
|
||||||
constexpr iir_biquad_config_t audio_24k_hpf_30hz_config{
|
constexpr iir_biquad_config_t audio_24k_hpf_30hz_config{
|
||||||
|
|
|
@ -225,30 +225,14 @@ void pocsag_encode(const MessageType type, BCHCode& BCH_code, const uint32_t fun
|
||||||
} while (char_idx < message_size);
|
} while (char_idx < message_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Get the number of bits that differ between the two values.
|
// EccContainer
|
||||||
// -------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
inline uint8_t bitsDiff(unsigned long left, unsigned long right) {
|
EccContainer::EccContainer() {
|
||||||
unsigned long xord = left ^ right;
|
setup_ecc();
|
||||||
uint8_t count = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < 32; i++) {
|
|
||||||
if ((xord & 0x01) == 1) ++count;
|
|
||||||
xord = xord >> 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
void EccContainer::setup_ecc() {
|
||||||
// -------------------------------------------------------------------------------
|
|
||||||
static uint32_t ecs[32]; /* error correction sequence */
|
|
||||||
static uint32_t bch[1025];
|
|
||||||
static int eccSetup = 0;
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
|
||||||
// -------------------------------------------------------------------------------
|
|
||||||
void setupecc() {
|
|
||||||
unsigned int srr = 0x3b4;
|
unsigned int srr = 0x3b4;
|
||||||
unsigned int i, n, j, k;
|
unsigned int i, n, j, k;
|
||||||
|
|
||||||
|
@ -311,23 +295,12 @@ void setupecc() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------
|
int EccContainer::error_correct(uint32_t& val) {
|
||||||
// -------------------------------------------------------------------------------
|
|
||||||
inline int errorCorrection(uint32_t& val) {
|
|
||||||
// Set up the tables the first time
|
|
||||||
if (eccSetup == 0) {
|
|
||||||
setupecc();
|
|
||||||
eccSetup = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i, synd, errl, acc, pari, ecc, b1, b2;
|
int i, synd, errl, acc, pari, ecc, b1, b2;
|
||||||
|
|
||||||
errl = 0;
|
errl = 0;
|
||||||
pari = 0;
|
pari = 0;
|
||||||
|
|
||||||
/* run through error detection and correction routine */
|
|
||||||
|
|
||||||
// for (i=0; i<=20; i++)
|
|
||||||
ecc = 0;
|
ecc = 0;
|
||||||
for (i = 31; i >= 11; --i) {
|
for (i = 31; i >= 11; --i) {
|
||||||
if (val & (1 << i)) {
|
if (val & (1 << i)) {
|
||||||
|
@ -336,7 +309,6 @@ inline int errorCorrection(uint32_t& val) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for (i=21; i<=30; i++)
|
|
||||||
acc = 0;
|
acc = 0;
|
||||||
for (i = 10; i >= 1; --i) {
|
for (i = 10; i >= 1; --i) {
|
||||||
acc = acc << 1;
|
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,
|
// Error correct twice. First time to fix any errors it can,
|
||||||
// second time to count number of errors that couldn't be fixed.
|
// second time to count number of errors that couldn't be fixed.
|
||||||
errorCorrection(codeword);
|
state.ecc->error_correct(codeword);
|
||||||
auto error_count = errorCorrection(codeword);
|
auto error_count = state.ecc->error_correct(codeword);
|
||||||
|
|
||||||
switch (state.mode) {
|
switch (state.mode) {
|
||||||
case STATE_CLEAR:
|
case STATE_CLEAR:
|
||||||
|
|
|
@ -56,23 +56,46 @@ enum MessageType : uint32_t {
|
||||||
ALPHANUMERIC
|
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 {
|
struct POCSAGState {
|
||||||
uint8_t codeword_index;
|
EccContainer* ecc = nullptr;
|
||||||
uint32_t function;
|
uint8_t codeword_index = 0;
|
||||||
uint32_t address;
|
uint32_t function = 0;
|
||||||
|
uint32_t address = 0;
|
||||||
Mode mode = STATE_CLEAR;
|
Mode mode = STATE_CLEAR;
|
||||||
OutputType out_type = EMPTY;
|
OutputType out_type = EMPTY;
|
||||||
uint32_t ascii_data;
|
uint32_t ascii_data = 0;
|
||||||
uint32_t ascii_idx;
|
uint32_t ascii_idx = 0;
|
||||||
uint32_t errors;
|
uint32_t errors = 0;
|
||||||
std::string output;
|
std::string output{};
|
||||||
};
|
};
|
||||||
|
|
||||||
const pocsag::BitRate pocsag_bitrates[4] = {
|
const pocsag::BitRate pocsag_bitrates[4] = {
|
||||||
pocsag::BitRate::FSK512,
|
pocsag::BitRate::FSK512,
|
||||||
pocsag::BitRate::FSK1200,
|
pocsag::BitRate::FSK1200,
|
||||||
pocsag::BitRate::FSK2400,
|
pocsag::BitRate::FSK2400,
|
||||||
pocsag::BitRate::FSK3200};
|
pocsag::BitRate::FSK3200,
|
||||||
|
};
|
||||||
|
|
||||||
std::string bitrate_str(BitRate bitrate);
|
std::string bitrate_str(BitRate bitrate);
|
||||||
std::string flag_str(PacketFlag packetflag);
|
std::string flag_str(PacketFlag packetflag);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue