hfmodemDocumentation

04 — Frame & block formats

The frame types VARA HF puts on the air, the callsign-keyed handshake tone generators, the byte layouts of the link-setup and DATA containers, and block sizing against speed level.

4.1 Frame taxonomy

The distinct waveform/frame types. The CR and the connect-response are a single MFSK PRNG-tone family fully specified in §4.2; the connected-ack is a two-tone-per-symbol burst on the same symbol grid (§4.2C); the link-setup and payload overs are the OFDM DATA waveform (`01 §2300`, `03`).

Frame type (label)PurposeWaveformKeyed byDirection
connect-request (CR)session establishment; elicits PENDING/answerMFSK, 10 preamble + 31 payload tones, ~1.74 scalled (destination) callsigninitiator→responder
connect-responseresponder accepts the CRMFSK, 8 preamble + 15 payload tones, ~1.005 scalled callsignresponder→initiator
link-setup (caller-ID)initiator sends the caller callsign + session paramsBW2300 rec3 wideband OFDM — the ordinary DATA waveform: 371 data emission columns of 512 samples, one-hot over a 16-bin span, turbo-coded (§4.2A). Reported as BITRATE (4) 175 bps TXcaller callsigninitiator→responder
connected-ackresponder confirms link uptwo tones per symbol, 4 preamble + 7 state symbols, 11 symbols / 0.479 s (§4.2C)nothing — carries no callsignresponder→initiator
DATA overpayload transport (ARQ ISS→IRS)the level's family — index, tone or OFDM (`01 §1.0`) — one or two frames per over—ISS→IRS
ACK overstop-and-wait ACK (ARQ IRS→ISS)short CONTROL burst (~0.34–0.68 s)—IRS→ISS
keepaliveidle link maintenance (~10–12 s)short + ~1.4 s burst pair—both
turn releasethe holder hands the channel back; the peer answers by transmittingMFSK, 2 preamble + 15 payload tones, 17 symbols / ~0.725 scalled callsignboth
disconnectends the session; keyed once, unansweredMFSK, 32 symbols / ~1.365 scalled callsignthe closing station

4.2 MFSK handshake-tone generators (CR / connect-response)

The callsign-keyed handshake bursts share one waveform family and one payload-tone generator, differing only by three scalars: (preamble, N_payload, SEED_OFF, PREADV). A builder can synthesize each burst, and a recognizer can accept or reject one, from the callsign alone. All are keyed to the called/destination callsign only and are caller-invariant; the caller's own callsign rides the link-setup burst (§4.2A), which is an ordinary BW2300 rec3 wideband OFDM burst, not one of these bursts.

The connected-ack is not a member of this family — see §4.2C.

4.2.1 Shared MFSK burst waveform (physical)

AttributeValue
Modulationsingle-tone-per-symbol MFSK; each symbol is one −j tone = sin(2π·carrier·n/2048)
Tone → frequencyf = carrier · 48000/2048 Hz (the emitted 2048-pt @48 kHz FFT bin equals the tone/carrier index); LS-fit f = 23.4361·carrier + 0.15, residual ≤ 3 Hz
Sample rate48000 Hz
Per-symbol advance2048 samples @48 kHz, symbol start to symbol start (§4.4.1a). It is exactly 48000/23.4375, the reciprocal of the tone spacing, so the tones are orthogonal over one symbol and the burst carries no clock of its own
Inter-symbol shapingA raised-cosine WOLA cross-fade of length N: a symbol spans advance + N samples and adjacent symbols overlap by N. Rise w[i]=(cos((i−0.5)π/32+π)+1)/2, fall w[i]=(cos((i−0.5)π/32)+1)/2, i=0..31; flat 1.0 between. This document does not fix N; N = 32 is the default here, giving a 2080-sample symbol span. Whole-burst correlation is 0.9926 / 0.9932 / 0.9935 for N = 32 / 16 / 0, i.e. indistinguishable (§4.4.1a). Any N leaves the advance and the tone sequence unchanged, so a receiver never depends on it
Bandwidth modeBW2300 (the 31/15 payload tone values are the BW2300 tone set; BW2750/BW500 keep the tone counts and change the values)
Retry cadenceburst retried ≈ every 2 s until answered

4.2.2 Per-burst structure

BurstFixed preamble tones (callsign-independent)Payload tones NTotalDuration(SEED_OFF, PREADV)
connect-request (CR)[74,68,70,60,60,77,50,76,78,74] (10)3141~1.74 s(50, 1)
connect-response[62,67,55,66,59,72,68,55] (8)1523~1.005 s(289, 511)
connect-request, BW500same 10 as the CR3141~1.74 s(57, 1)
connect-response, BW500same 8 as the response1523~1.005 s(289, 721)
connect-request, BW2750same 10 as the CR3141~1.74 s(58, 1) — on the BW2300 alphabet
connect-response, BW2750same 8 as the response1523~1.005 s(579, 91)

