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 current implementation has two band profiles:

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. It is proven as a one-packet waveform through real audio converters and simulated ITU-R F.1487 channels, but its shared-channel operation is not proven in a fading HF channel. Current hf-audio simulations show that the receiver scales in a static channel and then collapses to one or two recovered stations under ITU-style fading because SIC residuals are not tracked through a time-varying packet.

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 MOSAIC-UHF changes both symbol duration and tone spacing together. Keeping 192 bins and 16 candidates preserves the multi-user resource structure while changing only the time and frequency scale.

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.

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.

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 (tools/mosaic_modem.py) 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 on 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. That is the right estimator structure for HF, but alpha still does not claim shared-channel success under ionospheric fading: measured multi-user fade recovery remains limited, and further diversity / tracking work remains open.

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

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.

Parameter MOSAIC-HF MOSAIC-UHF Source
Audio sample rate 12,000 Hz 96,000 Hz profiles.py
Symbol duration 80 ms 5 ms config.py, profiles.py
Tone spacing 12.5 Hz 200 Hz config.py, profiles.py
Candidates / bins 16 / 192 16 / 192 config.py, profiles.py
Lowest tone 300 Hz 300 Hz config.py, profiles.py
Highest tone 2687.5 Hz 38,500 Hz config.py, profiles.py
SNR reference bandwidth 2,400 Hz 38,400 Hz config.py, profiles.py
Nominal SSB bandwidth 2,800 Hz 40,000 Hz config.py, profiles.py
Raw coded bit rate 50 bit/s 800 bit/s profiles.py
Receive FIR taps 129 1025 profiles.py
Receive transition 100 Hz 3000 Hz profiles.py
TX shaping taps 241 1921 tx_shaping.py, profiles.py

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.

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 tools/mosaic_hf_audio.py provide 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:

python tools/mosaic_hf_audio.py --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 but under ITU-style HF fading recover only 1-2 stations at 4 and 8 stations, stopping with no CRC-valid residual candidate. Simulated channel model, not on-air HF run-artifacts/hf-multiuser/hf-audio-nofade.json, run-artifacts/hf-multiuser/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 ITU-style fading the absolute recovered count saturates near one or two stations.
  3. Proven checklist. Real converter loopback, streaming throughput checks and single-user channel-model cliffs are in; on-air HF contacts, transceiver-in-the-loop ionospheric runs and faded multi-user success are out.
  4. Mechanism. Constant-gain cancellation over a ~13 s packet leaves a time-varying residual that buries weaker users after the first peel. UHF multi-user on-air success does not contradict this: the lab path is essentially static.

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

tools/many_station_scaling.py --profile hf-audio now measures concurrent MOSAIC-HF audio populations with independent start times, carrier offsets, amplitudes and seeded channel processes. These are simulations, not on-air HF contacts. The retained artifacts are run-artifacts/hf-multiuser/hf-audio-nofade.json and run-artifacts/hf-multiuser/hf-audio-scaling.json.

Stations No fading ITU-style HF fading
2 2/2, 2/2, 2/2 2/2, 1/2, 2/2
4 4/4, 4/4, 4/4 1/4, 1/4, 2/4
8 8/8, 6/8, 6/8 1/8, 2/8, 2/8

The no-fade control isolates the variable: in a static channel the HF receiver recovers all stations through four and most or all of eight. Under ITU-style HF fading, the absolute recovered count saturates at one or two even as the population grows; eight stations yields the same absolute count as two. That is a hard ceiling in this receiver, not graceful degradation. Non-complete faded runs stop with no CRC-valid candidate in residual generation, and the four- and eight-station faded failures use the copy-only interference projection stop path.

The mechanism is cancellation error in a time-varying 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
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
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

python tools/mosaic_modem.py --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:

python tools/mosaic_hf_audio.py devices

Run the retained analogue-converter loopback form:

python tools/mosaic_hf_audio.py --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