MOSAIC Alpha

Draft specification · findings

MOSAIC protocol whitepaper

Asynchronous multi-user amateur digital mode for HF and UHF

Abstract

MOSAIC is an asynchronous multi-user packet modem family. Its design goal is a shared channel in which several uncoordinated transmitters can overlap in time and frequency while a single receiver recovers every CRC-valid packet it can prove. The implementation has two channel families with three fixed modulation-and-coding profiles (MCS) in each:

Basic remains the discovery and fallback MCS. Faster MCSs shorten both preamble and payload airtime while increasing tone spacing and reducing the number of resource bins. They do not widen the occupied channel.

The receiver is deliberately conservative: it only cancels a signal after the ordinary packet decoder returns a CRC-valid payload. That CRC gate is the safety mechanism that lets the receiver subtract decoded users without turning uncertain detections into destructive interference cancellation.

This document separates measured-on-hardware evidence from software simulation. Hardware results are sourced primarily from JSON manifests under run-artifacts/; simulated results are labelled as such.

Evidence basis

Quantitative protocol constants in this paper come from:

No number in the results tables is intended as an estimate. If a result has not yet been measured, it is identified as unmeasured.

Motivation and design goals

The protocol is built for uncoordinated packet users rather than scheduled point-to-point links. The important design goals are:

  1. Asynchronous access. Transmitters do not need a common time reference.
  2. User separation by known signatures. Each candidate identity has a declared access signature; the receiver searches a catalogue of possible senders rather than accepting an unlimited blind active set.
  3. CRC-gated cancellation. A packet must pass the normal decode and CRC checks before it can be subtracted from the residual.
  4. Bounded computation. Carrier, drift, timing and cancellation searches are finite grids.
  5. Hardware realism. Strong RF energy is not treated as success; retained runs must prove burst duration and CRC validity.

The scaling goal remains larger than the present hardware evidence, but the current artifacts now include genuinely independent MOSAIC-UHF on-air results: a bladeRF x115 and a USRP B210 transmitted from separate processes while a HackRF One decoded both packets, and retained runs recovered twelve logical stations carried across those two independent RF transmit chains with both spread and randomly drawn arrivals. Earlier no-fade software scaling sweeps that appeared to show collapse were invalid: the unfaded branch in tools/many_station_scaling.py emitted double-sideband AM mirror images instead of true frequency offsets. With the analytic-signal fix, retained twelve-station no-fade seeds decode 12/12 and a corrected balanced sweep decodes 8/8, 10/10, 12/12 and 16/16. In the clean no-fade, no-AWGN conditions tested so far, multi-user interference is not the binding constraint. Fading is: at eight UHF stations, current faded simulations recover only 12/32 with 6 dB amplitude spread and 15/32 with balanced levels. The current unfaded single-seed deep sweep recovers 22/24 and 26/32, showing graceful degradation rather than collapse; those two large-count points are not outage rates.

The live station has also now completed a bidirectional keyboard QSO over the antenna path at 431.200 MHz. VE6SLP and VA6GA each delivered operator text at the remote console through the KISS live modem. This corrects the earlier bench conclusion that antenna-path CRC recovery was blocked; the retained diagnosis shows the antenna path was working and the failures were in live-modem capture, scheduling, filtering and console plumbing.

The HF profile has a different evidence status. All three HF-family MCSs have now been recovered together from a three-radio capture, but that experiment transported the HF waveform over a 431.200 MHz UHF carrier. It validates the mixed-rate waveform, profile bank and SIC path; it is not an ionospheric HF result. Shared-channel operation in a fading HF channel remains unproven.

Waveform and modulation

The transmit chain is fixed by the wire format: payload bytes are protected, encoded, interleaved, preceded by the signature-derived preamble, mapped onto the 16-of-192 tone grid, and shaped into a burst.

MOSAIC transmit chain

MOSAIC uses a deterministic 16-candidate tone selection on a 192-bin frequency resource grid. Each transmitted coded symbol carries 4 bits by selecting one of 16 local candidates. The candidate set changes each symbol interval as a function of the 32-bit access signature, an LCG state and a 32-bit mixer.

The mapping in candidate_bin_mapping() is defined only for:

Field Value Source
Candidates per symbol 16 src/mosaic_hf/waveform.py
Resource bins 192 src/mosaic_hf/waveform.py
Bins per candidate stratum 12 src/mosaic_hf/waveform.py
Access signature width 32 bits src/mosaic_hf/waveform.py

Signature-dependent 16-of-192 candidate hopping

The figure shows two access signatures over twenty symbol intervals. Each signature redraws sixteen candidate bins from the same 192-bin resource grid; a transmitted 4-bit symbol selects one candidate from that interval's set.

16-ary tone occupancy and equal-energy symbol map

The left panel is one symbol interval from candidate_bin_mapping(): sixteen candidate bins for signatures 0x2F and 0x5B, with one active tone marked. The right panel is the equal-energy 16-ary MFSK symbol map for signature 0x2F—the modem never superposes tones inside a symbol; the four coded bits index that interval's candidate list. Unit envelope at the analytic component is a waveform property, not an emissions proof.

The modem generates a unit-envelope analytic CPFSK/hopped-MFSK waveform. Tone frequencies are selected from the profile's tone grid and repeated for one symbol duration. The orthogonality condition is enforced in PhyConfig:

tone_spacing_hz * symbol_duration_s == 1

That constraint is why every MCS changes symbol duration and tone spacing together. Every interval retains 16 candidate tones. Basic uses 192 resource bins; the twice- and four-times-rate MCSs use 96 and 48 bins so all three rates remain in the same passband. The deterministic affine mapper is identical for the established 192-bin waveform and scales its number of bins per stratum for the faster MCSs.

Framing, FEC and CRC

The implemented P1 packet format is in src/mosaic_hf/framing.py.

Item Value Source
Maximum PHY payload 255 bytes MAX_PAYLOAD_BYTES
Header data 4 bytes HEADER_DATA_BYTES
Header information including CRC 5 bytes HEADER_INFO_BYTES
Header CRC CRC-8/ATM, poly 0x07, init 0 crc8_atm()
Payload CRC CRC-16/CCITT-FALSE, poly 0x1021, init 0xffff crc16_ccitt_false()
FEC terminated convolutional code, K=7 CONSTRAINT_LENGTH
Generators octal 171, 133 _GENERATORS
Tail bits 6 TAIL_BITS
Interleaver 4 rows, transmit by column interleave()

P1 frame layout

P1 bit-level fields, coding chain and CRC gate

The protected header carries magic 0xD3, version, payload length and reserved semantics, then CRC-8/ATM over those five information bytes. Header and payload regions are independently terminated with a K=7 rate-1/2 convolutional code (generators octal 171, 133), 4-row column-read interleaved, and packed four bits per 16-ary tone symbol. The decoder recovers the protected header first, uses the header length to size the payload coded block, and accepts the payload only when CRC-16/CCITT-FALSE passes. Only that full CRC-valid path may enter cancellation.

framing.py also contains an experimental M1/P4-A protected-header candidate. It is not described here as the active wire format because encode_frame() and the retained RF manifests use the P1 path.

Version-2 mixed-rate header

Mixed-rate packets use an explicitly distinct Version-2 protected header. The four data bytes are:

magic 0xD3 | version 2 | payload octets | PHY profile (high nibble) / FEC profile (low nibble)

Header coding and CRC remain the robust, independently terminated P1 rate-1/2 K=7 code and CRC-8/ATM. FEC profile zero is the existing terminated rate-1/2 K=7 payload code. Other FEC profile values are reserved and rejected. The payload remains CRC-16/CCITT-FALSE gated.

The receiver first obtains a tentative PHY profile from the profile-specific acquisition bank, then requires the protected header profile to match it. Mismatch is a hard header rejection: it cannot deliver a payload or enter SIC. Version 1 retains its zero reserved byte and remains decodable through the unchanged P1 path; no Version-1 reserved bit was reinterpreted.

Acquisition

The acquisition preamble contains 32 known symbols derived from the access signature. Symbol i uses:

mix32(signature + (i + 1) * 0x9e3779b9) & 0x0f

The reference AcquisitionConfig searches:

Field Reference default Source
Preamble symbols 32 AcquisitionConfig
CFO range -112.5 to +112.5 Hz AcquisitionConfig
Coarse CFO step 12.5 Hz AcquisitionConfig
Drift hypotheses -3, 0, +3 Hz/s AcquisitionConfig
Timing step 8 samples AcquisitionConfig
Fine CFO step 0.5 Hz AcquisitionConfig
Detection threshold 2.4 AcquisitionConfig

Retained SDR captures required a wider explicit physical search. The band profiles therefore use a -2000 to +2000 Hz acquisition range for hardware runs. MOSAIC-HF searches drift hypotheses of -10, 0 and +10 Hz/s. MOSAIC-UHF uses 5 ms symbols, a 160 ms preamble, a 200 Hz coarse CFO step, an 8 Hz fine CFO step and only a 0 Hz/s drift hypothesis; the source notes that 10 Hz/s drift would move less than 2 Hz over that preamble.

Mixed-rate profile bank

Version-2 preambles are conditioned on both access signature and four-bit PHY profile ID. The receiver runs a bounded bank only for locally enabled profiles. Each bank uses its own symbol duration, tone spacing, CFO grid and threshold. The protected header then confirms that tentative selection.

Raw correlation energies from different preamble durations are not compared. For profile p, the normalized detection statistic is:

Lambda_p(tau) = |sum_n r[tau+n] conjugate(x_p[n])|^2
                / (E_p sigma_hat^2(tau))

where E_p is template energy and sigma_hat^2 is the local noise estimate. Under the white-noise reference model this removes preamble-length bias. The receiver budgets false alarms across the complete enabled profile/timing/CFO/drift search, rather than applying an uncorrected per-cell probability to every bank. A shared coarse energy gate precedes expensive profile-specific fine searches in the realtime path. Thresholds remain empirically calibrated on coloured-noise and occupied-channel captures because the white-noise distribution is not an HF-channel guarantee.

The multi-user receiver

src/mosaic_hf/sic.py implements bounded offline catalogue discovery and successive interference cancellation (SIC). A candidate is not an assertion that a transmitter is active; it is one catalogue identity and access signature. On each residual generation, the receiver:

  1. Acquires every eligible catalogue entry on the residual.
  2. Ranks detected hypotheses by measured acquisition score.
  3. Attempts ordinary packet decoding.
  4. Marks rejected candidates by stage.
  5. Cancels at most CRC-valid packets.
  6. Repeats for a bounded number of generations or until no CRC-valid candidate remains.

MOSAIC receive and CRC-gated SIC chain

CRC-gated SIC cancellation flowchart

The cancellation ledger retains the prior residual segment, start and end samples, fitted gain and phase, and the acquisition-anchored trajectory fit. The code reports false_cancellation_count as structurally zero on the manager side because ledger entries are CRC-gated; campaigns with generated truth can separately check whether a ledger identity was inactive.