The BW500 handshake is the same waveform on a narrower alphabet. Same preambles, same symbol grid, same generator; the payload tones come from §4.2.3 with base 50 instead of 29, `P ∈ {0,1}` instead of `{0..4}`, and no parity term — 14 carriers on the even bins 50..76 (1171.9–1781.3 Hz) against BW2300's 70 carriers on 23.4 Hz spacing over 727–2273 Hz. No BW500 handshake burst can emit an odd carrier. A responder armed at BW500 does not answer the BW2300 request, and the reverse.

Measured on five real VARA HF 4.9.0 BW500 connects (called W1AW, N0DX, W2XY, KC2OUR). Each burst's payload pins one 24-bit generator state out of 2²⁴ and regenerates it 31/31 and 15/15; because the seeding map is many-to-one, the descriptors above are the intersection over the four callsigns — a singleton in both cases, where any single callsign leaves 9–18 candidate pairs. A real VARA raised PENDING on the synthesised BW500 request and declined one naming a different station.

The BW2750 request is the BW2300 waveform in a state of its own. Same preamble, same 70-carrier alphabet, SEED_OFF 58 where BW2300's is 50 and BW500's 57 — so a station listening at any of the three reads it, and the state behind the preamble is what names the bandwidth asked for. A responder armed at BW2750 answers the BW2300 request as well and brings that session up at 2300. The response is already on BW2750's own alphabet: §4.2.3 with base 22 instead of 29 and `P ∈ {0..5}` instead of `{0..4}`, parity kept — 84 carriers on bins 22..105 (515.6–2460.9 Hz). Every BW2750 carrier is a BW2300 carrier seven bins up or down, up exactly when Int(Rnd·6) exceeds Int(Rnd·5) on the same draw. Measured on the one BW2750 loopback (AAAA1 → BBBB2, 2026-07-21), both cables, each burst pinning one 2²⁴ state under its own alphabet and none under the other; the BW2300 session the same modems keyed 50 s later on the same tape is the control. One session and one callsign pair.

The post-connect session bursts of `05 §5.3.3` are further members with their own (SEED_OFF, PREADV). The connected-ack is not a member at all — §4.2C.

4.2.3 Payload-tone generator (closed form)

Keyed to the destination/called callsign (ASCII, uppercase, incl. any SSID chars). All arithmetic is exact integer. Rnd() is the public Visual Basic 6 runtime PRNG.

# --- public VB6 Rnd LCG (msvbvm60), period 2^24 ---
LCG(s)      = (s · 0x43FD43FD + 0xC39EC3) & 0xFFFFFF          # Rnd value = s / 2^24
# --- public VB6 Randomize(seed) seeding map ---
hi32(x)     = high 32 bits of the little-endian IEEE-754 double of x
fold(hi)    = (((hi & 0xFFFF) << 8) ^ ((hi >> 8) & 0xFFFF00)) & 0xFFFFFF
start(seed) = (fold(hi32(seed)) & 0xFFFF00) | 0x86           # 24-bit LCG start state

# --- CRC & callsign hash ---
crc  = CRC-16/GENIBUS(callsign_ascii)          # poly 0x1021, init/xorout 0xFFFF, no reflection (spec/03 §3.4)
seed = (crc + SEED_OFF) & 0x7FFF
mult = G(callsign) + ((crc + 50) >> 15)        # NB: the "+50" carry is fixed for ALL three bursts

# --- seed the tone stream (PREADV pre-advance draws) ---
s = start(seed)
repeat (mult + PREADV) times:  s = LCG(s)

# --- per payload column k = 0 .. N-1 ---
for k in 0..N-1:
    s = LCG(s);  P = floor( (s / 2^24) · 5 )    # coarse tone,  P ∈ {0..4}
    s = LCG(s);  D = floor( (s / 2^24) · 7 )    # dither,       D ∈ {0..6}
    parity = 1 if k is even else 0
    bin[k] = (29 + parity + 14·P + 2·D) & 0xFF

At BW500 the same loop runs with P = floor((s / 2^24) · 2), no parity term, and base 50 — bin[k] = (50 + 14·P + 2·D) & 0xFF — giving the 14-carrier alphabet of §4.2.2. At BW2750 it runs with P = floor((s / 2^24) · 6) and base 22 — bin[k] = (22 + parity + 14·P + 2·D) & 0xFF, 84 carriers. Everything above it, the LCG, the seeding map, the +50 carry and G, is unchanged at both.

G is the GF(2)-linear callsign hash (only the last three characters matter; earlier characters affect mult only through the CRC carry):

G(cs)      = 254 XOR T[-3](cs[-3]) XOR T[-2](cs[-2]) XOR T[-1](cs[-1])   # skip offsets past the string start
T[off](ch) = XOR over set bits i (i=0..5) of (ord(ch) XOR 0x41) of GBASIS[off][i]
GBASIS[-3] = [110, 220, 440, 338, 134, 310]     # bit index 0..5; bits 6..7 unused for A–Z/0–9
GBASIS[-2] = [102, 204, 408, 274,   6,  22]
GBASIS[-1] = [ 32,  64, 128, 258,  36, 216]

The full burst carrier sequence is preamble ++ [bin[0..N-1]], rendered by §4.2.1.

