Files
ua1zbe a1206d6fd4 UA1ZBE Custom Firmware v1.0
Features:
- VFO mode with direct frequency input
- POCSAG decoder (512/1200 baud) with BCH(31,21) correction
- Full-screen Spectrum analyzer
- FM Radio receiver
- RSSI signal indicator overlay
- Custom boot splash (UA1ZBE / POCSAG pager / build date)

Architecture:
- app/mode.c — mode dispatcher (VFO/POCSAG/Spectrum/FM)
- app/boot_splash.c — 2-second boot splash
- app/pocsag/ — POCSAG decoder + BCH correction
- app/display_rssi.c — RSSI indicator
- main.c — entry point with custom init
- syscalls.c — bare-metal _sbrk stub

Build: arm-none-eabi-gcc -Os -flto -Wall -Werror -Wextra
Size: 57.9KB Flash / 3.6KB RAM

Controls:
- 0-9: Direct frequency input (VFO)
- SK2: POCSAG mode
- SK1: Spectrum analyzer
- 0: FM Radio
- EXIT: Return to VFO
- F/*: Toggle 512/1200 baud (in POCSAG)
2026-04-12 00:44:05 +03:00

151 lines
4.4 KiB
C

/* 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 <stdbool.h>
#include <stdint.h>
/* 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 */