The flowchart is the safety argument in one page: the original mixture r₀ is immutable; each generation searches a finite catalogue; ordinary FEC/CRC decode must pass before a reconstruction is fitted; only then does

r_{k+1} = r_k - a_k s_k(θ_k)

update the residual, with a LIFO undo snapshot on the ledger. A failed CRC never writes the residual. This is not a joint maximum-likelihood receiver and it is not an unlimited blind active-set estimator. Its safety comes from the CRC gate and from bounded catalogue search.

Operator fast path and residual peel. The live modem (mosaic-modem) first walks each catalogue signature independently. That recovers equal-power overlapping stations far faster than a full SIC campaign and is what keeps continuous receive on the RF stream. Finding some stations must not stop the search for the rest: incomplete multi-user recovery continues with a CRC-gated residual peel (cancel_crc_valid_packet) and, when still incomplete, the full collision/SIC receiver. Payloads from each stage are merged by identity.

Gain tracking and multipath cancellation. The cancellation fit uses a piecewise-linear complex gain over a hat-function time basis when the profile enables it (PhyConfig.gain_knot_symbols; MOSAIC-HF defaults to three symbols per knot). A constant-gain fit is always computed; tracking is accepted when residual power improves by a clear margin, or when a mild BIC penalty on long extents still prefers the tracked model. MOSAIC-HF also opens a short multipath delay window (sic_max_delay_s = 3 ms) so the post-CRC canceller may fit a few integer-sample taps when a delayed path deepens the residual.

HF congested stack (simulation). On the HF profile the collision receiver additionally enables multi-basin acquisition, residual-aware header and payload soft metrics, and a known-payload add-back refine pass after the ordinary peel stalls. That combination is measured in tools/hf_fade_multiuser_harness.py (hf-congested mode). It is not an on-air HF claim: the evidence is Watterson-style software mixtures only. UHF profiles keep the historical single-tap / single-basin path so attested campaigns stay unchanged.

Bounded QRM/QRN front end. Before HF congested SIC, the receiver winsorizes only samples outside median ± eight MAD-derived standard deviations. It then searches the MOSAIC tone band for at most three full-capture coherent lines at least 24 dB above the median in-band FFT amplitude and subtracts each with a two-parameter real sine/cosine least-squares fit. A full-capture line must also have stable fitted amplitude across eight-symbol blocks. Lines that fail that test remain available to a second bounded stage. That stage accepts at most three trajectories present in at least three eight-symbol blocks, with linear drift limited to ±2 Hz/s and the same 24 dB spectral threshold. Subtraction is gated independently at one-symbol resolution so keyed-off spans remain unchanged. Hopped MOSAIC packets neither remain coherent at one tone for a full capture nor meet the tracked-line amplitude gate.

The same front end is used by the legacy HF collision path and the Version-2 adaptive HF profile bank; UHF is unchanged. Framing, FEC, header checks and payload CRC acceptance are unchanged. The retained campaign covers extreme impulses, persistent CW, a declared three-window keyed-CW pattern, and a +0.75 Hz/s linear drift. It does not establish rejection of arbitrary keyed patterns, wider or nonlinear chirps, digital interferers, or speech-like QRM.

Mathematical specification

The tone grid is orthogonal over one symbol. For tone spacing Δf and symbol duration T_s, the implemented profiles require:

Δf T_s = 1
∫_0^{T_s} exp(j 2π (m-n) Δf t) dt = 0,  m != n

MOSAIC-UHF is therefore not a separate modulation. It scales MOSAIC-HF by shortening T_s and increasing Δf together while keeping 16 candidates and 192 resource bins.

The AWGN channel helper defines SNR against the profile's reference bandwidth B_ref, not against total discrete-time Nyquist bandwidth. The implemented variance is:

σ² = P_signal / 10^(SNR_dB/10) * (f_s / 2) / B_ref

This definition is the basis for simulated SNR tables. The relationship to energy per bit is:

Eb/N0 = SNR_ref * B_ref / R_b
Eb/N0_dB = SNR_ref_dB + 10 log10(B_ref / R_b)

For MOSAIC-HF, B_ref/R_b = 2400/50 = 48, so the narrowband HF profile has 16.8 dB of processing gain relative to its 2400 Hz SNR reference bandwidth:

Eb/N0 = SNR_ref · (B_ref / R_b)
Eb/N0_dB = SNR_ref_dB + 10·log10(B_ref / R_b)
MOSAIC-HF:  B_ref = 2400 Hz, R_b = 50 bit/s  →  Eb/N0_dB = SNR_2400Hz_dB + 16.8 dB
MOSAIC-UHF: B_ref = 38400 Hz, R_b = 800 bit/s → Eb/N0_dB = SNR_38400Hz_dB + 16.8 dB

For MOSAIC-UHF, B_ref/R_b = 38400/800 = 48, so the same conversion applies. This conversion is mandatory when comparing MOSAIC results with other HF modes: -9 dB SNR in the 2400 Hz reference band is 7.8 dB Eb/N0, not an operation point near -9 dB Eb/N0. Example MOSAIC-HF conversions are:

SNR in 2400 Hz Eb/N0
-9 dB 7.8 dB
-12 dB 4.8 dB
-15 dB 1.8 dB
-18 dB -1.2 dB
-21 dB -4.2 dB
-24 dB -7.2 dB

This processing gain is the central HF trade: the 50 bit/s profile spends an SSB-width channel to buy about 17 dB of energy-per-bit margin. It is also why the two profiles can share the same matched-noise Eb/N0 cliff while occupying different bandwidths.

SIC subtracts only decoded, CRC-valid packets. At cancellation generation k:

r_{k+1} = r_k - a_k s_k(θ_k)

Here r_k is the residual, s_k(θ_k) is the reconstructed waveform at the acquired timing/CFO/drift trajectory, and a_k is the fitted complex amplitude (gain and phase). If the header or payload CRC fails, this update is not applied.

The Watterson-style fading helper applies delayed, independently faded analytic paths and normalizes path powers as a group:

y[n] = Σ_p sqrt(P_p) h_p[n] x_a[n - d_p]
h_p[n] = (1/sqrt(M)) Σ_{m=1..M} exp(j(2π f_{p,m} n/f_s + φ_{p,m}))
Σ_p P_p = 1

x_a is analytic audio, d_p is an integer sample delay, each Doppler tone f_{p,m} is drawn within the path's Doppler support, and the current reference slice does not implement fractional-delay filters.

Band profiles and mixed-rate MCS

The profile abstraction in src/mosaic_hf/profiles.py binds PHY parameters, TX shaping, direct-IQ filtering and acquisition settings so that rate-dependent stages move together.

Family / MCS ID Symbol Spacing Bins Highest tone Coded rate
HF Basic 0 80 ms 12.5 Hz 192 2687.5 Hz 50 bit/s
HF Chat 1 40 ms 25 Hz 96 2675 Hz 100 bit/s
HF Fast 2 20 ms 50 Hz 48 2650 Hz 200 bit/s
UHF Basic 0 5 ms 200 Hz 192 38,500 Hz 800 bit/s
UHF Fast 1 2.5 ms 400 Hz 96 38,300 Hz 1600 bit/s
UHF Turbo 2 1.25 ms 800 Hz 48 37,900 Hz 3200 bit/s

All profiles use 16 candidates per symbol, a 32-symbol profile-specific preamble, and the existing rate-1/2 payload FEC in the first executable slice. Therefore the table reports coded-bit rate, not application throughput. Measured throughput must include preamble, header, tail bits, CRC and access jitter.

The UHF profile is the same waveform family scaled by 16 in rate. The trade is bandwidth for time. The handoff notes report a software matched-noise sweep in which both profiles have the same cliff at about +1 dB Eb/N0. The same notes also state that, at fixed transmit power, UHF collects 12 dB more noise in its wider bandwidth. Therefore UHF is not a weaker waveform, but it does trade link margin for shorter air time.

Uncoordinated rate selection

CQ, first reply and capability recovery use Basic. Capability exchange carries the supported profile bitmap, and each link direction may choose a different MCS. Automatic selection is deliberately conservative:

allowed = local capability intersect peer capability
ceiling = min(operator ceiling, congestion ceiling, peer recommendation)
choice  = fastest allowed MCS no faster than ceiling and local link estimate

Promotion requires three consecutive CRC-valid frames above the next-profile margin threshold. A timeout, requested repeat, failed expected reply, or sharply reduced margin demotes immediately. This asymmetric dwell and hysteresis prevents rate ping-pong under fading. Broadcast or multi-recipient traffic uses the weakest selected recipient MCS unless the application splits recipients into explicit rate groups.

Stations remain asynchronous and unslotted. Access jitter scales with packet duration, and high measured occupancy suppresses nonessential background traffic. Faster MCSs reduce airtime; they do not reserve a lane or require coordination with other transmitters.

Mixed-profile SIC invariant

The candidate identity is (PHY profile ID, access signature, timing, CFO, drift). Detected candidates enter one profile-normalized queue. Header and payload decode, waveform reconstruction, trajectory fitting and reversible cancellation all use that candidate's exact profile. Every residual update remains CRC-gated:

profile bank -> Version-2 profile confirmation -> payload CRC
             -> profile-correct reconstruction -> reversible subtraction
             -> rerun all enabled profile banks

No acquisition score, FEC metric or semantic plausibility check can bypass the payload CRC. This preserves the established fail-closed SIC rule while allowing different numerologies to overlap.

Retained three-radio mixed-rate RF evidence

The final RF campaigns use a bladeRF x115 for Basic plus the middle MCS, a USRP B210 for the fastest MCS, and a HackRF One as the independent observer. Transmitters start at different times and share one passband. The UHF-family campaign is a 431.200 MHz UHF waveform result. The HF-family campaign uses the same UHF carrier because these SDRs cannot transmit on HF; it is explicitly not an HF-band propagation claim.

Family carried at 431.200 MHz CRC-valid profiles Measured RF-envelope occupancy Declared time with 2+ users Capture SHA-256
UHF Basic / Fast / Turbo 3/3 100.0% 53.4% 20d5e06f408dbb15a363cc98d1c9c859b4951771665e533bb933da06934b3cfc
HF Basic / Chat / Fast over UHF 3/3 100.0% 51.3% 35491fd01ed788c2772476d889e8e8f6aa703155e3e2cf8cd9eb1054c8ed6ae7

The occupancy measurement uses fixed 5 ms RMS blocks between the first and last CRC-valid packet extents. Its threshold is the geometric mean of the guard-interval median RMS and the active-span 95th-percentile RMS. The transmitted interval union independently declares 100% airtime occupancy. Cancellation removed 61.6%, 83.4% and 79.4% of the UHF Basic, Fast and Turbo packet-segment power respectively; the corresponding HF-family values were 58.0%, 93.9% and 81.7%.