Callsign-string / SSID rule (input to `CRC-16/GENIBUS` and `G`). The key fed to both the CRC and the G hash is the entire dialed callsign string, ASCII, uppercase, byte-for-byte — including embedded digits and any trailing SSID character(s). There is no stripping, no base-36 recoding, and no separator handling in this MFSK-tone path (the base-36 6-bit packing seen in the OFDM *connect frame* is a different representation and does not apply here). Consequences:

4.2.4 Recognizer

An initiator that dialed <called> regenerates the expected payload bins (same closed form) and matches them against the demodulated response to confirm step 2. The match is exact: the correct callsign reproduces all 15 response bins (and all 31 CR bins), while a different callsign agrees on at most 2 of the 15, so an exact-match test admits no false accepts. The alignment at which the payload bins are read is the one the burst's fixed preamble locks.

4.2.4a Known-answer test (KAT) vectors — payload tone bins

Concrete numeric outputs of the §4.2.3 generator, for cross-checking any independent implementation. Each row is the payload tone-bin sequence (the callsign-independent preamble of §4.2.2 is *prepended* to form the full carrier sequence; preambles are CR [74,68,70,60,60,77,50,76,78,74] and connect-response [62,67,55,66,59,72,68,55]). Values are FFT-bin indices (tone→frequency f = carrier·48000/2048).

callsigncrc16/genibusseed15multCR payload (31)
K7ABC0x57342237420[36,53,86,69,52,85,76,61,98,49,64,71,68,73,52,61,74,85,90,45,46,89,44,37,82,83,36,61,38,43,32]
W2XYZ0xE56326005279[44,33,64,49,62,33,70,53,72,35,94,89,80,77,60,89,88,81,40,73,78,61,72,95,78,77,70,61,78,93,78]
BBBB20x369E14032122[44,97,68,93,50,63,66,97,36,73,78,71,52,95,80,71,62,41,46,87,34,47,90,67,40,89,94,89,32,91,44]
AAAA10x7FF0343[80,85,90,65,88,43,78,69,52,85,90,39,94,73,98,71,68,59,62,95,78,77,78,43,94,65,72,41,56,81,58]
callsignconnect-response payload (15)
K7ABC[80,71,84,77,70,33,56,79,56,95,98,59,50,29,76]
W2XYZ[46,67,88,83,54,69,58,47,30,83,40,29,84,75,32]
BBBB2[36,43,40,63,92,63,70,59,80,59,58,29,74,81,58]
CCCC3[66,53,58,53,84,97,52,81,90,35,76,69,44,77,76]

Full carrier sequence (preamble ++ payload) for one anchor:

The link-setup frame rides the ordinary BW2300 rec3 wideband OFDM burst — the same waveform, coding chain and 92-byte container as a DATA over. There is no separate narrowband burst. The burst is caller-dependent: it is where the caller identity travels. In a BW2300 session the initiator's first wideband burst runs 4.406 s and decodes through the rec3 DATA chain to a CRC-clean 92-byte frame whose first four bytes are 04 10 41 70 — the 6-bit packed caller callsign AAAA1.

rec3 is one-hot: one lit bin per emission column out of a 16-bin span (first_bin=9, span=16, bpc=4). A view that does not span all 65 carriers sees one lit bin per column and reads as narrowband single-tone 16-FSK with 1024-sample symbols; the burst is in fact wideband, its symbols are 512 samples, and its 371 data emission columns are the data columns of a BW2300 base burst (395 total = 371 data + 24 reference, `01 §2300`) carrying 371 × 4 = 1484 coded bits exactly. Every initiator transmission occupies 735–2350 Hz; no narrowband emission appears anywhere in a session.

Frame recovery chain (RX) — identical to the DATA path (`03 §3.6.4`):

371 data emission columns → one-hot bin index → Gray (4 bits/column, MSB-first) → 1484 on-air bits
   → CHANNEL de-interleave:  coded[π[k]] = onair[k], π = interleave_stage1 col-3 [:1484]
   → rate-1/2 (13,15) turbo log-MAP decode  (N = 736 info bits; coded = 2N+12 = 1484;
        internal turbo interleaver = interleave_stage2 col-3 [:736], native — NOT pruned)
   → de-whiten:  info_bits XOR the BW2300 whitener PN  (MSB-first)  → 92-byte frame
   → CRC-16/GENIBUS over body[0:90] == frame[90:92] (big-endian)  → caller frame

The inner code is the same (13,15) turbo family as the OFDM DATA path (`03 §3.6.4`), here at rate 1/2 with N = 736 (vs N = 368 for BW500 L4). It is selected by the per-segment FEC descriptor, not a distinct encoder.

Interleavers. Between turbo encode and the σ symbol map VARA applies a channel interleave onair[k] = coded[π[k]], π = interleave_stage1 column 3, first 1484 entries (a clean 0..1483 bijection). The turbo encoder's internal N=736 interleaver is the native `interleave_stage2` column 3 (perm[i] = IL2[3+17·i][:736]). Full details, the N-parametric generation rule, and the end-to-end round trip through the complete σ⁻¹→de-interleave→turbo-decode→CRC receiver are in `03 §3.5.5`.

Frame byte layout (90-byte body + 2-byte CRC). The AAAA1 frame in full:

off  0.. 3   04 10 41 70   caller BASE callsign, 6-bit packed (SSID excluded)
off  4       00            constant
off  5       00            caller SSID as an integer, 0 if bare (W9SSJ-10 → 0x0a, W9SSJ-5 → 0x05)
off  6       00            constant
off  7       80            constant
off  8       14            constant
off  9       7f            high byte of CRC-16/GENIBUS(full callsign ASCII incl. "-SSID")  (0x7ff0 for AAAA1)
off 10..87   00 × 78       zero
off 88       04            constant
off 89       82            LIVE — varies per burst (block/trailer byte)
off 90..91   53 87         CRC-16/GENIBUS(body[0:90]), big-endian

The tail is not optional padding: CRC-16/GENIBUS over a body with bytes 10..89 all zero is 0x2e89, and with 88=04, 89=00 it is 0xe24d. Only 88=04, 89=0x82 reproduces the frame's 0x5387, and byte 89 is the unique solution over all 256 values.

The same 14 <crc_hi> … 04 <block> tail appears in a short DATA over: an over carrying 67 payload bytes is followed by 14 7f, 19 zero bytes, 04 82. So bytes 8..89 are a structured, zero-filled trailer that VARA regenerates whenever the payload does not fill the container — not an inert unused region, and clean zeros rather than stale buffer content.

At BW500 the same body fills one level-4 frame instead (`03 §3.6.4`): 44 body bytes + CRC-16/GENIBUS(body[0:44]) big-endian, 46 bytes, on a 403-column / 4.31 s burst. Every offset above keeps its position — from the front for bytes 0..9, from the back for 04 82 — and only the zero run shortens, 78 bytes to 32:

off  0.. 9   as above (packed base call, SSID at 5, 80 14, callsign CRC high byte)
off 10..41   00 × 32       zero
off 42..43   04 82         constant, then the block/trailer byte
off 44..45                 CRC-16/GENIBUS(body[0:44]), big-endian

Measured on five real VARA HF 4.9.0 BW500 connects (W9SSJ → 5e44d328 0000 00 80 14 f6 … 04 82 1dee; also K5ABC, KB1A, W9SSJ-10, VE7QRP), each regenerated byte-exact. The SSID rule is unchanged across the two bandwidths: W9SSJ-10 gives byte 5 = 0x0a and byte 9 = 0x37 at BW500 exactly as at BW2300. A real VARA armed at BW500 answered the synthesised frame with CONNECTED <caller> <called> 500, including for a caller appearing in no recording.

A high speed capable caller sets two bytes differently, at every bandwidth and for every callsign: body[7] = 0x81 (bit 0 set) where a free build keys 0x80, and the ask byte body[-2] = 06 where a free build asks at most 04; the grade byte stays 0x82 and the CRC follows. Rebuilt with those two bytes, all nine high speed capable link-setups on the bench tapes (two callers, three bandwidths) reproduce bit-exact (evidence/HIGH-SPEED-MARKER.md):

N0CALL, high speed capable:  39b0c130c00000 81 1409 00…00 06 82 b694   (BW2300 and BW2750; 2cfb at BW500)
N0CALL, free build:          39b0c130c00000 80 1409 00…00 04 82 3e0b

The responder prints a high speed capability notification right after it. Which of the two bytes it reads is not separable: they change together in every frame on file. Ask 06 is the highest level a session-confirm can start the called station at (§4.2C, *Level requests*); the confirm names the actual one. Nothing else before CONNECTED differs: the connect request, the level-4 connect response and the connected-ack are the free build's, symbol for symbol. vara_frames.link_setup_frame(..., high_speed=True) builds it, and link_setup_marks reads (high_speed, ask) back.

4.2B DATA over frame layout

Later TEXT boundary controls establish an exception to equating short payload with final delivery: a coded partial frame whose low field bits are 00 remains intermediate. At BW500 its 44-byte body can hold 42 compressed bytes, a lone 14 trailer and field b4. Full 01 and terminal 10/11 forms retain the layouts below. An overlapping caller CRC equal to the field remains ambiguous; its historical interpretation is preserved.

The 92-byte container is 90 body bytes + CRC-16/GENIBUS(body[0:90]) big-endian, as for the link-setup frame, but the body is laid out differently:

FULL over  : body[0..88] = 89 payload bytes; body[89] is a per-frame FIELD
SHORT over : body[0..n-1] = n payload bytes
             body[n]      = 0x14                 trailer marker
             body[n+1]    = high byte of CRC-16/GENIBUS(own callsign ASCII)
             body[n+2..87]= 0x00
             body[88..89] = two small fields (observed 01 02 and 03 02;
                            the link-setup frame carries 04 82 here)

In a session called by W9SSJ, a 61-byte and a 43-byte short over from the gateway both carry 0x14 0xf6, where crc16_genibus(b"W9SSJ") >> 8 == 0xf6 — the trailer carries the caller's identity, not the gateway's, so it identifies the session rather than the sender of the frame. A 90-byte over carries payload in every body byte including 88/89, so those two positions are trailer fields only when a trailer is present. This is the 14 <crc_hi> … 04 <block> tail of §4.2A with the <crc_hi> field identified.

