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
| Part | What the recordings show |
|---|---|
| Tone header | Four 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 section | About 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 phase | 188-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 phase | The 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 formats | Some 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 length | A 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:
- 188-110's own coding and interleaving, at 150, 300 and 600 bit/s with either interleaver, at every block alignment;
- any rate-1/2 convolutional code of constraint length up to 7 without interleaving (all 16384 generator pairs, with the doubling undone);
- simple row-column block interleavers of the sizes that fit;
- plaintext carrying the 0xF1F2F3F4F5F601 pattern an analyst reported in this traffic, in either bit order, plain or inverted.
How the bits are arranged
From all 39 of the recordings' 6432-bit blocks:
- A matrix of six columns. A burst of k blocks is one matrix of k × 1072 rows and 6 columns. It is sent a row at a time, and the message fills it a column at a time. What the message does not fill stays zero. A short message leaves the last columns empty. In the stream as sent, this shows as zeros at the trailing positions of every 6-bit group. Read down the columns, the payload comes first and the zeros last.
- A marker at the end of each column. Each column carries a byte of its own four times (0x4F, 0x2F, 0x8E, 0x6C, 0x4A, 0x20, first bit high), then 16 bits more. The marker is the same in every burst of every recording, and is there even in a column that is otherwise all zeros. It falls 48 bits before the column's end in single-block bursts, and elsewhere in the last block of longer ones. The 16 bits after it are not a CRC-16 of the column (no common variant matched, though at these SNRs bit errors could hide one). They differ between empty columns too, and always start with binary 11.
- A table at the top of the last column. What looked like alternating data and zero words is a table of 8-byte entries: 4 bytes of content, then 4 zero bytes. It sits at the top of the last column the message uses: c5 in the 6425 kHz bursts, c3 or c4 in the 3746 kHz ones, the second block's c0 in a two-block burst. The column's data continues after it. There are 4 to 6 entries; counted to the first bit error, the counts may be low. Each entry is 2 bytes that vary, a byte whose low three bits are a small number (some with the top two bits set as well), and a fourth byte. The entry count does not follow the number of full columns: three full columns came with tables of 4 and 5 entries. It looks like a directory in the clear, but what it indexes is not known.
- 64-byte units in the doubled format. In every bit-doubled burst, columns 1, 2, 4 and 5 hold 64 bytes, then two constant bytes, then zeros. The two bytes are 2F 8D, 8E 93, 4A EF and 20 84: the column's marker byte and one more. Columns 0 and 3 are full.
- A start. Column 0 begins with 1111 in nearly every burst. Nothing else near the start is common to all bursts.
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:
| Recording | Header | Bursts (blocks, by their markers) | Format |
|---|---|---|---|
| 11 s sample | 4 tones | 1 | once |
| 6425 kHz | 4 tones | 1, 1, 1, 1, 1, 1 | once |
| 3742.7 kHz (2016) | 4 tones | 1, 1 | once |
3746.08 kHz (made in LSB, --invert) | 4 tones | 1, 1, 1, 1, 1 | once |
| 10149.7 kHz (2015) | 4 tones | 2, 4, 4, 4 | once |
| mil-188-110a-mod | 6 tones | 1, 1, 1, 1, with the end marker | twice |
| Flipradio | 6 tones | 3, 3 | once |
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.