The aggregate record is run-artifacts/adaptive-mcs/mixed-rate-rf-evidence.json; the per-run manifests are under uhf-three-radio-final-20260815/ and hf-over-uhf-three-radio-20260815/. The measured envelope, waterfall and cancellation graph is site/assets/adaptive-mcs-rf-evidence.png.

Implementation on real radios

Hardware and transports

Implemented tools and retained bench runs use:

Radio Role in retained runs Format Source
bladeRF x115 forward transmit, reverse receive SC16 Q11 for vendor CLI docs/RF-BENCH-HANDOFF.md, physical_bench.py
HackRF One forward receive, reverse transmit CS8 for hackrf_transfer docs/RF-BENCH-HANDOFF.md, physical_bench.py
USRP B210 independent UHF transmitter and UHF decoding observer in retained runs interleaved complex64 tools/usrp_io.py, run-artifacts/three-radio-uhf-2/usrp-tx.log, run-artifacts/usrp-observer-2tx-hi/usrp-rx.log

The direct-IQ path also uses CF32LE/SigMF files for retained captures and observer products.

The B210 path is intentionally a separate process like the bladeRF and HackRF vendor tools. tools/usrp_io.py runs under /usr/bin/python3, because the UHD Python bindings are installed system-wide, and exchanges interleaved complex64 sample files. Its default UHD selector is type=b200. That default is not cosmetic: UHD also exposes the bladeRF through its SoapySDR bridge, so empty device arguments can silently open the wrong radio. The tool records positive identity fields (mboard_id, mboard_serial) so a retained result proves what actually transmitted or received. In run-artifacts/three-radio-uhf-2/usrp-tx.log the opened transmitter identifies as mboard_id B210, serial 31B92DD, with TX gain 52.0 dB, 1,000,000 sample/s rate and 1.29 s transmit duration. In run-artifacts/usrp-observer-2tx-hi/usrp-rx.log, the same B210 identifies as serial 31B92DD while receiving at gain 70.0 dB and 2,000,000 sample/s.

MOSAIC-HF access through a transceiver audio path

The bench still cannot make a two-radio HF RF link directly. The handoff now records measured tuning limits: the bladeRF x115 stops at 237.5 MHz and rejects set frequency rx 14100000 with Invalid parameter; it would need an XB-200 transverter that is not on the bench. The B210 tunes down to 42 MHz, lower than its published 70 MHz figure but still above HF. The HackRF reaches HF, but it is half duplex and therefore cannot be both ends of a two-radio link. That makes a direct two-radio HF RF link physically impossible on this bench.

That does not block MOSAIC-HF, because HF digital modes normally reach the band through an SSB transceiver's audio path, not by asking a computer interface to be an HF transmitter. MOSAIC-HF is already an audio-band waveform: the profile opens a 200-2800 Hz audio band around tones from 300 Hz to 2687.5 Hz, and its 12 kHz modem sample rate is exactly one quarter of the 48 kHz rate available on ordinary sound interfaces. src/mosaic_hf/audio_path.py and mosaic-hf-audio provides that missing path: audio out to the rig's microphone input, audio back from the receiver, and optional serial RTS/DTR PTT.

Three audio-path design choices are protocol-relevant because they prevent known HF-digital failure modes:

The rate conversion itself also has a measured failure mode. The 12 kHz to 48 kHz conversion uses a polyphase filter with group delay; resampling a burst with no trailing room can truncate final symbols and make the decoder fail with stream ends before coded payload. tests/test_audio_path.py now prepares the burst exactly as it is transmitted, including the keying guard, before checking rate-conversion decode.

The retained analogue-converter result is not a software loopback. The handoff records a Focusrite Scarlett 4i4 as device 13, with a physical output-to-input path on channel 0: a 1500 Hz probe returned 46 dB SNR, while channel 1 was silent. A full MOSAIC-HF packet was sent through the real DAC, analogue wiring and real ADC with:

mosaic-hf-audio --amplitude 0.9 loopback --device 13 \
  --repeat 3 --text "VE6SLP-1 DE VA6GA-2 QSL 599 K"

All 3 of 3 attempts decoded byte-exact, with acquisition scores from 647 to 677 and received RMS around 0.009. The return is about 40 dB below the transmitted level, which is why that bench probe used amplitude 0.9. On a real SSB transceiver, drive should start at the default 0.25 and be raised only until ALC just begins to move. This result demonstrates the converter path and the modem's tolerance of it; it is not an HF on-air contact and does not measure ionospheric propagation.

Direct-radiated MOSAIC-HF waveform on a UHF SDR carrier: acquisition only

run-artifacts/hf-rf-link/offline-decode.json records a partial measured RF result for the MOSAIC-HF profile radiated directly by an SDR over the real antenna path at 431.200 MHz. This did not open an HF band; it carried the HF waveform on the already-authorized UHF bench carrier. The bladeRF x115 transmitted with txvga2 10 dB and the pinned FPGA 7bb0c7b0fb9976cd631b679352aff3383ab371dad29306452140984363711fdf at version 0.16.0; the USRP B210 received at 52 dB. One packet was transmitted. The retained offline decode is tied to capture SHA-256 3d691092ca4379de901e3683bd8e38d962a07f8f91de2c2abc12ba662ad19875 and 187,000,000 capture samples.

The result is a clean partial: the receiver acquired the burst and correctly ranked the true sender first, but no CRC-valid packet was recovered.

Candidate Acquisition score CFO Drift Rank Rejection
VE6SLP-1 1314.2 +106.25 Hz -1.0 Hz/s 1 header
VA6GA-2 158.9 +160.75 Hz +10.0 Hz/s 2 header

crc_valid_sender_ids is empty. Both candidates evaluated 963 coarse and 546 fine hypotheses. The true sender's acquisition score is about 8.3 times the other catalogue entry, so the burst was radiated, propagated, detected and attributed, but it was not decoded. An independent FFT measurement on the same capture placed the carrier at +107.8 Hz, agreeing with the receiver's +106.25 Hz estimate to within 2 Hz; that corroborates acquisition and points the fault downstream.

The leading explanation is drift, not a proven root cause. MOSAIC-HF uses 12.5 Hz tone spacing, the reciprocal of its 80 ms symbol, and the packet is about 13 s long. The estimated -1.0 Hz/s drift accumulates to about 13 Hz over the frame, more than one full tone spacing. The preamble can still correlate before that drift dominates, while later header tones can walk off their bins. The current coarse drift grid of -10, 0 and +10 Hz/s also poorly covers a true drift near -1 Hz/s.

This partial does not contradict the intended MOSAIC-HF deployment. A 12.5 Hz tone grid presumes a more stable reference than a free-running SDR local oscillator at 431 MHz. That assumption is exactly what the transceiver audio path provides: the rig performs RF translation from its own reference and the modem sees audio. The same profile decodes byte-exact through real analogue converters and has the simulated ITU-R F.1487 threshold bracket described below. MOSAIC-UHF, with 200 Hz tone spacing, continues to be the profile used for live keyboard QSOs over this UHF antenna path.

Emission window

--frequency-hz sets the local oscillator. The modem audio is placed 100,000 Hz above that LO. For --frequency-hz 431200000, the emitted occupied spectrum is:

Profile Emitted spectrum Source
MOSAIC-HF 431.300200-431.302800 MHz emission_window_hz(), docs/RF-BENCH-HANDOFF.md
MOSAIC-UHF 431.300200-431.338700 MHz emission_window_hz(), docs/RF-BENCH-HANDOFF.md

An observer tuned to 431.200 MHz is not listening to the MOSAIC tones; for the HF profile, the observer should tune the lower edge as USB audio.

FPGA pinning hazard

The bladeRF hazard is severe enough to be part of the protocol evidence model. The handoff records that libbladeRF silently auto-loaded FPGA v0.14.0 after a reconnect. That image accepted set samplerate tx 1000000 and reported the requested rate, while actually clocking the DAC 4 times too fast. The failed burst was strong but undecodable: a 17.14 s waveform radiated in 4.278 s, with 0.02 s symbol dwell instead of 0.08 s and about 20 kHz occupied bandwidth instead of about 2.4 kHz.

The fix is to load hostedx115-latest.rbf in the same bladeRF-cli process that streams samples. BladeRfHackRfConfig expects FPGA version 0.16.0 and SHA-256 7bb0c7b0fb9976cd631b679352aff3383ab371dad29306452140984363711fdf. The live modem refuses --execute without an FPGA bitstream.

This is why received signal strength alone never proves a valid transmission. Retained manifests compare measured burst duration to expected waveform duration and require CRC validity.

The B210 selector failure is the same class of bench hazard. In both cases the bench accepted a plausible command while using hardware other than the operator intended: a stale bladeRF FPGA in one case, and a UHD-opened bladeRF instead of a B210 in the other. Both are controlled by recording positive radio identity, not by trusting that a command completed.

Methodological guardrails

Two bench details now have explicit artifact support because either can make a false result look plausible:

The same section of the tool also documents the bladeRF keying issue: --bladerf-lead-seconds defaults to 1.05 s because the bladeRF CLI loads the FPGA image before it can stream. The lead compensates the otherwise late bladeRF burst when another process, such as /usr/bin/python3 tools/usrp_io.py, is scheduled by wall clock.

The tool also refuses to let one radio observe and transmit in the same run. Full-precision observers are decoded with decode_collision_iq() in src/mosaic_hf/physical_bench.py; this keeps B210 CF32 captures in their native precision instead of first quantizing them to the HackRF's 8-bit CS8 format.

tools/decode_capture.py exists for the same evidence-preservation reason. A congested decode can take longer than the capture itself; if the decode process is interrupted after the radios have finished, re-keying the transmitters would create a different channel realization. The tool decodes the retained capture offline, records the capture SHA-256 in its JSON output, and accepts --format cf32 for full-precision observers as well as CS8 HackRF recordings.

The general rule for negative results is now explicit: check the received level before attributing a failed decode to the receiver. The low-gain B210 observer run at run-artifacts/usrp-observer-2tx/ detected both candidate preambles but recovered no CRC-valid payloads; its 100 ms envelope buckets peak at 0.011240958236157894. The high-gain rerun at run-artifacts/usrp-observer-2tx-hi/ peaks at 0.0802842453122139 and recovers both packets. The decode result alone cannot distinguish an under-gained front end from a receiver limitation; the envelope profile can.

The same lesson applies to population simulations. Superseded scaling files first suggested the wrong near/far mechanism, then seemed to show congestion collapse, and a coordinated-arrival explanation was offered when hardware decoded 12/12. Those explanations are withdrawn for current scaling claims: some old artifacts used a defective unfaded generator that manufactured mirror-image interference. The fault was in the sweep tool's signal generator, not in the receiver or protocol; the receiver did not become better, the measurement harness stopped lying about the channel. The first HF multi-user harness had the same failure mode: multiplying real audio by a cosine is amplitude modulation, not a frequency shift, and leaves a mirrored image that no catalogue entry can cancel. The correct path uses the Hilbert analytic signal before applying carrier offsets, as the channel code already did. Files without the even_spacing provenance key are kept as historical records, not current performance evidence. The fifth methodological lesson is the blunt one: when hardware beats the model, suspect the model.