At every level the byte ahead of the short frame's 82 names the host level its sender will read the peer's NEXT delivery at, and a delivery keyed at any other level is not heard. On the high speed capable bench tapes it read 06 (BW2300, BW2750) or 07 (BW500) because that is where the receiving station's ladder stood; on the 2026-09-26 bench at 12:42 UTC, B keyed each delivery at the 6, 4 or 5 A's preceding close named, and KB8AY's closes on air (04 82, 05 82, 03 42) each read our next over only at the level they named. A full frame's per-frame field is its control byte (`03 §3.7`). The continue frames then step the level within that delivery (`06 §6`).

The layout is the level's, not the bandwidth's. The container is always the speed level's own frame and the body is that frame less its CRC, so one law covers every level of every bandwidth: payload from offset 0, the per-frame field in the last body byte, and the trailer laid out from the END of the body when the over is short.

bandwidthlevelbodypayloadfieldtrailer at
BW2300rec3 / BITRATE (4)9089body[89]body[88..89]
BW2300rec2 / BITRATE (3)4847body[47]body[46..47]
BW500L4 / BITRATE (4)4443body[43]body[42..43]
BW500rec2 / BITRATE (3)3534body[34]body[33..34]

Measured on a stock VARA HF 4.9.0 BW500 pair, W9SSJ calling W1AW, 2026-08-30, one virtual cable per direction: the responder was handed 200 bytes and delivered them in five overs, four full level-4 bodies of 43 payload bytes apiece — per-frame fields 95, 91, 8d, 81 — and a closing record-2 body of 28 payload bytes ending 14 f6 00 00 00 04 82. 0xf6 is again the CALLER W9SSJ, keyed by the responder. The delivery steps its host 43/86/129/172/200 and every byte is 00–c7 in order, so nothing is inferred about where a payload boundary fell.

The over that closes a delivery drops a record, and the field byte announces it. Eight whole deliveries a stock 4.9.0 keyed on its own cable at BW2300 — six sessions, both stations, both directions, the record read off the waveform by the reference-column score and each over then decoded at it — pair the closing over's record with the per-frame field of the full over in front:

field on the last full overcloses atdeliveries
0x81record 2 / BITRATE (3)6
0x89base level / BITRATE (4)2
0x81, keyed to this stationbase level / BITRATE (4)3

The score is never close — 24 of 24 reference columns at the over's own record and 7 or fewer at the other, against 9 of 24 for noise — and no delivery mixes the two. The body is the same at either record: an empty close is 14 f6 00 … 00 04 82 laid out over 90 bytes and over 48, so the record is the only thing that moves. A delivery that fits one short over closes at the base level, with no full over in front of it to announce anything. The BW500 delivery above is the same rule at that bandwidth: four level-4 bodies behind 95 91 8d 81, then the record-2 close. The pair 0x81 followed by a base-level close is one no recording holds, and a stock responder handed it answers with the continue burst and goes on waiting for a message that has already ended — 93 bytes as one full over and a 4-byte close read 89 of 93 with the close at the base level and 93 of 93 at record 2, and 182 and 534 read byte-exact the same way.

The third row is the limit of the rule. Three deliveries a stock responder keyed to this station on 2026-09-03 — the same 93-byte reply in three sessions — carried 0x81 on the full over and closed at the base level, and our receiver read all three 93 of 93. So the byte is not the announcement on its own, and what every delivery on tape agrees on is narrower: a receiver reads the close at whichever record it was keyed, and a close this station keys at record 2 behind 0x81 is read where a base-level one behind the same byte was not.

Implementation note — kestrel diverges here. kestrel/arq/phy.py reserves body[0] for its own control marker and therefore offers 89 payload bytes per over. VARA uses all 90 and puts payload at offset 0. This is not what VARA specifies; a kestrel DATA over will not parse at a real gateway until the marker is dropped and the short-over trailer above is emitted instead.

Receiver behaviour of the zero region. The 78-byte zero region is not content the responder checks. Varying the frame and watching the responder:

body bytes 10..87responder
unmodified (control)CONNECTED
all 78 bytes randomCONNECTED
8-byte magic, 16/20/24/32-byte blocks, 0x00–0xff singlesCONNECTED
any single byte equal to `0x14`PENDING, then nothing
byte 88 04 → 05CONNECTED
byte 8 14 → 15PENDING, then nothing

So the 78-byte region is fully writable — 624 bits — with one constraint: the byte value `0x14` must not appear in it. 0x14 is the marker that opens the trailer structure at offset 8 (and reappears at the payload end of a short DATA over), and a stray one in the region mis-frames the parser. Neighbouring values 0x13/0x15 are accepted, as are 0x04, 0x7f, 0x80, 0x28, 0xff — only 0x14 rejects. Rejection is loud and immediate: the responder answers the connect-request and then declines, never completing.

