hfmodemDocumentation

STANAG 4539: the high-rate waveform

hfmodem.bateleur.s4539 sends and receives the waveform STANAG 4539 uses for 3200 to 9600 bit/s, and 12800 bit/s uncoded. It was written from MIL-STD-188-110B appendix C, which specifies the same waveform; the STANAG's own text was not to hand. No foreign 4539 implementation has been tested against this one: every result below is this implementation against itself, through bateleur.sim's channels.

The waveform (appendix C)

PartWhereParagraphs
2400 symbols/s on 1800 Hz; data as QPSK (scrambled to look like 8-PSK), 8-PSK, 16-, 32- and 64-QAM; the tables C-V to C-VIIs4539.TABLES, data_frameC.5.1
Scrambling: 9 stages, x^9 + x^4 + 1, reset every frame; added to 8-PSK symbol numbers, exclusive-or'd with QAM onesscramble_valuesC.5.1.3, figure C-5
Frames: 256 data symbols and a 31-symbol mini-probe; after every 72, the last 72 symbols of the preamble againsymbols, frames_planC.5.2
Preamble: 0-7 blocks of 184 for AGC, 184 fixed symbols, and 103 carrying the rate and interleaver as three Barker codes' phasespreamble, known103C.5.2.1
Mini-probe signs carrying rate, interleaver and set countprobe_signsC.5.2.2
Rate 1/2 K=7 code made a block code by full tail-biting, punctured to 3/4encode_block, fec.encode_tailbitingC.5.3.2
Block interleaver, 1 to 72 frames, loaded by increment and read in order, aligned to the reinserted preambleencode_blockC.5.3.3
End of message 4B65A5B2, and zero fillfind_eomC.5.4.3

The implemented scrambler puts the newest bit at index 0, shifts right and feeds index 0 from index 4 XOR index 8: the fifth and ninth stages, or s[n-5] XOR s[n-9]. The seed is eight zeros followed by one. The last 3–6 stages form each output symbol before those shifts. This reading is unverified against a foreign 4539 implementation. Its recurrence agrees with the independently cross-checked 4285 generator, but that does not establish the 4539 seed, output grouping or reset convention.

Tail-biting seeds from the first six input bits and encodes the remaining bits followed by those six. A known-payload foreign recording spanning two interleaver blocks, including the preamble and mini-probes, would settle these readings. Local round trips and the table C-XVI interleaver example establish only the checks they exercise.

The receiver

Acquisition finds the preamble's 184 fixed symbols, then reads the rate and interleaver from the Barker codes: a symbol-spaced channel is fitted to the known symbols around them, and each of the 64 sets of three phases is tried against what arrived. Read this way, the mode came out right in every case tried on the 2 ms / 1 Hz channel down to 5 dB.

A preamble found under the threshold (to 0.8 of it) is taken if the first frames' mini-probes are where it says, with the signs it says: a fade can take a preamble down that far, and on the 2 ms / 1 Hz channel at 15 dB one did.

Far off frequency. Appendix C asks for 75 Hz (C.6.3, C.6.4). The search correlates the preamble in chunks of 23 symbols, and 75 Hz turns such a chunk 260 degrees, which leaves a third of the score, under the threshold. So the search runs three times, shifted to 0 and to 50 Hz either way, and a preamble found by more than one is taken from the search whose shift was nearest what it measured: the others may have locked to a later path of it.

Late entry. A receiver that missed the preamble (a fade can cover all 77 ms of it) joins at the next reinserted preamble, which is how appendix C asks that a receiver be able to deduce the mode "from the subsequent data portion": it looks for the reinserted preamble's closing "-" probe, checks for the "+" probe 71 symbols before it (an ordinary probe has data there), reads the Barker codes between, and decodes from the interleaver block that starts there. The two known ends of a reinserted preamble, 71 symbols apart, give the carrier offset to a fraction of a hertz; the search alone left it several hertz out, and the gains then turned too far between probes to interpolate. The synchronisation preamble ends with the same 103 symbols, so a preamble whose first part was lost in a fade is joined at its end, and nothing after it is lost.