The live QSO adds the same lesson at system scale. Individual transmitter, receiver, decoder and console pieces passed in isolation while the assembled station failed. Progress came from measuring inside the running station: archiving real receive windows with --save-captures, timing actual decode workers, printing candidate offsets and repeating A/B tests rather than trusting one observation. Rejection taxonomy is useful, but not absolute: header rejection, payload_fec_crc and strong acquisition followed by failure can vary from run to run on a marginal link.

The first five live-station defects are now fixed or guarded by tests:

A subsequent usability pass added two measurement-driven changes:

Three more operational defects were found in that pass, bringing the running total to eight:

tests/test_live_modem_radios.py guards the live-radio fixes, including the overlap carry, unit-converted duplicate suppression, energy-ratio squelch, traffic cropping, concurrent-burst crop span and overlap rebasing.

One proposed FPGA-related live-modem "fix" is explicitly retracted. The source shows bladerf_transmit_command() already falls back from its fpga_bitstream argument to config.fpga_bitstream, so changing the caller was not the decisive antenna-path fix. FPGA pinning remains required, but that particular change was a no-op for the live QSO failure.

The HF channel-validation work added another process lesson. A sweep run with an outer ProcessPoolExecutor at 24-32 workers oversubscribed the host because src/mosaic_hf/acquisition.py already uses its own acquisition ThreadPoolExecutor. The documented control is MOSAIC_ACQUISITION_THREADS; when an outer process pool owns the cores it must be set to 1. The handoff records 10 OS threads per trial process before the fix and 2 after setting MOSAIC_ACQUISITION_THREADS=1. BLAS environment variables did not help because the extra threads were the modem acquisition pool, not BLAS. The transferable lesson is to measure the thread count instead of assuming an environment variable took effect.

The first ITU-R F.1487 sweep also produced a false negative before the harness was corrected. It passed a bare encoded burst to the decoder with no trailing samples, so decoding could run out of buffer with stream ends before coded payload for reasons unrelated to the channel. Two clues prevented that from becoming a protocol claim: flat-channel results were nearly independent of SNR, and total failure on a 0.5 ms / 0.1 Hz channel was implausible against an 80 ms symbol. Padding the receive buffer fixed the flat and good spot checks. The same class of defect had already appeared in audio-path resampling, where polyphase filter group delay required trailing room. A result that is flat against a parameter it should depend on is treated as evidence of a harness defect, not as proof of a robust modem.

Transmitter cleanliness and ambient RF

Measured-on-hardware transmitter and site observations:

Observation Result Evidence
Uncalibrated TX LO leakage, carrier to wanted signal -56.3424566419605 dB run-artifacts/leakage-baseline/carrier-leakage-manifest.json
Generated waveform DC content -220 dB docs/RF-BENCH-HANDOFF.md
TX DC calibration result -16.203982223509964 dB carrier to signal run-artifacts/leakage-calibrated/carrier-leakage-manifest.json
Effect of calibrate dc tx about 40 dB worse docs/RF-BENCH-HANDOFF.md, leakage manifests
Ambient carrier at 431.200 MHz with HackRF streaming 15.128914018928498 dB above local floor run-artifacts/rx-lo-leakage-1/receiver-lo-leakage-manifest.json
Ambient carrier at 431.200 MHz with HackRF idle 16.046696200057355 dB above local floor run-artifacts/rx-lo-leakage-1/receiver-lo-leakage-manifest.json

The DC calibration must not be used for the measured setup. The ambient carrier is unrelated to the modem and is present with all bench transmitters idle.

Measured results

Cabled hardware results

These are measured-on-hardware results over retained cabled captures. Rows whose profile is HF are MOSAIC-HF profile tests; they are not HF-band RF links.

Artifact Profile Direction Result CFO / drift Burst Evidence
run-artifacts/vendor-bladerf-tx-hackrf-rx-20260803/ HF bladeRF -> HackRF CRC-valid A -> B, text x +1186.0 Hz / 0.0 Hz/s not in manifest vendor-run-manifest.json
run-artifacts/vendor-hackrf-tx-bladerf-rx-20260803/ HF HackRF -> bladeRF CRC-valid B -> A, text x -1210.0 Hz / -1.0 Hz/s not in manifest vendor-run-manifest.json
run-artifacts/vendor-bladerf-tx-hackrf-rx-full-20260803/ HF bladeRF -> HackRF CRC-valid MOSAIC-A -> MOSAIC-B, text hello-node +1289.0 Hz / 0.0 Hz/s not in manifest vendor-run-manifest.json
run-artifacts/cabled-431200000-ve6slp-20260804/ HF bladeRF -> HackRF CRC-valid +1494.75 Hz / 0.0 Hz/s not in manifest vendor-run-manifest.json
run-artifacts/uhf-cabled-1/ UHF bladeRF -> HackRF CRC-valid +468.0 Hz / 0.0 Hz/s 1.29 / 1.31 s, complete vendor-run-manifest.json
run-artifacts/uhf-cabled-reverse-1/ UHF HackRF -> bladeRF CRC-valid -484.0 Hz / 0.0 Hz/s 1.149 / 1.17 s, complete vendor-run-manifest.json

The 50 dB cabled safety facts record bladeRF TX conducted power of 0.0 dBm (1.0 mW) at the TX connector and a 50 dB fixed 50-ohm pad in the cabled path (run-artifacts/measured-50db-cabled-facts-20260803.json).

On-air single-packet hardware results

All retained on-air results use bladeRF txvga2 of 10 dB or less when the bladeRF is the on-air transmitter, and the handoff states that the radios were a few feet apart. Rows whose profile is HF are MOSAIC-HF profile tests over the bench's UHF antenna path, not ionospheric HF contacts.

Measured UHF waterfall and transmit spectrum

The waterfall is measured on-air data from run-artifacts/solo-usrp-observer/raw_rx.cf32: a USRP B210 observing one MOSAIC-UHF packet with access signature 47 over the antenna path at 431.200 MHz. The plotted slice is representative of the retained 128 MB complex64 capture; the spectrum panel shows the corresponding transmitted profile containment. The weaker mirrored structure is not a second MOSAIC station; it is already present in the retained bladeRF SC16 transmit stream as a residual transmit-side image about 62 dB below the wanted component, and the retained decode for this capture contains only VE6SLP.

Artifact Profile Direction Result CFO / drift Peak / floor Burst Evidence
run-artifacts/on-air-431200000-fpga016-1/ HF bladeRF -> HackRF CRC-valid +467.75 Hz / -1.0 Hz/s peak 0.31396645307540894; floor not recorded 17.112 / 17.14 s, complete vendor-run-manifest.json
run-artifacts/on-air-reverse-1/ HF HackRF -> bladeRF CRC-valid -445.25 Hz / 0.0 Hz/s peak 0.17589831352233887; floor not recorded superseded guard reported 22.0 / 15.54 s vendor-run-manifest.json and handoff note
run-artifacts/on-air-txtest-20260805/ HF bladeRF -> HackRF CRC-valid +419.25 Hz / 0.0 Hz/s peak 0.3709171712398529; floor not recorded 16.473 / 16.5 s, complete vendor-run-manifest.json
run-artifacts/on-air-txtest-20260805-reply/ HF HackRF -> bladeRF CRC-valid -442.25 Hz / 0.0 Hz/s peak 0.180165097117424; floor 0.07945477217435837 15.189 / 15.22 s, complete vendor-run-manifest.json
run-artifacts/uhf-on-air-1/ UHF bladeRF -> HackRF CRC-valid +724.0 Hz / 0.0 Hz/s peak 0.33341526985168457; floor 0.008717949502170086 1.29 / 1.31 s, complete vendor-run-manifest.json
run-artifacts/uhf-on-air-reverse-1/ UHF HackRF -> bladeRF CRC-valid -724.0 Hz / 0.0 Hz/s peak 0.10893066972494125; floor 0.08279542624950409 1.169 / 1.19 s, complete vendor-run-manifest.json
run-artifacts/solo-usrp/ UHF USRP B210 -> HackRF CRC-valid VE6NAS +692.0 Hz / 0.0 Hz/s peak 100 ms mean magnitude 0.0833497866988182 no burst field; envelope profile retained three-radio-manifest.json
run-artifacts/solo-bladerf/ UHF bladeRF -> HackRF CRC-valid VE6SLP +804.0 Hz / 0.0 Hz/s peak 100 ms mean magnitude 0.09547556936740875 no burst field; envelope profile retained three-radio-manifest.json
run-artifacts/solo-bladerf-txvga10/ UHF bladeRF -> HackRF CRC-valid VE6SLP +812.0 Hz / drift not recorded peak 100 ms mean magnitude 0.30271145701408386 no burst field; envelope profile retained three-radio-manifest.json
run-artifacts/solo-usrp-observer/ UHF bladeRF -> USRP B210 CRC-valid VE6SLP +100.0 Hz / drift not recorded peak 100 ms mean magnitude 0.04625697433948517 B210 receive gain 52.0 dB three-radio-manifest.json, usrp-rx.log

The reverse UHF run is a valid packet, but its peak is close to the recorded floor. The handoff describes this as not a comfortable link.

All three bench radios have now been exercised in real RF roles: bladeRF as a transmitter, HackRF as a decoding receiver, and B210 as both a transmitter and a decoding receiver. That does not make the front ends equivalent, but it does show the waveform is not receivable only by one specific SDR path.

run-artifacts/hf-rf-link/offline-decode.json is also measured RF evidence, but it is deliberately reported as a partial: the MOSAIC-HF waveform radiated on the UHF SDR carrier acquired strongly and attributed VE6SLP-1 first, then failed at header decode with no CRC-valid sender. It is not an on-air HF contact and not a successful direct-radiated MOSAIC-HF packet.

On-air independent multi-transmitter hardware result

Multi-user recovery: on-air UHF proof, simulated UHF scaling, HF fade ceiling

The left panel summarizes retained radiated UHF multi-user decodes. The centre panel is current-harness no-fade simulation (complete recovery through 16 stations; single-seed 22/24 and 26/32 beyond that) with faded eight-station means marked separately. The right panel is the HF shared-channel simulation ceiling under ITU-style fading. Artifact paths are cited in the tables below.

run-artifacts/three-radio-uhf-2/three-radio-manifest.json is the first retained result in this repository where two physically independent transmitters are decoded by a third radio. It is stronger evidence than the composite runs because the transmitters do not share a local oscillator, power amplifier, timebase, sample clock or exact digital start time; their relative keying is only as repeatable as the operating-system scheduler.

