feat: QO-100 beacon AO-40 FEC decode end-to-end (Milestone 2)

Full chain now decodes the QO-100 central beacon to 256-byte AO-40 frames
on-air (frames>0, RS errors=0). Builds on M1 (stable BPSK demod).

FEC backend (BeaconFEC.pas, TBeaconFEC):
- AO-40 FEC: distributed sync (65 bit, step 80) -> 80x65 deinterleave ->
  Viterbi r=1/2 k=7 -> CCSDS descramble -> 2x RS(160,128). Links system
  libfec (Karn) load-time (libfec.so/.dll/.dylib). Viterbi AO-40 [0x4F,-0x6D]
  remapped to libfec [0x6D,0x4F]. Validated against gr-satellites reference
  vectors (fectest2: exact 256-byte frame, 0 mismatches).

DBPSK Manchester frontend (BeaconDecoder.pas):
- Chip-rate (800) processing: RRC matched filter -> ML signal-times-slope
  timing recovery -> Costas -> Manchester combine (block phase select) ->
  differential decode -> soft symbols to FEC.
- Carrier acquisition: squaring-FFT estimates the residual (carrier at +-pi/chip
  defeats decision-directed Costas), one-shot pulls it to DC via residual NCO.
- Costas frequency offload into pre-RRC NCO (with deadband) tracks LNB drift
  without hitting the +-pi Costas clamp.
- ML TED replaces Gardner, which degenerates on the Manchester chip stream.

Temporary on-air diagnostics retained (IQ dump + STATE log) pending wider
signal validation; to be removed before final cleanup.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 10:21:33 +03:00
co-authored by Claude Opus 4.8
parent c063d7a197
commit afc8775ddb
7 changed files with 717 additions and 50 deletions
+5 -2
View File
@@ -1261,7 +1261,8 @@ begin
BeaconLog(Format(
'STATE decHz=%.1f center=%.1f off=%.1f | Fs=%.0f Fdec=%.0f sps=%.1f | ' +
'resid=%.1f prom=%.1f snr=%.1f baud=%.1f decRMS=%.5f symMag=%.5f ' +
'timErr=%.5f wsym=%.4f carHz=%.1f hold=%d | CAR=%d SYM=%d invert=%d lockOn=%d locked=%d',
'timErr=%.5f wsym=%.4f carHz=%.1f hold=%d | CAR=%d SYM=%d invert=%d lockOn=%d locked=%d ' +
'| manPh=%d m0=%.5f m1=%.5f frames=%d rsErr=%d',
[FBeaconDecHz, FCenterFreq, FBeaconLogScope.OffsetHz,
FBeaconLogScope.FsHz, FBeaconLogScope.FdecHz, FBeaconLogScope.Sps,
FBeaconLogScope.ResidHz, FBeaconLogScope.CarProm, FBeaconLogScope.SNRdB,
@@ -1269,7 +1270,9 @@ begin
FBeaconLogScope.TimErr, FBeaconLogScope.Wsym, FBeaconLogScope.CarFreqHz,
FBeaconLogScope.LockHold,
Ord(FBeaconLogScope.CarrierLock), Ord(FBeaconLogScope.SymbolLock),
Ord(FBeaconDecInvert), Ord(FBeaconLockOn), Ord(FBeaconLocked)]));
Ord(FBeaconDecInvert), Ord(FBeaconLockOn), Ord(FBeaconLocked),
FBeaconLogScope.ManPhase, FBeaconLogScope.ManM0, FBeaconLogScope.ManM1,
FBeaconLogScope.Frames, FBeaconLogScope.LastRSErr]));
end;
end;