The equalizer (blockeq.BlockEqualizer) is the path model of the 188-110 receiver (patheq), with one difference that decides everything at 64-QAM: the paths' gains come from the probes, not from the equalizer's own decisions. A ninth of this waveform's symbols are known, and the data is dense; tracking the gains from decisions, a fade's burst of wrong decisions turned the gains a quarter round and the equalizer stayed there, probes and all (9600 bit/s on 2 ms / 1 Hz lost most of its symbols at 40 dB). So each probe's gains are fitted from the samples only known symbols reach, the gains between two probes are interpolated, and a data block is equalized when the probe after it has come. The path search runs again every frame over the last eight probes, where the decisions cannot mislead it.

Then, for each block:

Additional receiver requirements:

The frequency loop is second order: from the gains' common turn between probes it keeps the offset and its rate of change, so a steady Doppler sweep is followed without lag, to 100 Hz either way. Each change of frequency turns the gains already fitted to match, and a block's gains are interpolated between its probes with their common turn taken evenly, not straight across, which at a quarter turn loses 3 dB midway.

Simulation measurements

The recorded short harness was rerun in simulation on 2026-09-30 with corrected Gaussian-spectrum Rayleigh fading. Each row uses one trial, one second of requested payload (minimum 16 bytes), message seed 1000 and channel seed 0. Preambles, interleaver fill and flush extend the waveform beyond the requested payload duration. SNR is measured in 3 kHz; the carrier offset is +23 Hz unless shown otherwise. Two-path channels have independent equal-power taps. Spread is twice the Gaussian spectral standard deviation. Every missing bit counts as an error.

Bounds below are one-sided 95% binomial upper bounds assuming independent bit errors. Fading errors are correlated, so these are conditional bounds. The short trials do not establish a standards performance target.

The earlier long-run figures lacked per-trial records and an exact source revision; their fading figures are withdrawn. The corrected short runs below retain zero errors and unchanged conditional bounds in all fifteen rows. The C6 checks request only one second of payload: they do not measure a five-minute synchronization hold or a full traversal of the Doppler sweep.

C-XVII: short run

ModeChannel: delay / spreadSNR dBBitsErrorsUpper 95%
3200VLAWGN93,20000.000935728
3200VL2 paths, 2 ms / 1 Hz153,20000.000935728
4800VLAWGN134,80000.000623916
4800VL2 paths, 2 ms / 1 Hz204,80000.000623916
6400VLAWGN166,40000.000467974
6400VL2 paths, 2 ms / 1 Hz246,40000.000467974
8000VLAWGN198,00000.000374396
8000VL2 paths, 2 ms / 1 Hz288,00000.000374396
9600VLAWGN219,60000.000312007
9600VL2 paths, 2 ms / 1 Hz339,60000.000312007
12800USAWGN2712,80000.000234014

C6.3: short run

ModeChannel: delay / spreadSNR dBBitsErrorsUpper 95%Offset / sweep
9600L2 paths, 2 ms / 1 Hz309,60000.000312007-75 Hz / +0 Hz/s
9600L2 paths, 2 ms / 1 Hz309,60000.000312007+75 Hz / +0 Hz/s

C6.4: short run

ModeChannel: delay / spreadSNR dBBitsErrorsUpper 95%Offset / sweep
9600LAWGN249,60000.000312007+0 Hz / -3.5 Hz/s
9600LAWGN249,60000.000312007+0 Hz / +3.5 Hz/s

Reproduce these simulation rows serially:

python -m hfmodem.bateleur.measure --preset c-xvii --trials 1 --seconds 1 --output c-xvii-short.json
python -m hfmodem.bateleur.measure --preset c6-3 --trials 1 --seconds 1 --output c6-3-short.json
python -m hfmodem.bateleur.measure --preset c6-4 --trials 1 --seconds 1 --output c6-4-short.json

Omitting the short-run overrides requests the longer preset. See bateleur's simulation measurements for the channel correction and the recorded fields.

Not implemented