Role Radio Station and payload RF settings Evidence
Transmitter 1 bladeRF x115 VE6SLP -> VA6GA, independent alpha de VE6SLP, access signature 0x2f txvga1 -4 dB, txvga2 0 dB three-radio-manifest.json
Transmitter 2 USRP B210 VE6NAS -> VA6SLP, independent bravo de VE6NAS, access signature 0x5b TX gain 52.0 dB three-radio-manifest.json, usrp-tx.log
Observer HackRF One receiver-only bounded SIC decode LNA 8 dB, VGA 32 dB, 2,000,000 sample/s, 8 s capture three-radio-manifest.json, hackrf-rx.log

Measured receiver result:

Artifact Profile Result CFOs Timing in 96 ksample/s audio Stop condition Evidence
run-artifacts/three-radio-uhf-2/ UHF CRC-valid VE6SLP, VE6NAS VE6SLP +804.0 Hz; VE6NAS +700.0 Hz VE6SLP 2.9784166666666665-4.228416666666667 s; VE6NAS 2.9959375-4.2659375 s all unique catalogue entries cancelled three-radio-manifest.json
run-artifacts/three-radio-uhf-4station/ UHF CRC-valid VE6NAS, VA6GA, VE6SLP, VA6SLP +692.0, +604.0, +796.0, +908.0 Hz decoded extents span 2.98046875-4.610666666666667 s all unique catalogue entries cancelled three-radio-manifest.json
run-artifacts/onair-4station-imbalanced/ UHF CRC-valid VA6GA, VE6NAS, VE6SLP, VA6SLP +1308.0, +1196.0, +796.0, +444.0 Hz decoded extents span 3.033-4.563 s all unique catalogue entries cancelled three-radio-manifest.json
run-artifacts/onair-8station/ UHF CRC-valid VE6NAS-1, VE6SLP-2, VE6SLP-1, VE6NAS-2, VA6SLP-1, VA6GA-2, VA6SLP-2, VA6GA-1 -44.0, +764.0, +340.0, +956.0, +28.0, +548.0, +940.0, -300.0 Hz decoded extents span 3.0403541666666666-4.59759375 s all unique catalogue entries cancelled three-radio-manifest.json
run-artifacts/onair-12station/ UHF CRC-valid VE6SLP-1, VE6SLP-2, VE6SLP-3, VE6NAS-1, VE6NAS-2, VE6NAS-3, VA6GA-1, VA6GA-2, VA6GA-3, VA6SLP-1, VA6SLP-2, VA6SLP-3 +404.0, +820.0, +20.0, +1012.0, +252.0, +652.0, -236.0, +604.0, +76.0, +996.0, -156.0, +428.0 Hz decoded extents span 2.9991875-4.66775 s all unique catalogue entries cancelled offline-decode.json
run-artifacts/onair-12station-random/ UHF CRC-valid VE6SLP-1, VE6SLP-2, VE6SLP-3, VE6NAS-1, VE6NAS-2, VE6NAS-3, VA6GA-1, VA6GA-2, VA6GA-3, VA6SLP-1, VA6SLP-2, VA6SLP-3 +564.0, +460.0, +692.0, +1156.0, +1108.0, +796.0, -260.0, +996.0, +692.0, +108.0, -100.0, +1012.0 Hz decoded extents span 3.131177083333333-4.4988125 s all unique catalogue entries cancelled three-radio-manifest.json

For three-radio-uhf-2, the two decoded extents overlap for 1.2324791666666667 s, computed from the manifest sample indices and the MOSAIC-UHF 96,000 sample/s audio rate. The residual power trajectory in that receiver record is 0.0012211621653254354 -> 0.0006059456766651809 -> 0.00011367312073785727 after CRC-gated cancellation of VE6SLP and then VE6NAS.

The four-station run is a stronger receiver-load test, but not a four-radio claim: it uses two independent RF chains, each carrying two digitally superimposed logical stations. The bladeRF chain carries VE6SLP and VA6SLP; the USRP chain carries VE6NAS and VA6GA.

The imbalanced four-station run is a stronger unequal-power on-air case. It puts three logical stations on the bladeRF and one on the B210. Both radios' waveforms are peak-normalized to 0.9499999284744263 before transmission, so the bladeRF composite's scale is 0.221676317396694 while the single-station B210 scale is 0.5033002154561952. In the receiver fit, the B210-carried VA6GA gain is 0.08786596575527592; the three bladeRF-carried gains are 0.04468409135754174, 0.04516445963073307 and 0.043744873770962436, putting VA6GA 5.780477366688979-6.057870382925946 dB above them. This is consistent with the 7.12215636413299 dB digital-scale difference between the B210 and bladeRF waveforms, partly offset by the solo-calibration result that the bladeRF at txvga2 0 is 1.1797552840885095 dB hotter at the HackRF observer. All four still decode, with peak 100 ms mean magnitude 0.11614685505628586; the run sits comfortably inside the -10 dB no-channel near/far bound rather than at its edge. The operational hazard is that the number of logical stations composited on one radio silently sets their transmit level; adding another station to a composite attenuates the others. That is a bench artifact of the composite generator, not a protocol property.

The eight-station run uses SSID-style suffixes to make VE6SLP-1 and VE6SLP-2, for example, distinct station identities while both are still VE6SLP transmissions. This fixed a bench identification limitation rather than changing the protocol: addressed_payload() allows source and destination strings up to 63 bytes, and the three-radio tool now accepts a source that is either the licensed callsign or that callsign plus an SSID suffix. The run puts four stations on the bladeRF and four on the B210, observed by the HackRF; the manifest records bladeRF txvga2 10, B210 gain 64.0 dB, peak envelope 0.34632688760757446 and minimum envelope bucket 0.008672110736370087. The waveform digital scales were 0.21578100857516117 on the bladeRF and 0.18644231263234207 on the B210. The handoff records the run as an example of applying the received-level rule predictively: the higher bladeRF and B210 gains were checked in a prepare-only run before radiating, rather than by diagnosing a failed on-air decode.

The twelve-station runs are now the largest retained on-air decodes. The handoff records six logical stations composited on the bladeRF and six on the B210, again observed by the HackRF. In run-artifacts/onair-12station/, those twelve stations were deliberately given even arrivals from 0.00 s to 0.66 s in 0.06 s steps and well-separated carrier offsets. The first decode attempt was interrupted after the radios had already finished, so the retained CS8 observer capture was decoded offline with tools/decode_capture.py; offline-decode.json ties the result to run-artifacts/onair-12station/raw_rx.cs8 with SHA-256 7fdb4bbad1d5f03e4f096fe3724d3fe90aa86cce8f6e5df43408bc8fcc0129f4. It recovers all twelve catalogue identities and stops with all unique catalogue entries cancelled.

The follow-up run-artifacts/onair-12station-random/ used randomly drawn arrivals and carrier offsets for the same twelve stations; the handoff records seed 4242, with the closest pair 1.0 ms apart in arrival and 2.0 Hz apart in offset. It also recovers all twelve stations and stops with all unique catalogue entries cancelled. The earlier claim that the twelve-station on-air success depended on coordinated arrival is therefore withdrawn.

Shared-channel status by profile

The central shared-channel claim is proven on MOSAIC-UHF hardware. run-artifacts/onair-12station-random/three-radio-manifest.json is radiated, completed on air, uses independent bladerf and usrp transmitters, has catalogue size 12, and contains all twelve expected CRC-valid sender IDs: VE6SLP-1, VE6SLP-2, VE6SLP-3, VE6NAS-1, VE6NAS-2, VE6NAS-3, VA6GA-1, VA6GA-2, VA6GA-3, VA6SLP-1, VA6SLP-2 and VA6SLP-3. run-artifacts/onair-8station/ recovers 8/8 and run-artifacts/onair-4station-imbalanced/ recovers 4/4 on the same radiated, two-independent-transmitter basis. The honest nuance is physical: two radios carried twelve logical stations, so this is not a twelve-converter claim. The two RF carriers are nevertheless genuinely independent hardware with no shared clock or timing reference, decoded by a third radio.

The same claim is not proven on MOSAIC-HF. Auditing the retained multi-station artifacts shows that the on-air shared-channel records above all carry band_profile: uhf; HF evidence had been single-packet until the simulated hf-audio scaling runs described below.

Live keyboard QSO over the antenna path

The handoff now records a bidirectional live keyboard QSO at 431.200 MHz between two independent stations running the live KISS modem rather than offline bench scripts:

Console Delivered text Evidence
VE6SLP [08:54:58Z] <<< VE6SLP DE VA6GA UR 599 IN CALGARY K docs/RF-BENCH-HANDOFF.md
VA6GA [09:05:44Z] <<< VA6GA DE VE6SLP AGN 73 SK docs/RF-BENCH-HANDOFF.md

The station roles were VE6SLP using bladeRF transmit and HackRF receive, and VA6GA using the B210 for transmit and receive. The measured operating settings recorded for this path are B210 receive gain 52.0 dB, bladeRF txvga2 10 and txvga1 -4. The handoff explicitly warns that B210 receive gain 70.0 dB compresses the front end in this feet-apart bench geometry.

This result corrects the previous "antenna path blocked" interpretation. The antenna path was not the missing piece; the live software was.

The first live QSO was minutes-latency because the station decoded whole receive windows. The usability pass crops each non-silent receive window to the traffic span before decoding. On the retained real B210 observer capture run-artifacts/solo-usrp-observer/raw_rx.cf32, which contains 16,000,000 IQ samples over 8 s, the crop spans samples 1,200,000 to 5,400,000. The handoff records 19.0 s for the full-window decode and 4.6 s for the cropped decode, a 4.2x improvement, with the same payload b'MSA1\x06\x05\x00\x11VE6SLPVA6GAB210 solo observe'. Re-running the same measurement during local verification produced the same crop and byte-identical payload, with a similar roughly 4x speed-up.

The B210 receive window was also lengthened to amortize UHD setup cost. The handoff records about 2.0 s of B210 deaf time per receive window, or about 80% duty cycle with 8 s windows, while the HackRF has effectively no such gap. In one measured one-way run, 8 s USRP windows delivered 2 of 5 messages and 16 s windows delivered 5 of 5. At the 16 s default, a symmetric five-message exchange between VE6SLP and VA6GA delivered 4 of 5 messages in both directions, with 0 rejected frames and 0 station errors. That is usable, but it is not 5 of 5 and it is still a small-count result. The handoff records an end-to-end latency example of about 30 s: a message queued at 09:35:47 was on the far operator's screen at 09:36:22.

The preceding attempt, run-artifacts/three-radio-uhf-1/, is also useful engineering evidence. It used the same independent-radio topology but bladeRF txvga2 was 10 dB. The receiver recovered only VE6SLP, rejected VE6NAS at the header stage, and stopped with no CRC-valid candidate in residual generation. The successful run lowered bladeRF txvga2 to 0 dB rather than raising the B210, keeping total radiated power down while bringing the received signals closer together. tools/three_radio_congested.py also carries a --bladerf-lead-seconds option, defaulting to 1.05 s, because the bladeRF CLI loads the FPGA image before streaming and otherwise keys late relative to the other process.

