// ignore_for_file: constant_identifier_names import 'dart:math' as math; /// All system-wide numeric constants for the secure-voice pipeline. /// /// Design target: /// modem gross rate : 1 200 bps (4-FSK, 600 baud, 2 bits/symbol) /// RS(15,11) FEC net : ~880 bps /// voice codec : ~360 bps (LPC, 200 ms super-frames) /// one-way latency : ~250 ms (frame + processing + buffer) abstract final class C { // ── Sample rate ──────────────────────────────────────────────────── /// Must match the cellular audio path (PCM 8 kHz narrowband). static const int sampleRate = 8000; // ── 4-FSK modem ──────────────────────────────────────────────────── /// Symbol rate in baud. 600 baud × 2 bits/symbol = 1 200 bps gross. static const int baudRate = 600; /// Samples per symbol as a float (8000 / 600 = 13.333…). /// The modulator uses a phase accumulator; the demodulator uses an /// early–late gate timing recovery loop to handle the fraction. static const double samplesPerSymbol = sampleRate / baudRate; /// 4-FSK tone frequencies (Hz). Spaced 400 Hz apart, centred in /// the 300–3400 Hz cellular pass-band; chosen to survive AMR-NB codec. /// symbol dibit → frequency /// 00 → tone0 01 → tone1 10 → tone2 11 → tone3 static const List fskToneHz = [1000.0, 1400.0, 1800.0, 2200.0]; /// Amplitude of the transmitted FSK signal (0–1 normalised). /// Kept below 0.7 to avoid clipping after phone mic AGC compresses it. static const double txAmplitude = 0.60; /// Preamble: alternating dibit 00/11 repeated this many times before /// each packet. Used by the demodulator for timing acquisition. static const int preambleSymbols = 24; /// Two-byte wire sync-word placed after preamble (0x5A, 0xA5). static const List syncWord = [0x5A, 0xA5]; // ── Packet layout (pre-FEC plaintext frame) ──────────────────────── /// Bytes: [SYNC(2)] [SEQ(2)] [TYPE(1)] [LEN(1)] [PAYLOAD(11)] [CRC16(2)] static const int pktSyncLen = 2; static const int pktSeqLen = 2; static const int pktTypeLen = 1; static const int pktLenLen = 1; static const int pktPayloadLen = 11; // encrypted voice frame bytes static const int pktCrcLen = 2; static const int pktTotalBytes = pktSyncLen + pktSeqLen + pktTypeLen + pktLenLen + pktPayloadLen + pktCrcLen; // 19 B /// Packet type codes. static const int pkTypeVoice = 0x01; static const int pkTypeControl = 0x02; static const int pkTypeHandshake = 0x03; static const int pkTypePing = 0x04; // ── Reed-Solomon RS(15,11) over GF(16) ──────────────────────────── /// RS codeword length n (nibbles). static const int rsN = 15; /// RS data symbols per codeword k (nibbles). static const int rsK = 11; /// RS parity symbols per codeword (n-k). static const int rsParity = rsN - rsK; // 4 /// Correction capability: t = parity/2 = 2 nibble errors per codeword. static const int rsT = rsParity ~/ 2; // ── LPC voice codec ──────────────────────────────────────────────── /// Sub-frame size (samples). One LPC analysis frame = 20 ms at 8 kHz. static const int lpcSubframeSamples = 160; // 20 ms /// Number of sub-frames in one super-frame (transmitted as a packet). static const int lpcSubframesPerSuper = 10; // 200 ms total /// LPC analysis order (number of predictor coefficients). static const int lpcOrder = 10; /// Minimum pitch period in samples (max fundamental ~400 Hz). static const int pitchMinSamples = 20; /// Maximum pitch period in samples (min fundamental ~50 Hz). static const int pitchMaxSamples = 160; /// Pre-emphasis coefficient. Applied as s'[n] = s[n] - alpha·s[n-1]. static const double preEmphasis = 0.97; // ── AES-256-CTR ──────────────────────────────────────────────────── /// Key length in bytes. static const int aesKeyBytes = 32; /// Nonce length in bytes (packed from session-ID + packet-seq). static const int aesNonceBytes = 16; // AES block size = 128 bits // ── PBKDF2 ───────────────────────────────────────────────────────── static const int pbkdf2Iterations = 100000; static const int pbkdf2SaltBytes = 16; // ── AGC / Signal conditioning ────────────────────────────────────── /// Target RMS level for received signal (normalised 0–1). static const double agcTargetRms = 0.15; /// AGC attack time constant (samples). Fast attack. static const double agcAlpha = 0.001; // ── Goertzel window length (must be ≥ samplesPerSymbol) ─────────── /// We snap to 14 integer samples which is the nearest integer above /// samplesPerSymbol. Timing recovery compensates for the 0.67 sample drift. static const int goertzelWindow = 14; // ── Derived constants ────────────────────────────────────────────── /// Pre-computed Goertzel coefficients for each FSK tone. /// coeff[i] = 2 · cos(2π · f_i / sampleRate) static final List goertzelCoeff = fskToneHz .map((f) => 2.0 * math.cos(2.0 * math.pi * f / sampleRate)) .toList(growable: false); }