Files
mayhem-firmware/firmware/baseband/proc_flex.hpp
T
VasylSamoilov fc5beb0c09 FLEX RX: fix long-address numeric decode, improve short message format (#3136)
- Fix long-address numeric message decoding: read body[0] from Vy (j+1)
  for multi-word messages, from w1 for single-word. Fix MF word count
  for long addresses (n_field words, not n_field+1).
- Fix long-address short message: read 5 additional BCD digits from Vy
  for 8-digit decode.
- Fix BCD table: index 10 is '.' (dot) not ' ' (space) in both RX and TX.
- Clean up address decode: identify address type by value range per
  Table 3.8.1-1. Remove is_group/is_temp_group from packet structs.
- Rename SMSG to SHORT. Format message payload directly in baseband:
  TONE, NUM abc, SRC N, SRC N N=M R=R, RESERVED hex.
- Add BIW1 heartbeat packet (biw_field=0xFF) on every decoded frame.
- Move timezone table to file scope, simplify console log_message,
  add No signal initial status, simplify serial BIW output format.
2026-04-18 21:15:05 +02:00

198 lines
5.2 KiB
C++

#ifndef __PROC_FLEX_H__
#define __PROC_FLEX_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "dsp_decimate.hpp"
#include "dsp_demodulate.hpp"
#include "message.hpp"
#include "flex_defs.hpp"
#include "pocsag.hpp" // For EccContainer
#include <cstdint>
#include <array>
namespace flex {
enum class PageType {
SECURE,
SHORT_INSTRUCTION,
TONE,
STANDARD_NUMERIC,
SPECIAL_NUMERIC,
ALPHANUMERIC,
BINARY,
NUMBERED_NUMERIC
};
enum class State {
SYNC1,
FIW,
SYNC2,
DATA
};
struct FlexDemodParams {
unsigned int sample_freq = 24000;
double sample_last = 0.0;
int locked = 0;
int phase = 0;
unsigned int sample_count = 0;
unsigned int symbol_count = 0;
double envelope_sum = 0.0;
int envelope_count = 0;
uint64_t lock_buf = 0;
int symcount[4] = {0};
int timeout = 0;
int nonconsec = 0;
unsigned int baud = 1600;
};
struct FlexGroupHandler {
int64_t GroupCodes[17][100]; // Reduced size from 1000 to save RAM
int GroupCycle[17];
int GroupFrame[17];
};
struct FlexModulation {
double symbol_rate = 0.0;
double envelope = 0.0;
double zero = 0.0;
};
struct FlexStateInfo {
unsigned int sync2_count = 0;
unsigned int data_count = 0;
unsigned int fiwcount = 0;
State Current = State::SYNC1;
State Previous = State::SYNC1;
// S2 C-pattern detection
uint16_t sync2_shiftreg = 0; // 16-bit shift register for C match
int sync2_c_pos = -1; // symbol position where C was found (-1=not found)
int sync2_cinv_pos = -1; // symbol position where inv.C was found
};
struct FlexSync {
unsigned int sync = 0;
unsigned int baud = 0;
unsigned int levels = 0;
unsigned int polarity = 0;
uint64_t syncbuf = 0;
};
struct FlexFIW {
uint32_t rawdata = 0;
unsigned int checksum = 0;
unsigned int cycleno = 0;
unsigned int frameno = 0;
unsigned int roaming = 0; // bit 15: n (1=roaming provided)
unsigned int repeat = 0; // bit 16: r (1=multiple transmission)
unsigned int traffic = 0; // bits 17-20: t3-t0
};
struct FlexPhase {
uint32_t buf[88] = {0};
int idle_count = 0;
};
struct FlexData {
int phase_toggle = 0;
unsigned int data_bit_counter = 0;
FlexPhase PhaseA;
FlexPhase PhaseB;
FlexPhase PhaseC;
FlexPhase PhaseD;
};
enum class AddrType : uint8_t {
SHORT, // normal individual
LONG, // 9-10 digit
TEMPORARY, // 0x1F7800-0F (16 group slots)
OPERATOR, // 0x1F7810-1F (system messages)
NETWORK, // 0x1F6800-77FF (NID)
INFO_SVC, // 0x1F2800-67FF (under study)
RESERVED, // reserved ranges
UNKNOWN
};
struct FlexDecode {
PageType type = PageType::ALPHANUMERIC;
int long_address = 0;
int64_t capcode = 0;
AddrType addr_type = AddrType::SHORT;
int is_priority = 0;
};
} // namespace flex
class FlexProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
bool configured{false};
// DSP components
// 3.072MHz -> 24kHz (Decim 128)
// decim_0: 8, decim_1: 8, channel: 2. Total 128.
dsp::decimate::FIRC8xR16x24FS4Decim8 decim_0_iq{};
dsp::decimate::FIRC16xR16x32Decim8 decim_1_iq{};
dsp::decimate::FIRAndDecimateComplex channel_filter{};
dsp::demodulate::FM demod{};
// Buffers
std::array<complex16_t, 256> dst{};
const buffer_c16_t dst_buffer{dst.data(), dst.size()};
std::array<float, 16> audio{};
const buffer_f32_t audio_buffer{audio.data(), audio.size()};
// Flex State
flex::FlexDemodParams demodulator{};
flex::FlexModulation modulation{};
flex::FlexStateInfo state{};
flex::FlexSync sync{};
flex::FlexFIW fiw{};
flex::FlexData data{};
flex::FlexDecode decode{};
flex::FlexGroupHandler group_handler{};
pocsag::EccContainer ecc{};
// Methods
void configure();
void process_audio(const buffer_f32_t& audio);
// Internal Flex logic
int build_symbol(double sample);
void flex_demodulate(double sample);
void flex_sym(unsigned char sym);
unsigned int flex_sync_check(uint64_t buf);
unsigned int flex_sync(unsigned char sym);
void decode_mode(unsigned int sync_code);
void read_2fsk(unsigned int sym, uint32_t* dat); // Changed to uint32_t*
int decode_fiw();
int read_data(unsigned char sym);
void decode_data();
void decode_phase(char PhaseNo);
int bch_fix_errors(uint32_t* data_to_fix);
// Parsing
void parse_capcode(uint32_t aw1);
void parse_alphanumeric(uint32_t* phaseptr, const uint8_t* word_bad, char PhaseNo, int mw1, int mw2, int flex_groupmessage);
void parse_numeric(uint32_t* phaseptr, const uint8_t* word_bad, char PhaseNo, int j);
void parse_tone_only(uint32_t* phaseptr, char PhaseNo, int j);
void parse_unknown(uint32_t* phaseptr, char PhaseNo, int mw1, int mw2);
void send_packet(const flex::FlexPacket& packet);
void send_stats();
void send_debug(const char* text, uint32_t v1, uint32_t v2);
// Threads
BasebandThread baseband_thread{3072000, this, baseband::Direction::Receive};
};
#endif /*__PROC_FLEX_H__*/