The solo calibration captures explain the power-balance fix. Using the same HackRF observer and the 100 ms envelope buckets, the B210 at gain 52 measured a peak mean magnitude of 0.0833497866988182. The bladeRF at txvga2 0 measured 0.09547556936740875, which is +1.1797552840885095 dB relative to that B210 reference. The bladeRF at txvga2 10 measured 0.30271145701408386, which is +11.202487313470115 dB relative to the same reference. That 11.2 dB imbalance sits on the no-channel software near/far bound already reported in the handoff: UHF recovers both packets at -10 dB but loses one by -15 dB. The failed three-radio attempt is therefore consistent with a link-budget problem, not an arbitrary station-count limit.

On-air congested/composite hardware results

These are measured-on-hardware captures, but the congested signal was produced by one radio transmitting a digitally superimposed composite. The component bursts have distinct start times, carrier offsets and amplitudes, but they share one local oscillator, one power amplifier and one timebase. These runs therefore test receiver cancellation under controlled overlap; the independent three-radio result above is the stronger hardware claim for uncoordinated transmitters.

Artifact Profile Composite transmitters Receiver result Burst Stop condition Evidence
run-artifacts/on-air-collision-1/ HF second burst +3.5 s, +180 Hz, amplitude 0.7 CRC-valid VE6SLP only; VA6SLP rejected at header 16.769 / 16.8 s, complete no CRC-valid candidate in residual generation collision-run-manifest.json
run-artifacts/on-air-collision-2/ HF second burst +3.5 s, +180 Hz, amplitude 1.0 CRC-valid VE6SLP, VA6SLP 16.769 / 16.8 s, complete all unique catalogue entries cancelled collision-run-manifest.json
run-artifacts/uhf-on-air-congested-1/ UHF second burst starts at 0.85 s, +180 Hz, amplitude 0.85 CRC-valid VE6SLP, VA6SLP 1.759 / 1.98 s, incomplete envelope all unique catalogue entries cancelled collision-run-manifest.json
run-artifacts/usrp-observer-2tx-hi/ UHF B210 observer; bladeRF radiates two asynchronous logical stations, second +0.35 s, +600 Hz, amplitude 0.8 CRC-valid VE6SLP, VE6NAS peak 100 ms mean magnitude 0.0802842453122139; minimum envelope bucket 0.005275574978441 all unique catalogue entries cancelled three-radio-manifest.json, usrp-rx.log

The UHF congested run's burst_complete is false. The handoff explains that the envelope detector loses the quieter transmitter's tail, while the receiver still decodes from the samples.

The B210 observer run is a front-end independence result, not an independent-transmitter result: both logical stations are radiated by the bladeRF in one composite waveform, while the independent part is the receiving station.

Simulated/software results

These are not on-air measurements.

Result Label Evidence
Both MOSAIC-HF and MOSAIC-UHF show an Eb/N0 cliff at about +1 dB in a matched-noise software sweep. Simulated docs/RF-BENCH-HANDOFF.md
MOSAIC-HF audio ITU-R F.1487-style simulations recover 240/240 through -9 dB SNR (7.8 dB Eb/N0); the flat AWGN threshold is bracketed between +1.8 and -1.2 dB Eb/N0, while the fading cases cost about 3 dB near threshold. Simulated channel model, not on-air HF run-artifacts/hf-channel/itu-f1487.json, itu-f1487-waterfall.json, itu-f1487-cliff.json
MOSAIC-HF audio multi-user simulations work without fading; under Watterson-style multipath the older multi-tap-only peel averaged ~2.6/4 stations. The hf-congested stack (multi-tap + known-payload add-back refine + 2 basins + residual-aware header/payload metrics) recovered 4/4 users on 8/8 seeds at 2 ms delay spread / 0.5 Hz Doppler / 6 dB amplitude spread. Still simulation only — not on-air HF. Simulated channel model, not on-air HF run-artifacts/hf-multiuser/hf-fade-harness-latest.json, tools/hf_fade_multiuser_harness.py, earlier hf-audio-nofade.json / hf-audio-scaling.json
At fixed transmit power, the UHF profile collects 12 dB more noise in its wider bandwidth. Analytical/software-source claim in handoff docs/RF-BENCH-HANDOFF.md
UHF software test cancels a neighbour 6 dB down in a two-transmitter composite. Simulated, no RF channel tests/test_profiles.py
Handoff near/far sweep reports HF: 2/2 at 0 dB, -3 dB and -10 dB; UHF: 2/2 at 0 dB, -3 dB, -6 dB and -10 dB, then 1/2 at -15 dB and -25 dB. Simulated docs/RF-BENCH-HANDOFF.md
Scaling files without an even_spacing key are superseded and must not be quoted as current MOSAIC performance. For the old unfaded files, the cause was the sweep tool's signal generator: it produced double-sideband mirror images instead of true frequency offsets. Superseded simulated scaling results run-artifacts/scaling/README.md, uhf-balanced-12-seeds.json, uhf-balanced-knee.json, uhf-balanced-many.json, uhf-nofade.json, uhf-nofade-8.json, uhf-faded-seeds.json, uhf-spread0-8.json, uhf-spread12-8.json, hf-balanced.json
Current no-fade UHF random-arrival seeds with 0.0 dB amplitude spread, 0.6 s arrival span and +/-800.0 Hz CFO spread recovered 12/12 and 12/12. Both stopped with all unique catalogue entries cancelled. Simulated; current harness, no fading, no AWGN added by tools/many_station_scaling.py run-artifacts/scaling/uhf-analytic-12-seeds.json, tools/many_station_scaling.py
Current no-fade UHF random-arrival seed-7 re-sweep with 0.0 dB amplitude spread recovered 8/8, 10/10, 12/12 and 16/16. Every run stopped with all unique catalogue entries cancelled. Simulated; current harness, no fading, no AWGN added by tools/many_station_scaling.py run-artifacts/scaling/uhf-analytic-resweep.json, tools/many_station_scaling.py
Current no-fade UHF random-arrival seed-7 deep sweep with 0.0 dB amplitude spread recovered 22/24 and 26/32. Both larger runs stopped with no CRC-valid candidate in residual generation after copy-only interference projection. Simulated; current harness, no fading, no AWGN added by tools/many_station_scaling.py; single seed at each count run-artifacts/scaling/uhf-analytic-deep.json, tools/many_station_scaling.py
Current no-fade UHF even-spacing twelve-station runs recovered 8/12 at 0.0 dB amplitude spread and 5/12 at 6.0 dB spread. These are coordinated layouts, not uncoordinated random-arrival traffic. Simulated; current harness, no fading, no AWGN added by tools/many_station_scaling.py; even spacing run-artifacts/scaling/uhf-even-12-seeds.json, run-artifacts/scaling/uhf-even-12-spread6.json
Current-code eight-station faded UHF recheck with 6.0 dB amplitude spread recovered 12/32 station opportunities over four independent draws, an outage rate of 62.5%. Simulated; flat fading, no AWGN added by tools/many_station_scaling.py run-artifacts/scaling/uhf-faded-recheck.json, tools/many_station_scaling.py
Current-code eight-station faded UHF balanced run with 0.0 dB amplitude spread recovered 15/32 station opportunities over four independent draws, an outage rate of 53.1%. Simulated; flat fading, no AWGN added by tools/many_station_scaling.py run-artifacts/scaling/uhf-faded-balanced.json, tools/many_station_scaling.py

The simulated near/far sweep bounds the cancellation algorithm, not the radio link. The previous no-fade population scaling interpretation is withdrawn. In the defective unfaded path, each station's real audio was cast to complex and rotated, then emitted as real(audio * exp(j2*pi*f*t)), which is audio * cos(2*pi*f*t). That is double-sideband AM: it halves the wanted component and adds a mirror image of the entire station reflected about DC. The mirror matches no catalogue entry, so SIC cannot cancel it, and the error scales with station count. src/mosaic_hf/channel.py always applied a Hilbert transform first; only the unfaded branch in tools/many_station_scaling.py skipped it. tests/test_many_station_scaling.py now pins this defect by asserting that the image is more than 40 dB below the wanted component.

The current scaling provenance rule is simple: quote only files whose result records carry an even_spacing key. Files without that key are superseded; run-artifacts/scaling/README.md shows that old unfaded files can disagree sharply with current code on identical inputs because the sweep tool generated the wrong signal, not because the receiver or protocol changed. The analytic and balanced words in filenames are lab labels, not tool modes or distinct channel models.

The fixed no-fade branch now agrees with the twelve-station hardware evidence for the retained seeds that have landed: the same two twelve-station seeds that previously failed in superseded files now recover 12/12 and 12/12, and the current seed-7 random-arrival sweep recovers every station through 16: 8/8, 10/10, 12/12 and 16/16. Sixteen was the size of the signature pool used by that run, not a receiver limit. tools/many_station_scaling.py now contains 32 signatures, and tests/test_many_station_scaling.py checks that those signatures yield 32 distinct preamble sequences, which is what acquisition actually observes. The current deep random-arrival sweep recovers 22/24 and 26/32; those two large-count points are single seeds, so they show graceful degradation rather than an outage rate.

The old scaling artifacts provide no current congestion-collapse curve, HF scaling curve or near/far population rank-order conclusion to quote. Current even-spacing files are also not interchangeable with random-arrival results: even spacing is a coordinated layout, while random arrival and carrier offsets are the clean uncoordinated case.

The faded current-harness runs show a separate many-station problem: fading can set delivery rate when the packet has no diversity across the fade. This is a link-budget outage, not a receiver limitation; when a station is in a deep fade for the duration of its burst, the energy never arrives for any receiver to recover. The no-fade results above are pure mutual-interference simulations with no AWGN added, so they do not measure delivery in a fading channel. Combined with the current 53.1%-62.5% eight-station flat-fading outage results, the live many-station picture remains materially worse than any no-fade count alone suggests.

The current headline is therefore narrower and stronger than the withdrawn collapse story. In a clean no-fade, no-AWGN channel with random arrivals, every station is recovered through 16, then the single-seed deep sweep recovers 22/24 and 26/32. Under flat fading, only 2 to 5 of 8 stations recover in each current draw: 62.5% total outage with 6 dB amplitude spread and 53.1% with levels balanced. Balancing levels improves that eight-station faded result by about nine percentage points, but does not engineer the outage away. The binding constraint shown by the retained simulations is the fading channel, not multi-user interference in the clean channel.

HF audio channel and real-time receive validation

ITU-R F.1487-style simulated HF channels

run-artifacts/hf-channel/itu-f1487.json and run-artifacts/hf-channel/itu-f1487-waterfall.json record the current tools/hf_channel_validation.py sweeps for MOSAIC_HF_AUDIO. These are simulated channel-model results, not on-air HF contacts and not measurements of ionospheric propagation.