FieldValue
Caller callsign6-bit packed, MSB-first, from bit 0: A–Z → 1–26, 0–9 → 27–36, 0 = terminator (e.g. AAAA1 = 1,1,1,1,28). BASE call only — the SSID is not packed here
Caller SSIDbody[5], plain integer (0 if bare). The 6-bit field carries the base call unchanged; byte 9 is the CRC of the *full* CALL-SSID string (hyphen included). KAT: W9SSJ-10 → byte5 0x0a, byte9 0x37; W9SSJ-5 → byte5 0x05, byte9 0x43
Session paramsremaining body bytes (bandwidth / session config), zero in the frames given here
TrailerCRC-16/GENIBUS(body[0:90]), 2 bytes, big-endian, init=0xFFFF xorout=0xFFFF
Whitenerthe additive BW2300 whitener PN, XORed over the 736 info bits before turbo (MSB-first bit order)
Frame CRC (KAT)AAAA1 → 0x5387, K7ABC → 0x9aca, W2XYZ → 0x9de4 (the frames are caller-dependent)

4.2C Connected-ack (step 5) — two tones per symbol, no callsign

The burst a responder keys at the instant it emits CONNECTED <caller> <called> <bw>. It rides the §4.2.1 symbol grid — 2048-sample advance, 48 kHz, the same 23.4375 Hz carrier lattice — and nothing else about it belongs to §4.2: every symbol lights two equal-amplitude carriers, it is 11 symbols long, and its four-symbol preamble depends on no callsign. The seven symbols behind it are keyed to the caller (see *Answer tails* below).

AttributeValue
Modulationtwo tones per symbol, equal amplitude; per-symbol 2048-pt FFT shows two peaks of ratio 1.00 each with its own ±1-bin Hann skirt and nothing else
Length11 symbols = 22560 samples = 0.479 s of audio; PTT window 0.446–0.492 s
Fixed preamblethe first four symbols, invariant: {64,67} {56,74} {64,69} {68,78} (1500/1570.3, 1312.5/1734.4, 1500/1617.2, 1593.8/1828.1 Hz)
Remaining 7 symbolstwo tones each, drawn from the §4.2.3 generator with two draws per carrier (see *Answer tails* below)
Bandwidth dependencenone in the preamble. BW500, BW2300 and BW2750 all open on the same four pairs. Only the state symbols differ, and at BW500 they stay inside 52..74 where the wide modes spread 31..98
Callsign dependencenone in the preamble; the tail is keyed to the caller

Whole frames (tone pairs, (lo,hi)), as read off tape:

framesymbols 0..3 (preamble)symbols 4..10 (state)
BW2300, loopback(64,67) (56,74) (64,69) (68,78)(52,86) (83,87) (46,82) (59,77) (56,58) (69,75) (52,66)
BW2300, off airsame(34,80) (83,97) (50,66) (59,95) (64,96) (59,77) (56,72)
BW2750, loopbacksame(45,93) (76,94) (39,75) (52,84) (49,51) (54,76) (59,75) — keyed twice, at the link-setup and at the session-confirm
BW2750, loopback, answering the first keepalivesame(31,49) (44,86) (45,57) (54,102) (37,51) (66,98) (69,83)
BW2750, off airsame(41,87) (35,90) (43,73) (44,66) (60,71) (66,84) (63,65)
BW500, loopbacksame(58,64) (52,72) (54,74) (56,68) (58,72) (54,68) (58,68)

The off-air rows are read through a fading HF path and their state symbols are the least trustworthy part of this table; the preamble is what every path agrees on.

At BW500 the state symbols are the link's, not the bandwidth's. Two stock 4.9.0 BW500 sessions with different callers, every burst attributed by which one-way cable holds it — 71 keyings, and re-synthesised from the symbols below each correlates 0.990–0.999 with the audio it was read from:

linkwho keys itoccasionsymbols 4..10 (state)
AAAA1 → BBBB2, 2026-07-13responderthe link-setup, the confirm, the LAST data over(58,64) (52,72) (54,74) (56,68) (58,72) (54,68) (58,68)
respondereach idle keepalive, and the disconnect request(58,72) (56,76) (54,68) (56,70) (58,68) (58,70) (52,68)
responderthe initiator's turn-request — the grant, and nothing else in 478 s(56,68) (60,68) (50,72) (54,68) (60,76) (60,68) (62,76)
W9SSJ → W1AW, 2026-08-30responderthe link-setup(54,72) (56,70) (56,76) (54,66) (58,72) (54,76) (50,72)
callerthe responder's LAST data over(52,74) (56,76) (52,64) (56,68) (56,74) (58,68) (60,68)
callerthe idle cadence, ×8 at 12.06 s(58,76) (58,70) (52,76) (50,68) (54,64) (52,74) (54,66)

The two responders are in the same role answering the same occasion and share one symbol of seven, so the state field is a function of the link and the tails of one caller do not serve another — the same key the BW2300 burst was shown to carry across eight sessions. Role is real as well: a caller and a responder of one link share nothing.

The intermediate over draws a shorter two-tone burst. Only the LAST over of a delivery is answered with the 11-symbol frame; every over with another behind it draws an 8-symbol one, 0.341 s, two tones throughout, whose FIRST symbol is the 11-symbol burst's own opening pair and whose remaining seven are on the bandwidth's carriers:

