hfmodemDocumentation

The 188-110A "mod" waveform (receive only, raw bits)

hfmodem.bateleur.mod110 receives a waveform known to listeners as "MIL-188-110A-MOD" or the "hybrid modem": a tone header, a parallel-tone section, and then a 188-110A serial data phase. No standard describes it. It was worked out here from seven published off-air recordings (sigidwiki's, fetched for the analysis and not kept in this tree). No other implementation was read. The receiver stops at the channel bits. How those bits are coded is not known, so it does not decode them further.

What was measured

PartWhat the recordings show
Tone headerFour tones at 900, 1350, 1800 and 2250 Hz, or seven at 450 to 3150 Hz in 450 Hz steps, for about 1.6 s. The 1800 Hz tone is where the serial carrier is, which gives each recording's tuning offset.
Parallel sectionAbout eighteen tones on a 75 Hz grid at 75 Bd, for the 3.8 s from the header's end to the data phase. Not read.
Data phase188-110A serial at 150-1200 bit/s: 2400 Bd on 1800 Hz, frames of 20 data and 20 probe symbols, and 110's 160-symbol randomiser, which starts at the first frame. The probes fall exactly where 110's do. The data symbols are BPSK.
Sync phaseThe first 16 frames (640 symbols, 267 ms) carry zero data, so every symbol is the randomiser's. This matches the 267 ms "synchronization phase" in a commercial decoder's description.
Two formatsSome bursts send each channel bit twice in a row and end with a run of about 500 ones (every data symbol turned half round). The rest send each bit once and simply stop. The header's tone count does not say which: one six-tone recording has each.
Burst lengthA whole number of 6432-bit blocks, as far as could be measured: one, two, three and about four.

How the data is not coded

These were tested on the bits and ruled out:

How the bits are arranged

From all 39 of the recordings' 6432-bit blocks:

An earlier reading of these blocks found that matching blocks agree on 64-76% of their bits, and took that for repeated content. It was not: read down the columns, the matching pairs differ in their payloads and agree only in the zero fill and the markers.

The payload, read down the columns, still looks random. No rate-1/2 convolutional code of constraint length up to 7 fits it, and the 0xF1F2F3F4F5F601 pattern is not in it. Unused space stays zero, so whatever hides the payload does not run over the fill.

The receiver

Acquisition looks for the sync phase's 640 known symbols. A 188-110 data phase sending zeros (its flush, for one) is exactly that sequence. So a hit is taken only if the tone header shows before it: the four tones every variant has must carry half of the band's power from 5.2 to 4.0 s ahead. In the recordings they carried 0.6 to 0.98 of it, and anywhere else under 0.1. The threshold is 0.12. Noise alone scores under 0.05 against the 640 symbols, and the weakest burst in the recordings scored 0.17.

Equalization is 188-110's: the path model beside the RLS equalizer, trained on the sync phase and then on each probe.

The end is read from the burst's own framing. The six column markers sit at a row that moves with the burst's length (1056 − 32k for k blocks), so the receiver reads the bits as 1 to 5 blocks and takes the count whose last block shows all six markers, with at most 40 of their 192 bits wrong. On the recordings the right count had 0 to 30 wrong and every other count 58 or more. Only where no count shows them does it fall back on the end marker, or on the probes going to noise (both as wrong as noise makes them and three times as wrong as in the sync phase).

A shortcut was tried and dropped: judging the end from the probes as received, without the equalizer. One recording's sample clock is slightly off, and over four seconds that check drifted out of time with the signal and cut its bursts short. The equalizer tracks the timing.

The format is read from the bits. If nine pairs in ten agree, each pair is combined into one bit, soft values summed.

Using it

python -m hfmodem.bateleur.offair decode MOD --all --carrier 1814 capture.wav

--carrier is where the serial carrier is: the third tone of a four-tone header, the fourth of seven. --invert is for a recording made on the other sideband. Each burst's six columns are printed as hex, first bit high, in the message's order, or written one after the other with --output. The raw channel bits are Reception.bits.

Recordings

Every burst in the seven recordings, through mod110.receive:

RecordingHeaderBursts (blocks, by their markers)Format
11 s sample4 tones1once
6425 kHz4 tones1, 1, 1, 1, 1, 1once
3742.7 kHz (2016)4 tones1, 1once
3746.08 kHz (made in LSB, --invert)4 tones1, 1, 1, 1, 1once
10149.7 kHz (2015)4 tones2, 4, 4, 4once
mil-188-110a-mod6 tones1, 1, 1, 1, with the end markertwice
Flipradio6 tones3, 3once

Every burst's length was read from its markers. Two of the 6425 kHz bursts are weak (the probes put them under 0 dB). Before the markers were used, the probe test could not tell their end from noise and they ran on to 900 and 1524 frames. The 2015 four-block bursts used to be cut short at about 1260 frames of their 1287.

The Flipradio recording's sample clock runs 0.7% slow. Its header tones are 446.9 Hz apart, not 450. It decodes only once resampled by that much, which offair does not do. The receiver's timing loop follows small clock errors, not that one.