The first table is a sanity floor for multipath and Doppler behaviour, not a noise-sensitivity headline. Because MOSAIC-HF uses B_ref = 2400 Hz and R_b = 50 bit/s, each SNR value corresponds to Eb/N0 = SNR + 16.8 dB; these initial points therefore have enormous energy-per-bit margin. Each cell contains 10 trials.

Channel Delay spread Doppler 20 dB SNR / 36.8 dB Eb/N0 15 dB / 31.8 dB 10 dB / 26.8 dB 5 dB / 21.8 dB
Flat 0.0 ms 0.0 Hz 10/10 10/10 10/10 10/10
Good 0.5 ms 0.1 Hz 10/10 10/10 10/10 10/10
Moderate 1.0 ms 0.5 Hz 10/10 10/10 10/10 10/10
Poor 2.0 ms 1.0 Hz 10/10 10/10 10/10 10/10

The aggregate sanity-floor result is 160/160 recovered. The physical interpretation is that the MOSAIC-HF 80 ms symbol is 40 times longer than the poor channel's 2 ms delay spread, so multipath smears across 2.5% of a symbol. The non-coherent 16-FSK detector also does not require carrier phase tracking, so a 1 Hz Doppler process is not a phase-lock problem for the demodulator.

The follow-up low-SNR waterfall extended the same four channels down to -9 dB SNR, still in the 2400 Hz reference bandwidth. Each cell contains 12 trials.

Channel Delay spread Doppler 2 dB SNR / 18.8 dB Eb/N0 0 dB / 16.8 dB -3 dB / 13.8 dB -6 dB / 10.8 dB -9 dB / 7.8 dB
Flat 0.0 ms 0.0 Hz 12/12 12/12 12/12 12/12 12/12
Good 0.5 ms 0.1 Hz 12/12 12/12 12/12 12/12 12/12
Moderate 1.0 ms 0.5 Hz 12/12 12/12 12/12 12/12 12/12
Poor 2.0 ms 1.0 Hz 12/12 12/12 12/12 12/12 12/12

The aggregate low-SNR waterfall result is 240/240 recovered. That is expected: -9 dB SNR is still 7.8 dB Eb/N0 after the 16.8 dB processing gain of the 50 bit/s HF profile.

run-artifacts/hf-channel/itu-f1487-cliff.json then brackets the sensitivity cliff. Each cell contains 12 trials.

MOSAIC-HF ITU-R F.1487-style sensitivity waterfall

SNR Eb/N0 Flat Good Moderate Poor
-12 dB +4.8 dB 12/12 8/12 7/12 10/12
-15 dB +1.8 dB 12/12 0/12 0/12 0/12
-18 dB -1.2 dB 0/12 0/12 0/12 0/12
-21 dB -4.2 dB 0/12 0/12 0/12 0/12
-24 dB -7.2 dB 0/12 0/12 0/12 0/12

The flat AWGN threshold lies between the tested points of +1.8 and -1.2 dB Eb/N0: the flat channel is 12/12 at -15 dB SNR and 0/12 at -18 dB SNR. The transition from full recovery to zero recovery over one 3 dB step is expected for a well-matched K=7 rate-1/2 convolutional code, not a receiver instability.

The three fading channels all remain decodable at -12 dB SNR (+4.8 dB Eb/N0) and all fail at -15 dB SNR (+1.8 dB Eb/N0), where the flat channel is still perfect. The retained data therefore show about a 3 dB threshold cost for the ITU-R F.1487-style multipath and Doppler cases. The -12 dB faded counts are not ranked against each other: with 12 trials, their binomial standard error is about 1.4 counts, so 8/12, 7/12 and 10/12 are overlapping small-sample outcomes. The supported statement is that all three fading channels degrade together near threshold, recovering roughly 60-85% at that point.

HF limits one-pager

MOSAIC-HF alpha limits: sensitivity cliff, multi-user fade ceiling, proven vs open

MOSAIC-HF at alpha is a proven waveform and single-user decoder with a clear shared-channel hole. The figure compresses four facts:

  1. Single-user sensitivity (simulated F.1487). Flat AWGN brackets between +1.8 and −1.2 dB Eb/N0 (Eb/N0 = SNR_2400Hz + 10·log₁₀(2400/50) = SNR + 16.8 dB). Good/moderate/poor multipath cost about 3 dB near threshold.
  2. Shared-channel no-fade vs fade (simulated hf-audio). Without fading the receiver peels through four stations and most of eight. Under Watterson-style multipath the multi-tap-only peel was limited (~2.6/4 mean). The measured hf-congested stack recovers 4/4 on eight independent four-user seeds at 2 ms / 0.5 Hz / 6 dB (simulation only).
  3. Proven checklist. Real converter loopback, streaming throughput checks, single-user channel-model cliffs, and simulated faded four-user recovery under the congested stack are in; on-air HF contacts and transceiver-in-the-loop ionospheric multi-user QSOs are out.
  4. Mechanism. First-pass constant or lightly tracked cancellation over a ~13 s packet is biased by concurrent users and delayed paths. Multi-tap fits, residual-aware soft metrics, multi-basin locks, and known-payload add-back refine deepen the residual enough for survivors in the measured four-user harness. UHF multi-user on-air success remains a static-lab fact.

HF shared-channel simulation: no-fade success, fading progress

tools/many_station_scaling.py --profile hf-audio measures concurrent MOSAIC-HF audio populations with independent start times, carrier offsets, amplitudes and seeded channel processes. The newer tools/hf_fade_multiuser_harness.py A/B compares single-tap, multi-tap, refine, and the full hf-congested stack. These are simulations, not on-air HF contacts. Retained artifacts include run-artifacts/hf-multiuser/hf-fade-harness-latest.json, hf-audio-nofade.json, and hf-audio-scaling.json.

Stations No fading (earlier) Multi-tap only (Watterson) hf-congested (Watterson)
2 2/2 2.00/2 full 8/8 2.00/2 full 8/8
4 4/4 ~2.6/4 full 2/8 4.00/4 full 8/8
6 mean 2.83/6 full 0/6 mean 5.83/6 full 5/6
8 6–8/8 collapse mean 6.50/8, full 3/6 (multi-order known-payload residual rebuild)

The no-fade control isolates the variable: in a static channel the HF receiver already peels concurrent users. Under Watterson-style multipath the first-pass canceller is biased by unresolved neighbours and delayed energy; multi-tap alone is not enough. The congested stack — multi-tap fit, two acquisition basins, residual-aware header and payload metrics, and known-payload add-back refine — clears the measured four-user harness and nearly clears six-user mixtures. Eight-user faded recovery remains partial; on-air HF is not claimed.

HF shared-channel QRM/QRN boundary

tools/hf_qrm_qrn_harness.py fixes four users, 0.5 Hz Watterson-style Doppler, 2 ms delay spread, 6 dB station spread, arrivals over 8 s and mixture-relative AWGN. Two deterministic seeds compare the unchanged congested receiver against the bounded front end under clean audio, a full-span 1500 Hz carrier at −6 dB SIR, three keyed intervals at the same frequency and SIR, a full-span carrier with +0.75 Hz/s linear drift, Bernoulli-Gaussian impulses at rate 0.001/sample and 100× reference RMS, and persistent QRM plus QRN.

Scenario Baseline Robust front end False deliveries
Clean 8/8 8/8 0
Persistent carrier QRM 5/8 8/8 0
Keyed carrier QRM 6/8 8/8 0
Linearly drifting carrier QRM 2/8 8/8 0
Impulsive QRN 6/8 8/8 0
Combined 3/8 8/8 0
Total 30/48 48/48 0

The preprocessor itself used at most 1.026% of observation time across five timings of each retained 11.4–14.1 s input on the reference host. That timing excludes acquisition and SIC: successful four-user robust decodes took up to about 133 s per capture and are not realtime as a whole. The result is a finite synthetic simulation, not an HF-band measurement, sensitivity estimate or population outage rate. Retained data are run-artifacts/hf-multiuser/hf-qrm-qrn-congested.json, hf-interference-realtime.json, and hf-qrm-qrn-evidence.json.

HF QRM/QRN recovery and preprocessing evidence

For UHF lab proxy of multipath, a static two-path FIR can be applied to the composite TX IQ (apply_two_path_iq_fir, onair_collision --multipath-delay-s) so a single SDR radiates a frequency-selective mixture without a second reflector. That path is instrumented; radiated multipath captures require an operator-run TX step.

The mechanism is cancellation error in a time-varying multipath channel. SIC reconstructs the strongest decoded station and subtracts it, but the replica is only as good as the channel estimate. A MOSAIC-HF packet runs about 13 s and an ionospheric channel does not hold still for 13 s. The first station can decode before any cancellation is needed; later stations depend on a subtraction whose residual can bury what remains underneath. This is a receiver limitation, not a waveform limitation: the one-station waveform decodes through the F.1487 channel set, while the current canceller lacks an in-packet channel tracker.

This also explains why UHF escapes the failure in the retained hardware. Radios a few feet apart in a static lab present an essentially non-fading channel, matching the no-fade column where HF also works. UHF does not have a better receiver; its retained shared-channel runs simply do not exercise the time-varying HF channel that breaks cancellation.

A dummy-load HF transceiver test would likewise reproduce the no-fade column. It would be valuable for qualifying rig audio, ALC behaviour, drive level and keying, but it would not predict on-air shared-channel behaviour because the dummy load removes the Doppler and multipath that break HF multi-user cancellation.

The real converter path reinforces the headroom issue. In run-artifacts/hf-multiuser/real-2.json, two overlapping stations through the Scarlett 4i4 converters recovered 1/2 when the second station was 4.6 dB weaker; run-artifacts/hf-multiuser/real-2-equal.json recovered 0/2 at equal power. The same unequal mixture decoded offline at 2/2 in run-artifacts/hf-multiuser/offline-2.json. Equal power is hard for peeling because there is no strongest station to remove first, and the converter path reduces headroom because transmit drive is capped at 0.25 peak to avoid SSB ALC while summing stations raises crest factor.

Continuous-reception real-time requirement

A continuous receiver must decode faster than real time. Otherwise backlog and operator-visible latency grow for as long as the station runs. This is a correctness property of the live modem, not merely an optimization target.

The original MOSAIC-HF acquisition grid remains appropriate for free-running SDR oscillators: it searches +/-2000 Hz and drift hypotheses. MOSAIC_HF_AUDIO uses the same waveform as MOSAIC-HF but narrows the acquisition search to +/-100 Hz at 12.5 Hz coarse steps and removes drift hypotheses. The reason is physical. A transceiver audio path occupies 200-2800 Hz inside an SSB filter; a 2000 Hz dial/audio error would move the waveform to 2200-4800 Hz, outside the passband. What remains is rig clock error between stations tuned to the same dial frequency; the source notes that 1 ppm at 14 MHz is 14 Hz each, so +/-100 Hz is generous. The source also records that the wider search decodes a 13 s packet in 273 s, about 19 times slower than real time.

