/* UA1ZBE Custom Firmware - POCSAG Decoder * * Decodes POCSAG paging signals from FM discriminator output. * Supports 512 and 1200 baud rates with dynamic switching. * * POCSAG Protocol: * - Preamble: 0xAAAAAAAB (at least 576 bits of alternating 1010... ending with 1) * - Sync word: 0x7CD21538 * - Data words: 32-bit (21 data + 10 BCH + 1 parity) * - BCH(31,21) code, corrects up to 2 errors per word * * Architecture: * - Samples audio from BK4819 discriminator (REG_69 AF output or FSK FIFO) * - Zero-crossing detection for bit slicing * - State machine: IDLE -> PREAMBLE -> SYNC -> DATA * - Ring buffer for decoded messages * - Zero malloc — all static allocation */ #ifndef APP_POCSAG_POCSAG_H #define APP_POCSAG_POCSAG_H #include #include /* POCSAG baud rates */ #define POCSAG_BAUD_512 512 #define POCSAG_BAUD_1200 1200 /* Maximum message length in bytes (arbitrary, fits in static buffer) */ #define POCSAG_MSG_MAX_LEN 64 /* Maximum messages in ring buffer */ #define POCSAG_MSG_POOL_SIZE 8 /* Decoded message structure */ typedef struct { uint32_t address; /* Address (21-bit frame address) */ uint8_t func; /* Function code (2 bits) */ char text[POCSAG_MSG_MAX_LEN]; /* Decoded text (alpha pager) */ uint8_t text_len; /* Length of decoded text */ bool is_alpha; /* true = alpha pager, false = numeric */ uint32_t timestamp; /* System tick when received */ } pocsag_msg_t; /* Decoder state machine states */ typedef enum { POCSAG_STATE_IDLE = 0, POCSAG_STATE_PREAMBLE, POCSAG_STATE_SYNC, POCSAG_STATE_DATA } pocsag_state_t; /* Decoder context — all static, no malloc */ typedef struct { /* Configuration */ uint32_t baud_rate; /* Current baud rate: 512 or 1200 */ /* State machine */ pocsag_state_t state; /* Bit accumulation */ uint32_t shift_reg; /* Shift register for incoming bits */ uint8_t bit_count; /* Number of bits collected in shift_reg */ uint8_t preamble_count; /* Consecutive alternating bits in preamble */ /* Sync detection */ uint8_t sync_word_count; /* Number of sync words found in a row */ /* Word collection */ uint8_t batch_count; /* Words in current batch (0-16) */ uint8_t word_in_batch; /* Current word position in batch */ /* Message assembly */ uint32_t current_address; uint8_t current_func; uint8_t msg_data[POCSAG_MSG_MAX_LEN]; uint8_t msg_len; bool msg_is_alpha; /* Ring buffer of decoded messages */ pocsag_msg_t messages[POCSAG_MSG_POOL_SIZE]; volatile uint8_t msg_read_idx; volatile uint8_t msg_write_idx; volatile uint8_t msg_count; /* Statistics */ uint32_t total_bits; uint32_t total_words; uint32_t corrected_errors; uint32_t uncorrectable_errors; /* Flag for new message available */ volatile bool msg_available; /* Signal active flag */ bool signal_detected; } pocsag_decoder_t; /* Global decoder instance */ extern pocsag_decoder_t gPocsag; /* === API === */ /* Initialize decoder, set baud rate */ void POCSAG_Init(uint32_t baud_rate); /* Switch baud rate dynamically (512 <-> 1200) */ void POCSAG_SwitchBaud(uint32_t baud_rate); /* Get current baud rate */ uint32_t POCSAG_GetBaud(void); /* Feed audio sample (12-bit ADC value from discriminator) * Call this from the 10ms timeslice or a dedicated sampling timer. * Returns true if a bit was detected. */ bool POCSAG_FeedSample(uint16_t audio_sample); /* Feed a single bit (after external bit-slicing) */ void POCSAG_FeedBit(bool bit); /* Check if a decoded message is available (non-blocking) */ bool POCSAG_MessageAvailable(void); /* Pop the next decoded message from the ring buffer */ bool POCSAG_GetMessage(pocsag_msg_t *msg); /* Get decoder state (for display) */ pocsag_state_t POCSAG_GetState(void); /* Get signal detection status */ bool POCSAG_SignalDetected(void); /* Reset decoder to idle state */ void POCSAG_Reset(void); /* Process decoder state machine (call from main loop) */ void POCSAG_Process(void); /* Configure BK4819 for POCSAG reception on given frequency */ void POCSAG_ConfigureRadio(uint32_t frequency_hz); /* Stop POCSAG reception, restore normal RX */ void POCSAG_Stop(void); /* Get state description string */ const char *POCSAG_StateString(pocsag_state_t state); #endif /* APP_POCSAG_POCSAG_H */