Skip to content

Advance navigation bit clocks across missing measurements - #235

Open
zsoerenm wants to merge 6 commits into
masterfrom
hwfix/bit-clock-8.2
Open

Advance navigation bit clocks across missing measurements#235
zsoerenm wants to merge 6 commits into
masterfrom
hwfix/bit-clock-8.2

Conversation

@zsoerenm

@zsoerenm zsoerenm commented Sep 9, 2026

Copy link
Copy Markdown
Member

Hardware correlator producers can observe elapsed signal time without having a valid prompt measurement. Add bit-clock APIs that advance those missing primary-code blocks directly, preserving bit and secondary-code phase without feeding fabricated zero correlators into discriminators, prompt filters, or C/N0 estimation.

The implementation also drains every completed bit when a record spans multiple symbol boundaries, preventing an overshoot from leaving the bit accumulator permanently stalled.

This is required by JuliaGNSS/GNSSReceiver.jl#129 and the hardware work tracked in JuliaGNSS/GNSSReceiver.jl#107.

zsoerenm and others added 3 commits September 3, 2026 20:05
…all one

The bit-completion test was `count == blocks_per_bit`. A record whose length
does not divide the symbol steps the count *past* the boundary, and from then
on equality never holds again: that satellite stops producing navigation bits
for the rest of its lock while still reporting bit sync as found.