Local retained real-time checks with tools/hf_realtime_check.py use simulated 20 dB SNR streams, 20 s receive windows and 15 s overlap. They exercise the streaming requirement that a window may contain more than one transmission: the consumer advances by PacketDecodeResult.packet_end_sample rather than stopping at the first packet.

Artifact Audio duration Transmissions Decode time Real-time factor Recovered Median latency Worst latency Backlog
run-artifacts/hf-realtime-check/minutes5.txt 300 s 16 88.8 s x0.296 14/16 11.2 s 17.1 s 0.0 s
run-artifacts/hf-realtime-check/minutes12.txt 720 s 38 153.3 s x0.213 30/38 9.4 s 24.5 s 0.0 s

Latency is measured from the end of a transmission to the text being available and includes waiting for the receive window to close. These are simulated streaming checks of decoder throughput and window handling; they are not HF on-air QSOs.

Alpha release claims

This section is the publish boundary for the alpha tree. A row marked claimed is backed by retained artifacts or executable constants cited in this paper. A row marked not claimed is either unmeasured, failed in retained work, or still research. Marketing language that exceeds this matrix is out of scope for alpha.

Capability matrix

Capability Status Evidence class Anchor
P1 wire format (header CRC-8/ATM, payload CRC-16, K=7 rate-1/2, 16-of-192 hop) Claimed Executable constants framing.py, waveform.py, frame_bit_layout.png
MOSAIC-HF 50 bit/s and MOSAIC-UHF 800 bit/s profiles (Δf·T_s = 1) Claimed Executable constants profiles.py, band-profile table
Eb/N0 conversion SNR_ref_dB + 10·log₁₀(B_ref/R_b) (= +16.8 dB for both profiles) Claimed Definition + tables Mathematical specification
CRC-gated SIC with immutable r₀ and LIFO undo Claimed Implementation + flowchart sic.py, sic_flowchart.png
Catalogue-bounded multi-user search (not unlimited blind active set) Claimed Implementation sic.py, acquisition config
Cabled CRC-valid HF-profile and UHF-profile point-to-point on bench SDRs Claimed Measured hardware cabled vendor manifests
On-air single-packet UHF and HF-profile-on-UHF-carrier CRC recovery Claimed Measured hardware on-air vendor / solo manifests
Independent two-transmitter on-air UHF decode by a third radio Claimed Measured hardware three-radio-uhf-2/
On-air UHF 4/4, 8/8, 12/12 logical stations on two RF chains (incl. random arrivals) Claimed Measured hardware onair-4station-imbalanced/, onair-8station/, onair-12station/, onair-12station-random/
Live bidirectional keyboard QSO at 431.200 MHz (KISS modem) Claimed Measured hardware / handoff live QSO section; small-count
MOSAIC-HF real DAC→analogue→ADC loopback byte-exact Claimed Measured converters Scarlett 4i4 3/3
MOSAIC-HF single-user F.1487-style cliff (~+1.8…−1.2 dB Eb/N0 flat; ~3 dB fade cost) Claimed Simulated channel model hf-channel/itu-f1487*.json, hf_itu_sensitivity.png
MOSAIC-HF no-fade multi-user peel in audio simulation Claimed Simulated hf-multiuser/hf-audio-nofade.json
UHF no-fade software scaling complete through 16; graceful 22/24, 26/32 Claimed Simulated, current harness scaling/uhf-analytic-*.json (files with even_spacing)
Continuous HF-audio decode faster than real time with zero backlog (sim streams) Claimed Simulated streaming hf-realtime-check/
Continuous UHF live demod keep-up (packet walk + residual peel + multi-ROI crop, uhf-live) Claimed Simulated streaming tools/uhf_realtime_check.py (wall ratio ≪ 1)
Operator web panel (TX inhibit, transcript, receiver health) Claimed Implementation src/mosaic_hf/operator_panel.py, --ui-port
Version-2 adaptive mixed-rate profile bank and per-QSO control Claimed Implementation + software realtime gates mcs_receiver.py, mcs_adaptation.py, adaptive-mcs-realtime.png
UHF Basic/Fast/Turbo concurrently recovered with two independent transmit radios and a third-radio observer Claimed Measured hardware, 3/3 adaptive-mcs/uhf-three-radio-final-20260815/
HF Basic/Chat/Fast concurrently recovered over a UHF RF carrier Claimed with carrier boundary Measured hardware, 3/3 adaptive-mcs/hf-over-uhf-three-radio-20260815/
Four-user HF congested receiver under bounded synthetic CW QRM / impulsive QRN Claimed with simulation boundary 48/48 robust vs 30/48 baseline across persistent, keyed, +0.75 Hz/s drifting, impulsive, and combined cells; zero false deliveries; two seeds hf-qrm-qrn-congested.json, hf-qrm-qrn-evidence.png
Fail-closed FPGA pin, burst-duration check, envelope profile, capture SHA binding Claimed Bench methodology guardrails section
Twelve independent RF converters on air Not claimed two chains carry logical composites
Three simultaneous independent RF transmit chains Not claimed unmeasured
On-air HF ionospheric contact / transceiver-in-the-loop QSO Not claimed no retained HF on-air contact
General QRM rejection Not claimed Outside current bounded front end one declared keyed-CW schedule and bounded linear drift are measured; arbitrary keyed patterns, nonlinear chirps, digital and speech-like interferers are not
MOSAIC-HF shared-channel success under ITU-style fading Not claimed Simulated failure hf-audio-scaling.json (≈1–2 recovered)
Robust UHF multi-user under fading Not claimed Simulated partial faded 8-station means ≪ 8
Tracked-gain SIC alone solves HF multi-user fading Not claimed Root-cause diagnosis HF limits section; BIC-selected tracking present, fade recovery still limited
Spectrum compliance, spurious, antenna or regulatory approval Not claimed out of scope
Instant messaging / sub-second chat Not claimed Air time is the floor UHF packet ≈2.6 s; HF packet ≈40 s; continuous demod keeps up with the stream but does not shrink airtime
Joint ML multi-user receiver / unlimited blind discovery Not claimed architecture is CRC-gated peel
Superseded no-fade scaling files without even_spacing Withdrawn Harness defect scaling/README.md

What alpha is for

Alpha is a deterministic reference modem and validation method: inspectable constants, CRC-gated cancellation, retained captures, and an honest split between radiated UHF multi-user proof and HF single-user / audio-path proof. It is not an HF multi-user service claim and not a twelve-radio field network.

Required citations when quoting alpha

Known limitations and future work

Reproducing retained result types

The following are the exact command forms recorded in the source and handoff. They are reproduction instructions, not commands run while writing this paper.

Cabled or on-air point-to-point run

python tools/cabled_bladerf_hackrf.py --output run-artifacts/<name> \
  --mode cabled --frequency-hz 431200000 --profile uhf --capture-seconds 4 \
  --fpga hostedx115-latest.rbf --source VE6SLP --destination VA6GA \
  --text "MOSAIC-UHF cabled first light" --execute

Reverse direction:

python tools/cabled_bladerf_hackrf.py --output run-artifacts/<name> \
  --mode on-air --callsign VE6SLP --frequency-hz 431200000 --profile uhf \
  --direction hackrf-to-bladerf --hackrf-tx-gain-db 20 --capture-seconds 4 \
  --fpga hostedx115-latest.rbf --destination VE6SLP \
  --text "MOSAIC-UHF on air reply" --execute

Congested composite run

python tools/onair_collision.py --output run-artifacts/<name> \
  --mode on-air --callsign VE6SLP --frequency-hz 431200000 --profile uhf \
  --txvga2-db 10 --capture-seconds 8 --fpga hostedx115-latest.rbf \
  --transmitter "VE6SLP:VA6GA:0x2f:0.20:0.0:1.0:alpha" \
  --transmitter "VA6SLP:VE6NAS:0x5b:0.85:180.0:0.85:bravo" --execute

Three-radio independent-transmitter run

python tools/three_radio_congested.py --output run-artifacts/<name> \
  --callsign VE6SLP --frequency-hz 431200000 --profile uhf \
  --capture-seconds 8 --txvga2-db 0 --usrp-gain-db 52 \
  --transmitter "bladerf:VE6SLP:VA6GA:0x2f:0.0:0.0:1.0:independent alpha de VE6SLP" \
  --transmitter "usrp:VE6NAS:VA6SLP:0x5b:0.0:0.0:1.0:independent bravo de VE6NAS" \
  --fpga hostedx115-latest.rbf --execute

Carrier leakage measurement

python tools/tx_carrier_leakage.py --output run-artifacts/<name> \
  --frequency-hz 431200000 --callsign VE6SLP --destination VA6GA \
  --profile uhf --txvga2-db 10 --fpga hostedx115-latest.rbf --execute

Do not add --calibrate for the retained operating mode; the measured calibrated run made carrier leakage much worse.

Offline retained-capture decode

python tools/decode_capture.py --capture run-artifacts/<name>/raw_rx.cs8 \
  --output run-artifacts/<name>/offline-decode.json --profile uhf \
  --format cs8 --candidate CALLSIGN-1:0x2f --candidate CALLSIGN-2:0x5b

For CF32 observer captures, pass --format cf32. The output records the capture SHA-256 so the decode remains tied to the exact recording.

Live KISS modem

mosaic-modem --callsign VE6SLP --peer VA6GA --profile uhf \
  --frequency-hz 431200000 --fpga hostedx115-latest.rbf --execute

The live station exposes KISS on 127.0.0.1:8001 by default and runs an RX -> guard -> TX -> guard cycle.

MOSAIC-HF transceiver audio path

List audio devices:

mosaic-hf-audio devices

Run the continuous browser and KISS station:

mosaic-hf-audio \
  --callsign VE6SLP --ssid 1 \
  --ptt-port /dev/ttyUSB0 --ptt-line rts \
  serve --peer VA6GA-2 \
  --input-device 13 --output-device 13

This service continuously captures audio, decodes the bounded peer callsign catalogue through the HF congested receiver, serves the operator panel on 127.0.0.1:8088, and serves KISS TCP on 127.0.0.1:8001. Audio output and PTT remain inhibited until the operator arms TX. The service lifecycle, peer decode, and fail-closed transmit path have deterministic tests. A two-way on-air HF QSO remains unclaimed.

Run the retained analogue-converter loopback form:

mosaic-hf-audio --amplitude 0.9 loopback --device 13 \
  --repeat 3 --text "VE6SLP-1 DE VA6GA-2 QSL 599 K"

For a real SSB transmitter, start with the default --amplitude 0.25, use a serial --ptt-port/--ptt-line where possible, and raise drive only until ALC just begins to move.

Test environment note

tests/test_gnuradio_virtual_channel.py must be run with /usr/bin/python3, not the pyenv interpreter. The repository contains a gnuradio/ directory that can shadow the real GNU Radio package as a namespace package from the repository root; the failure only appears in subprocesses launched from elsewhere. With system Python, the retained check is:

/usr/bin/python3 -m pytest tests/test_gnuradio_virtual_channel.py -q

Limits of the current evidence