linkwho keys itcopiessymbols
AAAA1 → BBBB2, 2026-07-13responder50, bit-identical(64,67) (60,64) (52,68) (56,64) (58,70) (54,68) (60,74) (50,70)
W9SSJ → W1AW, 2026-08-30caller3(64,67) (50,66) (60,64) (50,68) (58,76) (52,76) (58,76) (60,68)
caller1, the over before the delivery's last(64,67) (62,76) (58,76) (50,68) (50,72) (60,74) (50,68) (52,76)

Both 0830 forms are frames of one stream: the first is frame 0, the second frame 5, a request one level down (*Level requests* below).

Answer tails. Every two-tone burst — this one, the 8-symbol continue and the 8-symbol NAK — is a fixed opening (this preamble, or (64,67) alone) and seven symbols off the §4.2.3 stream: seeded from (crc + SEED_OFF) & 0x7FFF, advanced by G(cs) + ((crc + 50) >> 15) + 1, then per symbol k

Pa = Int(Rnd*n_p): Do: Pb = Int(Rnd*n_p): Loop While Pb = Pa
Da = Int(Rnd*7): Db = Int(Rnd*7)
tones = base + parity(k, 1) + 14*P + 2*D   for (Pa,Da) and (Pb,Db)

on the bandwidth's alphabet. Frames follow one another on one stream, so a burst is a callsign, a SEED_OFF and a frame index:

burstkeySEED_OFF (caller / responder keys it)frame
11-symbol control, answering the link-setup, the confirm, the last overcaller852 / 10800
11-symbol control, answering a keepalive (poll)caller852 / 10801
8-symbol continue, one per level stepcalled62 / 2900, 6, 4, 7, 8, 5
8-symbol NAKcalled62 / 2901
11-symbol turn grant, answering a turn-request or, unasked, the caller's answer to the last over of a deliverycalled29013

Every row of the tables above falls out of it, with the AAAA1 link's turn grant at frame 13 of BBBB2's 290 stream [vara_frames.answer_tail]. At BW2750 the two-tone answers sit on the bandwidth's own band (bins 22–105); read on the BW2300 band, most of them lose one to four symbols.

Level requests. The frame index of the 8-symbol answer is a step code: it names the sender's next level relative to the one just read, on every delivered frame at every level and bandwidth. The sender keys exactly the level asked — 428 of 428 delivered full frames on the high speed capable bench tapes, with no misfit — and all 707 IRS answer keyings there decode to one frame of one stream with the generator above (evidence/ACK-LEVEL-REQUEST.md):

answer framesender's next framen (8-symbol + 32-symbol)
0same level (a single frame or, at the top, a double one)121 + 9
6+186 + 14
4+230 + 9
7+320 + 15
8+458 + 8
5−1 (the frame was delivered)58 + 0
1NAK: not delivered; the sender falls 1–4 levels by its own rule64 + 0

Frames 2, 3 and 9 never occur, so no −2 or −3 request is on file; a fall of more than one only follows a NAK. The step is in host levels: at BW2300, 12 + frame 4 is

  1. Speed limited BW500 pairs use the same codes below level 4. The 32-symbol

single-tone session frame (SEED_OFF 60, PREADV 1 + 62·i) carries the same code as the answer to a callee ISS's first data frame. The caller's 16-symbol session-confirm (SEED_OFF 1551, PREADV 1 + 30·i) sets the callee's first level:

confirm framePREADVcallee's first data leveln (500 / 2300 / 2750)
391 (SESSION_CONFIRM)41 / 7 / 3
412158 / 6 / 3
515168 / 4 / 17

The 11-symbol control burst answers the last delivered frame of a delivery and the end-of-delivery polls, and carries no step; the next delivery starts from the confirm or the first answer. The policy behind the codes is in `06 §6.2`.

Timing. The responder's PTT window opens on the CONNECTED line and closes 0.446–0.492 s later, and the burst inside it is this one. Off air the same burst appears 0.2–0.4 s after the initiator's link-setup over ends, and nowhere else.

RX use. What identifies the ack is the four preamble pairs — eight carriers — plus the fact that it arrived in the turnaround window that answers the receiver's own link-setup. The test is specific: a genuine ack matches over 46–63 consecutive 32-sample alignments, while real off-air HF holding no ack produces no preamble match at any alignment.

The responder also keys this same 11-symbol frame later in the session — answering the initiator's step-6 confirm and its per-over responses — with the same preamble and different state symbols. So the preamble identifies the frame, not the step; the step comes from where in the exchange it arrives.

4.3 Block sizing vs speed level

Net host bytes cleared per ACKed block at 500 Hz, as reported by the host BUFFER ledger, on a free build. Levels 1–2 are not tabulated here.

Speed levelNet host bytes / blockBlocks / over
3 (61 bps)341–2 (grows on success)
4 (88 bps, free cap)431–2 (over 4.31 s → 8.51 s, ×1.94)

Above level 4 a link must be high speed capable: the caller's link-setup carries body[7] = 0x81 and ask 06 (§4.2A), and every level to the bandwidth's top (13 / 16 / 17 at BW500 / BW2300 / BW2750) is then open. A frame there carries its level's frame bytes less 3 — the control byte and the CRC — and the top level of each bandwidth also keys two frames an over; per-level sizes are `06 §6.1c`.

4.4 Provisional facts, cautions & open questions

The implementer-ready facts are in §4.1–§4.3 and §4.2A. This section carries what this document leaves unspecified, and the readings of the waveform that mislead.

4.4.0 500 Hz DATA byte layout

The link-setup is specified, at §4.2A: a 44-byte body plus the big-endian CRC-16/GENIBUS trailer, filling one level-4 frame, and identical to the BW2300 body of §4.2A but for the length of the zero run between byte 9 and the 04 82. Five real VARA HF 4.9.0 BW500 connects (callsigns W9SSJ, K5ABC, KB1A, W9SSJ-10, VE7QRP) regenerate byte-exact including the CRC, and a real VARA answered the synthesised frame with CONNECTED <caller> <called> 500.

Payload overs are specified at §4.2B: the body is 44 bytes at level 4 and 35 at record 2, laid out by the wide bandwidth's own law, and a stock pair's 200-byte delivery reassembles from it byte for byte. What is still unspecified is the per-frame field the last body byte carries — it moves per over — and any length or sequence field inside the trailer's zero run. A session running COMPRESSION TEXT (`08`) also puts a compressed and framed transform of the host payload on the air, so two overs carrying the same payload need not be bit-identical.

An earlier revision declared the whole layout unestablished because "the 500 Hz soft symbols carry no readable constellation", citing 03 §3.5. That premise is retracted — §3.5's single-band 256-point model is superseded by §3.6, which is round-trip-validated and byte-exact on real VARA audio.

4.4.1 The CR is MFSK, not OFDM

Read through a clipped analysis window, the CR looks first like a set of 9 discrete carriers and then like a contiguous twiddle-scrambled OFDM band. It is the plain single-tone-per-symbol MFSK of §4.2: 10 fixed preamble tones [74,68,70,60,60,77,50,76,78,74] + 31 callsign-keyed payload tones, one tone every 2048 samples, BW2300. The tone→frequency map f = carrier·48000/2048 fits 93 (carrier,freq) pairs with residual ≤ 3 Hz.

4.4.1a Per-symbol advance — 2048 exactly

The advance is exactly 2048 samples, symbol start to symbol start. Three things corroborate it structurally, and each fails at 2042:

An advance of 2042 arises from taking a 2074-sample stride and subtracting an assumed cross-fade length, which no observation of the waveform constrains. Being clock-free, the advance cannot be explained away as a sound-card rate error: a receiver that mis-times it loses matched-filter output but not the tone identities, which is why a 2042 advance demodulates real bursts and stays invisible.

The cross-fade length is not specified here. Whole-burst correlation is 0.9926 / 0.9932 / 0.9935 for N = 32 / 16 / 0 — nothing distinguishes them, and nothing can, because the overlap is 0.7 ms of a 1.75 s burst. §4.2.1 carries N=32 as its default; the advance does not depend on it.

4.4.2 CR generator — properties of the callsign map

The payload law is bin[k] = (29 + parity[k] + 14·P + 2·D) & 0xFF with P a coarse tone {0..4} and D a dither {0..6}, both drawn from the public VB6 Rnd LCG (2 draws/column). The seed derives from CRC-16/GENIBUS(called) via the VB6 Randomize seeding map (fold/start in §4.2.3). Two properties:

4.4.3 Responder reaction to a CR

A station whose MYCALL matches the CR's called callsign answers the burst with the host sequence BUSY ON → PENDING → PTT ON, once per retried burst (`07`). Nothing in the CR beyond the called callsign affects this, so a CR built entirely from §4.2.3 elicits it.

4.4.4 Connect-response (step 2)

A member of the same MFSK family: 8 preamble tones [62,67,55,66,59,72,68,55] + 15 payload tones, ~1.005 s, same column law, (SEED_OFF, PREADV) = (289, 511). It is keyed to the called callsign alone — different callers dialing the same destination elicit byte-identical responses.

4.4.5 Burst classification, and the two readings that mislead

A per-symbol native-2048-point FFT separates the burst kinds by the ratio of the 2nd-strongest to the strongest carrier: ≈0.50 for single-tone MFSK, ≈0.85–0.97 for OFDM, 1.00 for the two-tone connected-ack. Burst inventory of a BW2300 connect:

#Dur2nd/1stTypeKeyed byRole
11.78 s0.51MFSKcalledCR (§4.2)
21.01 s0.51MFSKcalledconnect-response (§4.2)
34.37 s0.90OFDMcallerlink-setup / caller-identity DATA frame
40.48 s1.00two-tonenothingconnected-ack (§4.2C)

The caller frame is recovered from ordinary received audio through the rec3 DATA chain, CRC-clean, with the caller callsign 6-bit-packed at the frame start and any SSID in body[5] (§4.2A). No access to the pre-WOLA cell grid is needed for it. That grid is a sub-0.1 ms transient and the audio is WOLA-non-invertible — both true, and neither of them stands between the received audio and the frame.

4.4.7 Open items