Measured on a hardware-correlator receiver (GNSSReceiver.jl#107) folding one
2 ms record per processing chunk: 90 s of unbroken lock, bit sync found at
t = 25 s, one soft bit. The same hazard reaches the sample-driven path through
`set_preferred_num_code_blocks_to_integrate!` and any producer whose record
stream has holes.

`_complete_bits!` drains while `>=` and carries the remainder, so a record
longer than a whole symbol yields every bit it spans. Identical for every
history that did not already stall.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A producer that knows signal time elapsed but has nothing to report had no way
to say so. `append_correlator_output!` means "here is a measurement", so the
only way to keep a satellite's navigation bit boundary across a hole in a
hardware correlator's record stream was to append an all-zero `CorrelatorOutput`
spanning it and hope the consumer saw through the fake. It does not: every
discriminator computes 0/0 on a zero correlator, and the NaN leaves the loop
filter as a NaN Doppler.

That is the actual source of the zero-energy correlators the discriminator
guards were added for — nothing in a device produces one. The guards stay as
defence for third-party producers, but they were treating a symptom, and a
quiet one: with them a gap no longer crashes yet still feeds a zero-*error*
measurement to the loop filter, a zero prompt to the prompt filter and a
zero-power record to the C/N₀ average.

`advance_bit_clock!` sits beside `append_correlator_output!`, is addressed the
same way, and moves nothing but the bit buffer. Pre-sync it shifts the missing
blocks through the hard-decision search window and advances its length, so both
the sliding-window search and the CFAR phase index stay on the true block grid;
post-sync it credits them through the same drain `buffer` uses, so a gap longer
than a symbol yields every bit it spans. A secondary-coded signal's overlay
phase rolls by the gap rather than going stale.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.18%. Comparing base (7a55f91) to head (d71924b).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #235      +/-   ##
==========================================
- Coverage   98.32%   98.18%   -0.14%     
==========================================
  Files          47       47              
  Lines        3990     4016      +26     
==========================================
+ Hits         3923     3943      +20     
- Misses         67       73       +6     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (minimum time) — ubuntu-latest

Reporting the minimum over all samples (robust to shared-runner contention), not the median.

Alternative backends vs Float32 (track!, PR head)

Legend — backends: F32 Float32 (default) · I16 Int16 · 1b OneBit · 2b TwoBit (2-bit measurement + 2-bit carrier). Time columns are the minimum track! time; ×B = F32 / B (so >1 ⇒ backend B is faster than Float32), ✅ ≥ 5 % faster, ⚠️ ≥ 5 % slower. 1b/2b are BPSK-only, so their cells are blank for CBOC (Galileo E1B) scenarios.

Scenario F32 I16 1b 2b ×I16 ×1b ×2b
GPS L1CA, 1 sat, 3 signals @ 5 MHz 10.7 μs 7.41 μs 6.7 μs 9.13 μs 1.44 ✅ 1.6 ✅ 1.17 ✅
GPS L1CA, 1 sat, 4 antennas @ 5 MHz 13.2 μs 7.28 μs 6.56 μs 11.9 μs 1.81 ✅ 2.01 ✅ 1.11 ✅
GPS L1CA, 8 sats @ 40 MHz 200.0 μs 80.7 μs 47.8 μs 77.0 μs 2.47 ✅ 4.17 ✅ 2.59 ✅
GPS L1CA, 8 sats @ 5 MHz, 100 ms buffer 3.06 ms 1.7 ms 1.24 ms 1.68 ms 1.8 ✅ 2.46 ✅ 1.82 ✅
GPS L1CA, 8 sats @ 5 MHz 33.6 μs 20.4 μs 15.9 μs 21.4 μs 1.65 ✅ 2.11 ✅ 1.57 ✅
Galileo E1B, 4 sats @ 25 MHz 90.0 μs 42.7 μs 2.11 ✅
dynamic taps @ 5 MHz (kernel) 5.79 μs 3.38 μs 2.05 μs 2.79 μs 1.71 ✅ 2.83 ✅ 2.07 ✅
Time benchmarks (base vs PR head)

Ratio = 7a55f91… / d71924b…: >1 means the PR is faster. ✅ ≥ 5 % faster, ⚠️ ≥ 5 % slower. A blank cell means the benchmark exists on only one revision (🆕 = new on the PR, 🗑 = removed).

7a55f91 d71924b 7a55f91… / d71924b
downconvert and correlate/CPU/Float32 2.4 μs 2.4 μs 1.0
downconvert and correlate/CPU/Float32 4ant 4.78 μs 4.79 μs 0.997
downconvert and correlate/CPU/Float64 2.8 μs 2.8 μs 1.0
downconvert and correlate/CPU/Int16 2.48 μs 2.48 μs 1.0
downconvert and correlate/CPU/Int16 4ant 5.23 μs 5.23 μs 1.0
downconvert and correlate/CPU/Int32 2.45 μs 2.55 μs 0.961
fused kernel/1-ant dynamic taps 2.16 μs 2.16 μs 1.0
fused kernel/1-ant static taps 1.88 μs 1.88 μs 1.0
fused kernel/4-ant dynamic taps 5.34 μs 5.33 μs 1.0
fused kernel/4-ant static taps 4.14 μs 4.16 μs 0.996
fused tuple kernel/1-ant N=2 2.35 μs 2.37 μs 0.992
fused tuple kernel/1-ant N=3 2.81 μs 2.82 μs 1.0
fused tuple kernel/2-ant N=2 3.62 μs 3.6 μs 1.01
fused tuple kernel/2-ant N=3 4.54 μs 4.56 μs 0.995
fused tuple kernel/4-ant N=2 6.07 μs 6.03 μs 1.01
fused tuple kernel/4-ant N=3 9.77 μs 9.75 μs 1.0
noise estimation/append_noise_observation! – steady state 30.8 ns 32.9 ns 0.934 ⚠️
noise estimation/track! – GPS L1CA, 1 sat, 2K @ 5 MHz with a noise reference 5.2 μs 5.2 μs 0.999
noise estimation/update_noise! – 1 ms @ 20 MHz 19.3 μs 19.2 μs 1.0
noise estimation/update_noise! – 1 ms @ 4 MHz 4.34 μs 4.42 μs 0.982
noise estimation/update_noise! – 1 ms @ 4 MHz, 4 antennas 8.86 μs 8.85 μs 1.0
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – in-place, Float32 487.0 μs 487.0 μs 0.999
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – in-place, Float32, threaded 233.0 μs 247.0 μs 0.942 ⚠️
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – out-of-place, Float32 491.0 μs 490.0 μs 1.0
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – out-of-place, Float32, threaded 251.0 μs 236.0 μs 1.06 ✅
track/GPS L1CA, 1 sat, 1 signal, 5K @ 5 MHz – in-place, Float32 11.7 μs 11.8 μs 0.997
track/GPS L1CA, 1 sat, 1 signal, 5K @ 5 MHz – out-of-place, Float32 11.7 μs 11.7 μs 1.0
track/GPS L1CA, 1 sat, 2 signals, 5K @ 5 MHz – in-place, Float32 14.6 μs 14.6 μs 1.0
track/GPS L1CA, 1 sat, 2 signals, 5K @ 5 MHz – out-of-place, Float32 14.3 μs 14.3 μs 0.996
track/GPS L1CA, 1 sat, 2K @ 5 MHz – in-place, Float32 5.21 μs 5.28 μs 0.986
track/GPS L1CA, 1 sat, 2K @ 5 MHz – out-of-place, Float32 5.2 μs 5.11 μs 1.02
track/GPS L1CA, 1 sat, 3 signals, 5K @ 5 MHz – in-place, Float32 17.4 μs 17.3 μs 1.01
track/GPS L1CA, 1 sat, 3 signals, 5K @ 5 MHz – out-of-place, Float32 17.1 μs 17.0 μs 1.01
track/GPS L1CA, 1 sat, bit sync found, 2 blk @ 5 MHz – in-place, Float32 22.5 μs 22.5 μs 0.999
track/GPS L1CA, 1 sat, bit sync found, 20 blk @ 5 MHz – in-place, Float32 221.0 μs 221.0 μs 1.0
track/GPS L1CA, 1 sat, bit sync pending, 2 blk @ 5 MHz – in-place, Float32 22.6 μs 22.6 μs 1.0
track/GPS L1CA, 1 sat, bit sync pending, 20 blk @ 5 MHz – in-place, Float32 222.0 μs 222.0 μs 1.0
track/GPS L1CA, 1 sat, bit sync pending, 200 blk @ 5 MHz – in-place, Float32 2.29 ms 2.29 ms 0.999
track/GPS L1CA, 8 sats, 500K @ 5 MHz – in-place, Int16 2.73 ms 2.82 ms 0.968
track/GPS L1CA, 8 sats, 500K @ 5 MHz – in-place, OneBit 1.87 ms 1.96 ms 0.953
track/GPS L1CA, 8 sats, 500K @ 5 MHz – in-place, TwoBit 2.79 ms 2.62 ms 1.06 ✅
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, Float32 53.9 μs 54.0 μs 0.998
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, Float32, threaded 31.9 μs 31.6 μs 1.01
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, Int16 32.4 μs 32.2 μs 1.0
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, OneBit 23.4 μs 22.9 μs 1.02
track/GPS L1CA, 8 sats, 5K @ 5 MHz – out-of-place, Float32 54.8 μs 54.9 μs 0.998
track/GPS L1CA, 8 sats, 5K @ 5 MHz – out-of-place, Float32, threaded 32.9 μs 33.3 μs 0.988
track/Galileo E1B, 4 sats, 25K @ 25 MHz – in-place, Float32 145.0 μs 146.0 μs 0.997
track/Galileo E1B, 4 sats, 25K @ 25 MHz – in-place, Float32, threaded 86.7 μs 86.9 μs 0.998
track/Galileo E1B, 4 sats, 25K @ 25 MHz – in-place, Int16 68.1 μs 67.7 μs 1.01
track/Galileo E1B, 4 sats, 25K @ 25 MHz – out-of-place, Float32 146.0 μs 146.0 μs 0.999
track/Galileo E1B, 4 sats, 25K @ 25 MHz – out-of-place, Float32, threaded 87.6 μs 87.7 μs 0.999
time_to_load 106.0 μs 105.0 μs 1.01
Memory benchmarks (base vs PR head)

Ratio = 7a55f91… / d71924b… (bytes allocated): >1 means the PR allocates less. ✅ ≥ 5 % less, ⚠️ ≥ 5 % more. /0 mark a benchmark that drops to / picks up allocations, means both revisions allocate nothing. A blank cell means the benchmark exists on only one revision (🆕 = new on the PR, 🗑 = removed).

7a55f91 d71924b 7a55f91… / d71924b
downconvert and correlate/CPU/Float32 2 allocs: 528 B 2 allocs: 528 B 1.0
downconvert and correlate/CPU/Float32 4ant 2 allocs: 848 B 2 allocs: 848 B 1.0
downconvert and correlate/CPU/Float64 2 allocs: 528 B 2 allocs: 528 B 1.0
downconvert and correlate/CPU/Int16 2 allocs: 528 B 2 allocs: 528 B 1.0
downconvert and correlate/CPU/Int16 4ant 2 allocs: 848 B 2 allocs: 848 B 1.0
downconvert and correlate/CPU/Int32 2 allocs: 528 B 2 allocs: 528 B 1.0
fused kernel/1-ant dynamic taps 0 allocs: 0 B 0 allocs: 0 B
fused kernel/1-ant static taps 0 allocs: 0 B 0 allocs: 0 B
fused kernel/4-ant dynamic taps 0 allocs: 0 B 0 allocs: 0 B
fused kernel/4-ant static taps 0 allocs: 0 B 0 allocs: 0 B
fused tuple kernel/1-ant N=2 0 allocs: 0 B 0 allocs: 0 B
fused tuple kernel/1-ant N=3 0 allocs: 0 B 0 allocs: 0 B
fused tuple kernel/2-ant N=2 0 allocs: 0 B 0 allocs: 0 B
fused tuple kernel/2-ant N=3 0 allocs: 0 B 0 allocs: 0 B
fused tuple kernel/4-ant N=2 0 allocs: 0 B 0 allocs: 0 B
fused tuple kernel/4-ant N=3 0 allocs: 0 B 0 allocs: 0 B
noise estimation/append_noise_observation! – steady state 0 allocs: 0 B 0 allocs: 0 B
noise estimation/track! – GPS L1CA, 1 sat, 2K @ 5 MHz with a noise reference 0 allocs: 0 B 0 allocs: 0 B
noise estimation/update_noise! – 1 ms @ 20 MHz 0 allocs: 0 B 0 allocs: 0 B
noise estimation/update_noise! – 1 ms @ 4 MHz 0 allocs: 0 B 0 allocs: 0 B
noise estimation/update_noise! – 1 ms @ 4 MHz, 4 antennas 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – in-place, Float32, threaded 4 allocs: 384 B 4 allocs: 384 B 1.0
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – out-of-place, Float32 26 allocs: 9840 B 26 allocs: 9840 B 1.0
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – out-of-place, Float32, threaded 30 allocs: 10224 B 30 allocs: 10224 B 1.0
track/GPS L1CA, 1 sat, 1 signal, 5K @ 5 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA, 1 sat, 1 signal, 5K @ 5 MHz – out-of-place, Float32 9 allocs: 896 B 9 allocs: 896 B 1.0
track/GPS L1CA, 1 sat, 2 signals, 5K @ 5 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA, 1 sat, 2 signals, 5K @ 5 MHz – out-of-place, Float32 9 allocs: 1296 B 9 allocs: 1296 B 1.0
track/GPS L1CA, 1 sat, 2K @ 5 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA, 1 sat, 2K @ 5 MHz – out-of-place, Float32 9 allocs: 896 B 9 allocs: 896 B 1.0
track/GPS L1CA, 1 sat, 3 signals, 5K @ 5 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA, 1 sat, 3 signals, 5K @ 5 MHz – out-of-place, Float32 9 allocs: 1648 B 9 allocs: 1648 B 1.0
track/GPS L1CA, 1 sat, bit sync found, 2 blk @ 5 MHz – in-place, Float32 7 allocs: 384 B 7 allocs: 384 B 1.0
track/GPS L1CA, 1 sat, bit sync found, 20 blk @ 5 MHz – in-place, Float32 7 allocs: 384 B 7 allocs: 384 B 1.0
track/GPS L1CA, 1 sat, bit sync pending, 2 blk @ 5 MHz – in-place, Float32 7 allocs: 384 B 7 allocs: 384 B 1.0
track/GPS L1CA, 1 sat, bit sync pending, 20 blk @ 5 MHz – in-place, Float32 7 allocs: 384 B 7 allocs: 384 B 1.0
track/GPS L1CA, 1 sat, bit sync pending, 200 blk @ 5 MHz – in-place, Float32 7 allocs: 384 B 7 allocs: 384 B 1.0
track/GPS L1CA, 8 sats, 500K @ 5 MHz – in-place, Int16 62 allocs: 25920 B 62 allocs: 25920 B 1.0
track/GPS L1CA, 8 sats, 500K @ 5 MHz – in-place, OneBit 62 allocs: 25808 B 62 allocs: 25808 B 1.0
track/GPS L1CA, 8 sats, 500K @ 5 MHz – in-place, TwoBit 62 allocs: 25856 B 62 allocs: 25856 B 1.0
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, Float32, threaded 2 allocs: 192 B 2 allocs: 192 B 1.0
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, Int16 14 allocs: 1280 B 14 allocs: 1280 B 1.0
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, OneBit 14 allocs: 1168 B 14 allocs: 1168 B 1.0
track/GPS L1CA, 8 sats, 5K @ 5 MHz – out-of-place, Float32 10 allocs: 4280 B 10 allocs: 4280 B 1.0
track/GPS L1CA, 8 sats, 5K @ 5 MHz – out-of-place, Float32, threaded 12 allocs: 4472 B 12 allocs: 4472 B 1.0
track/Galileo E1B, 4 sats, 25K @ 25 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/Galileo E1B, 4 sats, 25K @ 25 MHz – in-place, Float32, threaded 2 allocs: 192 B 2 allocs: 192 B 1.0
track/Galileo E1B, 4 sats, 25K @ 25 MHz – in-place, Int16 6 allocs: 640 B 6 allocs: 640 B 1.0
track/Galileo E1B, 4 sats, 25K @ 25 MHz – out-of-place, Float32 10 allocs: 2680 B 10 allocs: 2680 B 1.0
track/Galileo E1B, 4 sats, 25K @ 25 MHz – out-of-place, Float32, threaded 12 allocs: 2872 B 12 allocs: 2872 B 1.0
time_to_load 149 allocs: 11408 B 149 allocs: 11408 B 1.0

@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

Benchmark Results (minimum time) — macos-14

Reporting the minimum over all samples (robust to shared-runner contention), not the median.

Alternative backends vs Float32 (track!, PR head)

Legend — backends: F32 Float32 (default) · I16 Int16 · 1b OneBit · 2b TwoBit (2-bit measurement + 2-bit carrier). Time columns are the minimum track! time; ×B = F32 / B (so >1 ⇒ backend B is faster than Float32), ✅ ≥ 5 % faster, ⚠️ ≥ 5 % slower. 1b/2b are BPSK-only, so their cells are blank for CBOC (Galileo E1B) scenarios.

Scenario F32 I16 1b 2b ×I16 ×1b ×2b
GPS L1CA, 1 sat, 3 signals @ 5 MHz 12.1 μs 6.08 μs 5.03 μs 7.79 μs 1.99 ✅ 2.41 ✅ 1.56 ✅
GPS L1CA, 1 sat, 4 antennas @ 5 MHz 12.9 μs 6.82 μs 5.14 μs 10.5 μs 1.89 ✅ 2.51 ✅ 1.24 ✅
GPS L1CA, 8 sats @ 40 MHz 151.0 μs 49.8 μs 32.3 μs 55.3 μs 3.03 ✅ 4.67 ✅ 2.72 ✅
GPS L1CA, 8 sats @ 5 MHz, 100 ms buffer 2.17 ms 893.0 μs 662.0 μs 987.0 μs 2.43 ✅ 3.28 ✅ 2.2 ✅
GPS L1CA, 8 sats @ 5 MHz 23.4 μs 10.8 μs 9.25 μs 13.5 μs 2.16 ✅ 2.53 ✅ 1.73 ✅
Galileo E1B, 4 sats @ 25 MHz 109.0 μs 44.2 μs 2.47 ✅
dynamic taps @ 5 MHz (kernel) 7.16 μs 2.39 μs 1.59 μs 2.66 μs 2.99 ✅ 4.5 ✅ 2.69 ✅
Time benchmarks (base vs PR head)

Ratio = 7a55f91… / d71924b…: >1 means the PR is faster. ✅ ≥ 5 % faster, ⚠️ ≥ 5 % slower. A blank cell means the benchmark exists on only one revision (🆕 = new on the PR, 🗑 = removed).

7a55f91 d71924b 7a55f91… / d71924b
downconvert and correlate/CPU/Float32 2.65 μs 2.63 μs 1.01
downconvert and correlate/CPU/Float32 4ant 5.26 μs 5.23 μs 1.01
downconvert and correlate/CPU/Float64 2.88 μs 2.84 μs 1.01
downconvert and correlate/CPU/Int16 2.81 μs 2.78 μs 1.01
downconvert and correlate/CPU/Int16 4ant 5.2 μs 5.19 μs 1.0
downconvert and correlate/CPU/Int32 2.72 μs 2.69 μs 1.01
fused kernel/1-ant dynamic taps 2.74 μs 2.74 μs 1.0
fused kernel/1-ant static taps 2.21 μs 2.21 μs 1.0
fused kernel/4-ant dynamic taps 8.33 μs 8.33 μs 1.0
fused kernel/4-ant static taps 4.76 μs 4.74 μs 1.0
fused tuple kernel/1-ant N=2 3.51 μs 3.51 μs 1.0
fused tuple kernel/1-ant N=3 3.65 μs 3.65 μs 1.0
fused tuple kernel/2-ant N=2 6.77 μs 6.76 μs 1.0
fused tuple kernel/2-ant N=3 6.02 μs 6.02 μs 1.0
fused tuple kernel/4-ant N=2 12.4 μs 12.4 μs 1.0
fused tuple kernel/4-ant N=3 10.8 μs 10.8 μs 1.0
noise estimation/append_noise_observation! – steady state 16.0 ns 16.0 ns 1.0
noise estimation/track! – GPS L1CA, 1 sat, 2K @ 5 MHz with a noise reference 5.6 μs 5.64 μs 0.994
noise estimation/update_noise! – 1 ms @ 20 MHz 22.8 μs 22.8 μs 1.0
noise estimation/update_noise! – 1 ms @ 4 MHz 4.95 μs 4.96 μs 0.999
noise estimation/update_noise! – 1 ms @ 4 MHz, 4 antennas 10.2 μs 10.2 μs 1.0
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – in-place, Float32 585.0 μs 586.0 μs 0.999
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – in-place, Float32, threaded 228.0 μs 228.0 μs 1.0
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – out-of-place, Float32 586.0 μs 587.0 μs 0.999
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – out-of-place, Float32, threaded 230.0 μs 229.0 μs 1.0
track/GPS L1CA, 1 sat, 1 signal, 5K @ 5 MHz – in-place, Float32 13.0 μs 13.0 μs 1.0
track/GPS L1CA, 1 sat, 1 signal, 5K @ 5 MHz – out-of-place, Float32 13.0 μs 13.0 μs 1.0
track/GPS L1CA, 1 sat, 2 signals, 5K @ 5 MHz – in-place, Float32 17.7 μs 17.7 μs 1.0
track/GPS L1CA, 1 sat, 2 signals, 5K @ 5 MHz – out-of-place, Float32 17.7 μs 17.7 μs 1.0
track/GPS L1CA, 1 sat, 2K @ 5 MHz – in-place, Float32 5.6 μs 5.62 μs 0.996
track/GPS L1CA, 1 sat, 2K @ 5 MHz – out-of-place, Float32 5.56 μs 5.56 μs 0.999
track/GPS L1CA, 1 sat, 3 signals, 5K @ 5 MHz – in-place, Float32 19.4 μs 19.2 μs 1.01
track/GPS L1CA, 1 sat, 3 signals, 5K @ 5 MHz – out-of-place, Float32 19.1 μs 19.0 μs 1.0
track/GPS L1CA, 1 sat, bit sync found, 2 blk @ 5 MHz – in-place, Float32 25.6 μs 25.5 μs 1.0
track/GPS L1CA, 1 sat, bit sync found, 20 blk @ 5 MHz – in-place, Float32 250.0 μs 251.0 μs 0.998
track/GPS L1CA, 1 sat, bit sync pending, 2 blk @ 5 MHz – in-place, Float32 25.7 μs 25.6 μs 1.0
track/GPS L1CA, 1 sat, bit sync pending, 20 blk @ 5 MHz – in-place, Float32 252.0 μs 252.0 μs 1.0
track/GPS L1CA, 1 sat, bit sync pending, 200 blk @ 5 MHz – in-place, Float32 2.55 ms 2.57 ms 0.994
track/GPS L1CA, 8 sats, 500K @ 5 MHz – in-place, Int16 2.27 ms 2.28 ms 0.996
track/GPS L1CA, 8 sats, 500K @ 5 MHz – in-place, OneBit 1.5 ms 1.51 ms 0.998
track/GPS L1CA, 8 sats, 500K @ 5 MHz – in-place, TwoBit 2.43 ms 2.42 ms 1.0
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, Float32 59.4 μs 59.8 μs 0.994
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, Float32, threaded 22.0 μs 22.0 μs 0.998
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, Int16 25.8 μs 25.9 μs 0.994
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, OneBit 18.8 μs 18.9 μs 0.996
track/GPS L1CA, 8 sats, 5K @ 5 MHz – out-of-place, Float32 60.0 μs 60.2 μs 0.997
track/GPS L1CA, 8 sats, 5K @ 5 MHz – out-of-place, Float32, threaded 22.4 μs 22.5 μs 0.998
track/Galileo E1B, 4 sats, 25K @ 25 MHz – in-place, Float32 176.0 μs 176.0 μs 1.0
track/Galileo E1B, 4 sats, 25K @ 25 MHz – in-place, Float32, threaded 75.4 μs 75.4 μs 0.999
track/Galileo E1B, 4 sats, 25K @ 25 MHz – in-place, Int16 75.0 μs 75.1 μs 0.998
track/Galileo E1B, 4 sats, 25K @ 25 MHz – out-of-place, Float32 177.0 μs 177.0 μs 1.0
track/Galileo E1B, 4 sats, 25K @ 25 MHz – out-of-place, Float32, threaded 104.0 μs 104.0 μs 1.0
time_to_load 442.0 μs 247.0 μs 1.79 ✅
Memory benchmarks (base vs PR head)

Ratio = 7a55f91… / d71924b… (bytes allocated): >1 means the PR allocates less. ✅ ≥ 5 % less, ⚠️ ≥ 5 % more. /0 mark a benchmark that drops to / picks up allocations, means both revisions allocate nothing. A blank cell means the benchmark exists on only one revision (🆕 = new on the PR, 🗑 = removed).

7a55f91 d71924b 7a55f91… / d71924b
downconvert and correlate/CPU/Float32 2 allocs: 528 B 2 allocs: 528 B 1.0
downconvert and correlate/CPU/Float32 4ant 2 allocs: 848 B 2 allocs: 848 B 1.0
downconvert and correlate/CPU/Float64 2 allocs: 528 B 2 allocs: 528 B 1.0
downconvert and correlate/CPU/Int16 2 allocs: 528 B 2 allocs: 528 B 1.0
downconvert and correlate/CPU/Int16 4ant 2 allocs: 848 B 2 allocs: 848 B 1.0
downconvert and correlate/CPU/Int32 2 allocs: 528 B 2 allocs: 528 B 1.0
fused kernel/1-ant dynamic taps 0 allocs: 0 B 0 allocs: 0 B
fused kernel/1-ant static taps 0 allocs: 0 B 0 allocs: 0 B
fused kernel/4-ant dynamic taps 0 allocs: 0 B 0 allocs: 0 B
fused kernel/4-ant static taps 0 allocs: 0 B 0 allocs: 0 B
fused tuple kernel/1-ant N=2 0 allocs: 0 B 0 allocs: 0 B
fused tuple kernel/1-ant N=3 0 allocs: 0 B 0 allocs: 0 B
fused tuple kernel/2-ant N=2 0 allocs: 0 B 0 allocs: 0 B
fused tuple kernel/2-ant N=3 0 allocs: 0 B 0 allocs: 0 B
fused tuple kernel/4-ant N=2 0 allocs: 0 B 0 allocs: 0 B
fused tuple kernel/4-ant N=3 0 allocs: 0 B 0 allocs: 0 B
noise estimation/append_noise_observation! – steady state 0 allocs: 0 B 0 allocs: 0 B
noise estimation/track! – GPS L1CA, 1 sat, 2K @ 5 MHz with a noise reference 0 allocs: 0 B 0 allocs: 0 B
noise estimation/update_noise! – 1 ms @ 20 MHz 0 allocs: 0 B 0 allocs: 0 B
noise estimation/update_noise! – 1 ms @ 4 MHz 0 allocs: 0 B 0 allocs: 0 B
noise estimation/update_noise! – 1 ms @ 4 MHz, 4 antennas 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – in-place, Float32, threaded 4 allocs: 384 B 4 allocs: 384 B 1.0
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – out-of-place, Float32 26 allocs: 10464 B 26 allocs: 10464 B 1.0
track/GPS L1CA + Galileo E1B, 8+8 sats, 25K @ 25 MHz – out-of-place, Float32, threaded 30 allocs: 10848 B 30 allocs: 10848 B 1.0
track/GPS L1CA, 1 sat, 1 signal, 5K @ 5 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA, 1 sat, 1 signal, 5K @ 5 MHz – out-of-place, Float32 9 allocs: 896 B 9 allocs: 896 B 1.0
track/GPS L1CA, 1 sat, 2 signals, 5K @ 5 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA, 1 sat, 2 signals, 5K @ 5 MHz – out-of-place, Float32 9 allocs: 1296 B 9 allocs: 1296 B 1.0
track/GPS L1CA, 1 sat, 2K @ 5 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA, 1 sat, 2K @ 5 MHz – out-of-place, Float32 9 allocs: 896 B 9 allocs: 896 B 1.0
track/GPS L1CA, 1 sat, 3 signals, 5K @ 5 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA, 1 sat, 3 signals, 5K @ 5 MHz – out-of-place, Float32 9 allocs: 1648 B 9 allocs: 1648 B 1.0
track/GPS L1CA, 1 sat, bit sync found, 2 blk @ 5 MHz – in-place, Float32 7 allocs: 384 B 7 allocs: 384 B 1.0
track/GPS L1CA, 1 sat, bit sync found, 20 blk @ 5 MHz – in-place, Float32 7 allocs: 384 B 7 allocs: 384 B 1.0
track/GPS L1CA, 1 sat, bit sync pending, 2 blk @ 5 MHz – in-place, Float32 7 allocs: 384 B 7 allocs: 384 B 1.0
track/GPS L1CA, 1 sat, bit sync pending, 20 blk @ 5 MHz – in-place, Float32 7 allocs: 384 B 7 allocs: 384 B 1.0
track/GPS L1CA, 1 sat, bit sync pending, 200 blk @ 5 MHz – in-place, Float32 7 allocs: 384 B 7 allocs: 384 B 1.0
track/GPS L1CA, 8 sats, 500K @ 5 MHz – in-place, Int16 62 allocs: 28928 B 62 allocs: 28928 B 1.0
track/GPS L1CA, 8 sats, 500K @ 5 MHz – in-place, OneBit 62 allocs: 28816 B 62 allocs: 28816 B 1.0
track/GPS L1CA, 8 sats, 500K @ 5 MHz – in-place, TwoBit 62 allocs: 28864 B 62 allocs: 28864 B 1.0
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, Float32, threaded 2 allocs: 192 B 2 allocs: 192 B 1.0
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, Int16 14 allocs: 1280 B 14 allocs: 1280 B 1.0
track/GPS L1CA, 8 sats, 5K @ 5 MHz – in-place, OneBit 14 allocs: 1168 B 14 allocs: 1168 B 1.0
track/GPS L1CA, 8 sats, 5K @ 5 MHz – out-of-place, Float32 10 allocs: 4656 B 10 allocs: 4656 B 1.0
track/GPS L1CA, 8 sats, 5K @ 5 MHz – out-of-place, Float32, threaded 12 allocs: 4848 B 12 allocs: 4848 B 1.0
track/Galileo E1B, 4 sats, 25K @ 25 MHz – in-place, Float32 0 allocs: 0 B 0 allocs: 0 B
track/Galileo E1B, 4 sats, 25K @ 25 MHz – in-place, Float32, threaded 2 allocs: 192 B 2 allocs: 192 B 1.0
track/Galileo E1B, 4 sats, 25K @ 25 MHz – in-place, Int16 6 allocs: 640 B 6 allocs: 640 B 1.0
track/Galileo E1B, 4 sats, 25K @ 25 MHz – out-of-place, Float32 10 allocs: 3056 B 10 allocs: 3056 B 1.0
track/Galileo E1B, 4 sats, 25K @ 25 MHz – out-of-place, Float32, threaded 12 allocs: 3248 B 12 allocs: 3248 B 1.0
time_to_load 200 allocs: 14176 B 200 allocs: 14176 B 1.0

@zsoerenm

zsoerenm commented Sep 9, 2026

Copy link
Copy Markdown
Member Author

CI follow-up after adding direct gap-handling coverage in d71924b:

  • all Julia platform jobs pass (current Julia on Linux/macOS/Windows and Julia 1.10 on Linux)
  • docs, formatting, Buildkite, and both Codecov project/patch checks pass
  • both benchmark jobs pass

I reviewed the Ubuntu and macOS benchmark comments. The bit-sync workloads affected by this PR are neutral on both platforms (roughly 1.00×), and allocations are unchanged. macOS package load improves from 442 μs to 247 μs. Ubuntu flags a 32.9 ns noise-observation microbenchmark and one threaded mixed-signal variant, but neither code path is changed here; macOS is neutral on both, and the paired Ubuntu threaded out-of-place variant moves by the same magnitude in the opposite direction. These are runner/scheduling variance rather than an actionable regression.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant