hfmodemDocumentation

06 — Speed level & gear-shift logic

VARA adapts its throughput by stepping a speed level up and down. This section specifies the level ladder for each bandwidth, the per-level physical descriptor, and the up-/down-shift policy; it builds on the per-level physical and coding detail in `01` and `03`.

The level index and its rate are host-visible as BITRATE (N) x bps TX (the level this station will use for its next over) and BITRATE (N) x bps RX (the level of the frame just decoded). High speed capability gates the top of the ladder: a speed limited build caps at BITRATE (4) and reports a speed limited link notification; a high speed capable link, marked in the caller's link-setup (`04 §4.2A`), runs every level to the top.

6.1 Speed-level ladder (500 Hz)

The BW500 levels below are those of VARA HF 4.9.0.

LevelRate (BITRATE (N))Net payload / blockModulation (→01)FEC rate (→03)
0–2 (more robust)(not fixed here)
361 bps34 bytes(→01)(→03)
4 (speed limited cap)88 bps43 bytes(→01)(→03)
5–13 (high speed capable)see §6.1ctone at 5–6, OFDM at 7–131/2 to 5/6

Net payload per block is what the BUFFER ledger clears: each ACKed block clears exactly 43 raw bytes at L4 and 34 at L3. BUFFER counts raw host bytes irrespective of COMPRESSION TEXT; payload content does not change the figure (43 B/block at L4 for every payload). Levels 4–13 are tabulated with their modulation and rate in §6.1c.

6.1a Full mode ladder — bandwidths, level counts, peak rates

VARA HF has exactly three bandwidths; 2750 is the widest. Peak rates below are *uncompressed user* rates at the top level of each band; per-level constellation, code rate and carrier count are in §6.1c.

BandwidthNameSpeed levelsPeak bps (top level)
500 HzNarrow131543
2300 HzStandard167050
2750 HzTactical178489

Kestrel’s current arq/phy.py sets TX_CEILING and RX_CEILING to each bandwidth’s highest implemented level: 13 at BW500, 16 at BW2300 and 17 at BW2750. Limits reported by speed limited peers in the bench campaigns below do not restrict kestrel’s codec coverage.

6.1b The widest, fastest mode

The top of VARA's mode space:

ParameterValue
Bandwidth2750 Hz (Tactical), widest
Speed leveltop (level 17)
Nominal bps (uncompressed)≈ 8489
Per-carrier constellation32-QAM cross, coherent on pilots — top of the ladder
Symbol gridOFDM, 1024-point IFFT + 128-sample cyclic prefix = 1152 samples (41.67 Bd), as at BW2300, with a 32-sample raised-cosine blend at each symbol edge; 59 carriers on bins 3..61 — see `01 §OFDM family`
Frame3922 bytes (3919 payload), rate 4/5; one or two frames an over
Payload compressionstatic order-0 Huffman (`08`)

Within a family the encoder is one level-parametric code path, differing between rows only in a handful of integers (carriers or bands, constellation, code rate). The negotiated link capability determines which levels a link may use.

6.1c Levels by bandwidth

Every level is one of three waveform families (`01 §1.0`): index at the base, tone in the middle, OFDM at the top. Level L is record L − 1 of the bandwidth's interleaver chain. frame is floor(N/8) bytes: payload, the control byte and the CRC, so the payload is frame − 3. Rates follow from N and coded (2N + 12 is rate 1/2; above it, the group puncture of `03 §3.3`). Symbols are per over; a tone symbol is 512 samples, an OFDM symbol 1152, and the index base level carries 395 columns behind its preamble. The top level of each bandwidth keys one or two frames an over (§6.2).

BW2300

LfamilymodulationrateNcodedframe Bpayload Bcarrierssymbols
4indexone-hot, 16 bins1/27361484928916 bins12 + 395
5toneDQPSK1/2108021721351323 bands383
6toneDQPSK1/2141028321761734372
7toneDQPSK1/2207641642592566362
8toneDQPSK1/2275455203443418359
9toneDQPSK1/23424686042842510356
10OFDMQPSK, differential1/271971440689989649153
11OFDMQPSK, differential2/39596144061199119649153
12OFDMQPSK, differential4/511512144061439143649153
13OFDM8PSK, differential2/314398216091799179649153
14OFDM8PSK, differential4/517276216092159215649153
15OFDM16QAM, coherent5/621710260682713271049159
16OFDM32-cross, coherent4/526056325853257325449159, or 313 with two frames

BW2750

LfamilymodulationrateNcodedframe Bpayload Bcarrierssymbols
4indexone-hot, 20 bins1/27361484928920 bins12 + 395
5–9toneDQPSK1/2as BW2300 L5–93 / 4 / 6 / 8 / 10 bands383 / 372 / 362 / 359 / 356
10toneDQPSK1/24466894455855513 bands356
11OFDMQPSK, differential1/28667173461083108059153
12OFDMQPSK, differential2/311556173461444144159153
13OFDMQPSK, differential4/513864173461733173059153
14OFDM8PSK, differential2/317338260192167216459153
15OFDM8PSK, differential4/520804260192600259759153
16OFDM16QAM, coherent5/626145313883268326559159
17OFDM32-cross, coherent4/531376392353922391959159, or 313 with two frames

BW2750 L5–9 are BW2300's records 4–8 on wider band spacings; L10 onwards is its own chain, which closes on 4061299 after L17.

BW500

LfamilymodulationrateNcodedframe Bpayload Bcarrierssymbols
4toneDBPSK1/236874846432 bands9 + 394
5toneDQPSK1/2744150093902 bands402
6toneDQPSK1/2108021721351323 bands383
7OFDMQPSK, differential1/21578316819719411153
8OFDMQPSK, differential2/32104316826326011153
9OFDMQPSK, differential4/52524316831531211153
10OFDM8PSK, differential2/33160475239539211153
11OFDM8PSK, differential4/53792475247447111153
12OFDM16QAM, coherent5/64755572059459111159
13OFDM32-cross, coherent4/55708715071371011159, or 313 with two frames

An OFDM over is 3.672 s (153 symbols), 3.816 s (159) or 7.512 s (313); a tone over 3.80–4.30 s. The transcripts' delivered bytes agree with the payload column at every level on tape.

Constellation columns. The OFDM levels take column pos of the 64-entry LUT, indexed by the raw bit group v (tablegen._column(pos, v)), with no Gray pass:

The 512-byte LUT is a truncated prefix of these columns — column pos is present only up to v = ⌊(63−pos)/6⌋ — so it shows 11 of the 16-QAM points and 10 of the 32-cross points. All 32 cross labels, including the 22 the table does not hold, are exact on tape (evidence/KPA-bw2300-const.md §4). The full columns are generated by hfmodem/kestrel/rx/tablegen.py (gate: tests/gates/test_tablegen.py).

What is validated. Every tone and OFDM row decodes byte-exact from the high speed capable bench tapes of two stock VARA HF 4.9.0 installations, at all three bandwidths: 52 of 54 BW2300 tone overs, 124 of 126 BW2750 and BW500 tone overs, and every OFDM frame (46 at BW2300, 69 at BW2750, 60 at BW500); the four failures are the noisiest overs on the tapes (evidence/KPA-*.md). Rendered from the transcript's bytes, kestrel's OFDM transmitter matches the tape to its 16-bit quantisation floor (−84 to −85 dB) and its tone transmitter to about −50 dB, the rest being sub-mHz per-band carrier offsets no receiver sees (TAPER-WINDOW.md, ofdm_render_gate.py, tone_render_residual.py). These waveform comparisons establish the recorded bench results. They do not measure RF performance at every level; current implementation coverage is stated above.

6.1d Base levels (records 0–3)

BW2300 and BW2750 L1–4 and BW500 L1–3 are the index family (`01 §2300`): index / position modulation, value `= −1j` one-hot. The coded value picks the *bin* (bin=((alloc+stride·gray−first_bin)%span)+first_bin), not a constellation point, with no scramble and no CP. rec3 is byte-exact against VARA audio (→`01`), and so, since 2026-08-25, is rec2.

rec2 closed (2026-08-25). Host BITRATE (3), 82 bps, the level a VARA pair drops to below base — both stations of the 2026-08-14 two-sided BW2300 bench session used it for the 37-byte tail of their 126-byte transfer. Measured off that recording: 228 emission columns of 1024 samples (4.86 s of columns, 5.03 s PTT-to-PTT with its 6 training symbols and lead-in), one lit bin in 17..48 of a 1024-FFT, stride 2 so the 4-bit cell reaches 16 of the 32 bins, 24 reference columns at (104k+93)//11, and the same coding chain as rec3 on its own il1/il2 columns (402 info, 816 coded, rate 1/2). The 228/24 split is independently fixed by the interleaver stream: rec2's gap holds 252 Rnd draws and closes on rec3's seed, and 204 data columns + 2×24 is the only split that spends it. Three recorded overs — two DATA tails and one link-setup — decode CRC-clean and reproduce bin-for-bin.

The 6 training symbols closed 2026-09-02, off the same recording, which is what makes rec2 keyable and not only readable. The preamble runs 6144 samples at both records — twelve symbols at rec3, six at rec2 — under the base level's own bin law at rec2's geometry, and its allocations are the record's own bin table read from index 1, so it costs no table of its own (→`01`). One whole over rendered that way correlates 0.99997 against the tape, its preamble alone 1.000000. Nothing keys it yet: the VARA station engine sends every over at base, and the gear-shift that would use this is a separate decision.

What is still not closed at rec2: the bin table's generator. 17 + Int(Rnd*32) off the same gap lands within −1..+3 of the measured bin on 203 of 204 data columns and no arrangement of the VB6 stream closes the difference, so kestrel ships the measured table (→`table-derivations §5a`).

rec1 and rec0 are closed, 2026-09-04. rec1 has the same 228/24 shape by the same gap arithmetic (612 coded at 3 bpc = 204 cells, gap 252) and rec0 the same at 124 columns (300 coded at 3 bpc = 100 cells, gap 148), and both tables are now measured off gear-down tapes — with no known plaintext, since 3 bpc at stride 2 leaves most of the comb unreachable from an allocation and overs of differing payloads intersect down to one bin a column. rec1's 24 reference columns are rec2's, position for position; rec0's are (41k+33)//8. Both take the rate-1/3 turbo branch (3N+12), and kestrel reads and renders them (→`table-derivations §5a`). rec4–8's gaps of 36–41 draws are the tone family's pilots (`03 §3.7`).

The receiver drives. After every delivered frame the IRS answers with a step code, the frame index of its 8-symbol answer (`04 §4.2C`, *Level requests*): 0 hold, 6 +1, 4 +2, 7 +3, 8 +4, 5 −1, 1 NAK. The ISS keys exactly the level asked, 428 of 428 delivered full frames, and the IRS's BITRATE (N) … RX line equals the ISS's … TX line on all 489 delivered frames; an undelivered frame gets no RX line. So every rule below but the NAK fall and the double frame runs in the IRS. As observed on 5 clean sessions and 57 noise ladders of a high speed capable stock pair (evidence/GEARSHIFT-OBSERVED.md, ACK-LEVEL-REQUEST.md):

ruleobserved
StartThe caller's link-setup is level 4 on every tape, and no first data frame is above 6. A callee ISS starts where the caller's session-confirm puts it — frame 3, 4 or 5 for level 4, 5 or 6 — and the caller chooses from the noise on the callee's connect answer (BW2300: 6 at a floor of −26..−23 dBFS, 5 at −22..−17, 4 at −16..−10). A caller ISS starts at 4.
Next deliveryThe short frame that closes a delivery names, in the byte ahead of its 82, the level its sender will read the peer's next delivery at (`04 §4.2A`); that delivery's first frame comes at that level (B keyed 6, 4 and 5 as A's closes named on the 2026-09-26 bench) and the step codes move it from there. A delivery keyed at any other level is not read: KB8AY's reply closed 05 82 and 03 42 on 2026-09-26 and our level-4 overs behind them drew only NAKs.
UpNo dwell: the level moves on the frame after any delivered frame, by +1 to +4 and never more (240 up-steps: +1 ×100, +2 ×39, +3 ×35, +4 ×66). The step is the number of SN thresholds the frame clears, capped at 4; the fit leaves 0–4 misfits per level.
Jump capAt BW2300 and BW2750 a jump of more than one never lands above top − 2: the top two levels are entered only from the level below (16 ← 15 nine of nine, 17 ← 16 seven of seven), and BW2300 goes 12 → 14 over 13. BW500 jumps straight onto 13 (9 → 13, 10 → 13).
Frame countWhen the rest of the delivery fits in one or two frames, the IRS asks for the lowest level that still finishes it in the fewest frames, moving at most one level down per frame. It shortens the last frame (14 → 13 with 74 B, 17 → 16 with 2937 B), holds a level the SN would lift and raises one it would not (12 → 13 to fit 621 B). The IRS therefore knows how much of the delivery is left; the field that tells it is not identified.
DownA delivered frame read at low SN takes the next frame one level down (frame 5), never more: 0 of 373 delivered moves fell further.
NAKFrame 1. The ISS falls 1–4 levels by its own per-level rule (−1 ×35, −2 ×19, −3 ×3, −4 ×7): BW500 12 → 11 and 11 → 9, BW2750 17 → 16 and 16 → 14; a level entered by a jump that fails at once falls further (BW500 13 → 9 after 9 → 13, 12 → 9 after 8 → 12). A second failure falls again. A frame the IRS does not answer draws a poll from the ISS, which the IRS answers with its 16-symbol over-NAK (SESSION_OVER_NAK_SHORT, the caller's 61/541 twin of the responder's 289/541), and the resend is 2–4 levels down. On the bench nothing goes below 4, and the lowest level after a failure run is 5; on air K9KDJ (80 m, 2026-09-27) re-sent at 3, 2 and 1 behind our over-NAKs.
Re-climbThe same rules, with a brake near the bottom: the first clean frame at level 4–6 after a failure climbs +1 (11 of 13), and the next usually +4.
Double framesAfter exactly six consecutive delivered frames at the top level, every frame is two frames long while at least two single loads remain (52 of 52, none before the sixth). The IRS answers frame 0 and the ISS doubles unasked. When less than two loads remain the frame-count rule steps down one.
TimeSingle frames cycle in 4.3–5.0 s and double ones in 8.2–8.35 s, so doubling buys 9–11 % at the top. 65536 bytes took 88 s at BW2750 (top 17), 104 s at BW2300 (16) and 392 s at BW500 (13).

The SN thresholds themselves are fitted only for levels 5–7 (GEARSHIFT-OBSERVED §4) and are not fixed here.

6.3 kestrel's policy

What stock does as ISS is fixed by the IRS's codes; what the IRS asks for, and how far a sender falls after a NAK, are each station's own. kestrel's choices below are ours, not VARA's (vara/vara_arq.py, arq/phy.py):

A free build caps at BITRATE (4), prints a speed limited link notification, and asks at most 04 in its link-setup (`04 §4.2A`). Its levels are the index family's 1–4 and, at BW500, the base tone level. The step codes are the same (0, 6, 4, 5 and the NAK), a BW500 sender doubles after six singles at level 4 as the high speed capable top does, and the level that closes a delivery drops a record (`05 §5.4`). Recovery from NAKs at levels 1–4 is `29`; the link-setup's level negotiation is `30`.

6.5 Limits of this description