feat(track): add VectorPLLAndDLL vector-tracking Doppler estimator#204
feat(track): add VectorPLLAndDLL vector-tracking Doppler estimator#204giove-a wants to merge 1 commit into
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #204 +/- ##
==========================================
- Coverage 97.87% 97.55% -0.32%
==========================================
Files 32 33 +1
Lines 3299 3401 +102
==========================================
+ Hits 3229 3318 +89
- Misses 70 83 +13 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Benchmark Results (minimum time) — macos-14Reporting the minimum over all samples (robust to shared-runner contention), not the median. Alternative backends vs Float32 (
|
| Scenario | F32 | I16 | 1b | 2b | ×I16 | ×1b | ×2b |
|---|---|---|---|---|---|---|---|
| 4-antenna @ 5 MHz | 11.5 μs | 5.8 μs | 4.01 μs | 8.42 μs | 1.98 ✅ | 2.86 ✅ | 1.36 ✅ |
| GPS L1CA, 8 sats @ 40 MHz | 381.0 μs | 118.0 μs | 61.0 μs | 117.0 μs | 3.23 ✅ | 6.24 ✅ | 3.27 ✅ |
| GPS L1CA, 8 sats @ 5 MHz | 56.8 μs | 23.5 μs | 17.3 μs | 23.8 μs | 2.42 ✅ | 3.28 ✅ | 2.39 ✅ |
| Galileo E1B, 4 sats @ 25 MHz | 141.0 μs | 59.0 μs | 2.39 ✅ | ||||
| dynamic taps @ 5 MHz (kernel) | 6.92 μs | 2.23 μs | 1.59 μs | 2.64 μs | 3.1 ✅ | 4.36 ✅ | 2.62 ✅ |
| multi-signal N=3 @ 5 MHz | 11.2 μs | 5.25 μs | 3.85 μs | 5.93 μs | 2.14 ✅ | 2.92 ✅ | 1.9 ✅ |
Time benchmarks (base vs PR head)
Ratio = 5f03cd2… / 8ee2ef8…: >1 means the PR is faster. ✅ ≥ 5 % faster,
| 5f03cd2… | 8ee2ef8… | 5f03cd2… / 8ee2ef8… | |
|---|---|---|---|
| downconvert and correlate/CPU/Float32 | 2.46 μs | 2.5 μs | 0.983 |
| downconvert and correlate/CPU/Float32 4ant | 4.86 μs | 4.88 μs | 0.998 |
| downconvert and correlate/CPU/Float64 | 2.68 μs | 2.7 μs | 0.991 |
| downconvert and correlate/CPU/Int16 | 2.61 μs | 2.63 μs | 0.991 |
| downconvert and correlate/CPU/Int16 4ant | 4.82 μs | 4.83 μs | 0.998 |
| downconvert and correlate/CPU/Int32 | 2.53 μs | 2.56 μs | 0.989 |
| fused kernel/1-ant dynamic taps | 2.56 μs | 2.56 μs | 1.0 |
| fused kernel/1-ant static taps | 2.06 μs | 2.06 μs | 1.0 |
| fused kernel/4-ant dynamic taps | 7.81 μs | 7.79 μs | 1.0 |
| fused kernel/4-ant static taps | 4.43 μs | 4.59 μs | 0.966 |
| fused tuple kernel/1-ant N=2 | 3.27 μs | 3.51 μs | 0.933 |
| fused tuple kernel/1-ant N=3 | 3.4 μs | 3.41 μs | 0.997 |
| fused tuple kernel/2-ant N=2 | 6.31 μs | 6.32 μs | 0.999 |
| fused tuple kernel/2-ant N=3 | 5.61 μs | 5.61 μs | 1.0 |
| fused tuple kernel/4-ant N=2 | 11.5 μs | 11.6 μs | 0.996 |
| fused tuple kernel/4-ant N=3 | 10.1 μs | 10.2 μs | 0.992 |
| track/1. Float32/2K – track | 2.74 μs | 2.67 μs | 1.03 |
| track/1. Float32/2K – track! | 2.73 μs | 2.74 μs | 0.998 |
| track/2. L1 8sat/5K – track | 49.3 μs | 49.7 μs | 0.992 |
| track/2. L1 8sat/5K – track! | 48.8 μs | 49.3 μs | 0.99 |
| track/2. L1 8sat/5K – track! Int16 | 21.3 μs | 21.7 μs | 0.983 |
| track/2. L1 8sat/5K – track! OneBit | 15.7 μs | 16.1 μs | 0.972 |
| track/2. L1 8sat/5K – track!-threaded | 49.4 μs | 49.7 μs | 0.993 |
| track/2. L1 8sat/5K – track-threaded | 49.6 μs | 50.0 μs | 0.992 |
| track/3. E1B 4sat/25K – track | 136.0 μs | 136.0 μs | 1.0 |
| track/3. E1B 4sat/25K – track! | 135.0 μs | 135.0 μs | 1.0 |
| track/3. E1B 4sat/25K – track! Int16 | 58.7 μs | 58.6 μs | 1.0 |
| track/3. E1B 4sat/25K – track!-threaded | 136.0 μs | 135.0 μs | 1.0 |
| track/3. E1B 4sat/25K – track-threaded | 136.0 μs | 136.0 μs | 1.0 |
| track/4. 8L1+8E1B/25K – track | 490.0 μs | 490.0 μs | 1.0 |
| track/4. 8L1+8E1B/25K – track! | 489.0 μs | 489.0 μs | 1.0 |
| track/4. 8L1+8E1B/25K – track!-threaded | 488.0 μs | 489.0 μs | 0.999 |
| track/4. 8L1+8E1B/25K – track-threaded | 490.0 μs | 490.0 μs | 1.0 |
| track/5. multi-signal N=1/5K – track | 6.38 μs | 6.43 μs | 0.991 |
| track/5. multi-signal N=1/5K – track! | 6.35 μs | 6.4 μs | 0.992 |
| track/6. multi-signal N=2/5K – track | 10.7 μs | 10.3 μs | 1.04 |
| track/6. multi-signal N=2/5K – track! | 10.4 μs | 10.4 μs | 1.0 |
| track/7. multi-signal N=3/5K – track | 11.5 μs | 11.6 μs | 0.993 |
| track/7. multi-signal N=3/5K – track! | 11.6 μs | 11.7 μs | 0.996 |
| track/8. L1CA presync 2 blk – track! | 12.3 μs | 12.4 μs | 0.993 |
| track/8. L1CA presync 20 blk – track! | 120.0 μs | 120.0 μs | 0.999 |
| track/8. L1CA synced 2 blk – track! | 12.2 μs | 12.3 μs | 0.993 |
| track/8. L1CA synced 20 blk – track! | 118.0 μs | 118.0 μs | 0.999 |
| time_to_load | 543.0 μs | 160.0 μs | 3.4 ✅ |
Memory benchmarks (base vs PR head)
Ratio = 5f03cd2… / 8ee2ef8… (bytes allocated): >1 means the PR allocates less. ✅ ≥ 5 % less, ∞/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).
| 5f03cd2… | 8ee2ef8… | 5f03cd2… / 8ee2ef8… | |
|---|---|---|---|
| downconvert and correlate/CPU/Float32 | 2 allocs: 576 B | 2 allocs: 576 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: 576 B | 2 allocs: 576 B | 1.0 |
| downconvert and correlate/CPU/Int16 | 2 allocs: 576 B | 2 allocs: 576 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: 576 B | 2 allocs: 576 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 | — |
| track/1. Float32/2K – track | 9 allocs: 944 B | 9 allocs: 944 B | 1.0 |
| track/1. Float32/2K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/2. L1 8sat/5K – track | 10 allocs: 4656 B | 10 allocs: 4656 B | 1.0 |
| track/2. L1 8sat/5K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/2. L1 8sat/5K – track! Int16 | 13 allocs: 1056 B | 13 allocs: 1056 B | 1.0 |
| track/2. L1 8sat/5K – track! OneBit | 45 allocs: 2736 B | 45 allocs: 2736 B | 1.0 |
| track/2. L1 8sat/5K – track!-threaded | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/2. L1 8sat/5K – track-threaded | 10 allocs: 4656 B | 10 allocs: 4656 B | 1.0 |
| track/3. E1B 4sat/25K – track | 10 allocs: 3056 B | 10 allocs: 3056 B | 1.0 |
| track/3. E1B 4sat/25K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/3. E1B 4sat/25K – track! Int16 | 5 allocs: 416 B | 5 allocs: 416 B | 1.0 |
| track/3. E1B 4sat/25K – track!-threaded | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/3. E1B 4sat/25K – track-threaded | 10 allocs: 3056 B | 10 allocs: 3056 B | 1.0 |
| track/4. 8L1+8E1B/25K – track | 26 allocs: 10464 B | 26 allocs: 10464 B | 1.0 |
| track/4. 8L1+8E1B/25K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/4. 8L1+8E1B/25K – track!-threaded | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/4. 8L1+8E1B/25K – track-threaded | 26 allocs: 10464 B | 26 allocs: 10464 B | 1.0 |
| track/5. multi-signal N=1/5K – track | 9 allocs: 944 B | 9 allocs: 944 B | 1.0 |
| track/5. multi-signal N=1/5K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/6. multi-signal N=2/5K – track | 9 allocs: 1408 B | 9 allocs: 1408 B | 1.0 |
| track/6. multi-signal N=2/5K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/7. multi-signal N=3/5K – track | 9 allocs: 1760 B | 9 allocs: 1760 B | 1.0 |
| track/7. multi-signal N=3/5K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/8. L1CA presync 2 blk – track! | 7 allocs: 320 B | 7 allocs: 320 B | 1.0 |
| track/8. L1CA presync 20 blk – track! | 7 allocs: 320 B | 7 allocs: 320 B | 1.0 |
| track/8. L1CA synced 2 blk – track! | 7 allocs: 320 B | 7 allocs: 320 B | 1.0 |
| track/8. L1CA synced 20 blk – track! | 7 allocs: 320 B | 7 allocs: 320 B | 1.0 |
| time_to_load | 196 allocs: 13984 B | 196 allocs: 13984 B | 1.0 |
A vector-tracking (VDFLL) estimator whose per-satellite loops are closed by an external navigation filter rather than per-satellite loop filters. Configuration-only; per-sat SatVectorPLLAndDLL state lives on TrackedSat and plugs into the shared per-sat update via _process_estimator_driver_signal. With vt_on unset a satellite runs the conventional FLL-assisted scalar PLL/DLL fallback. Once promoted (update_vt_states! / set_vt_on!), the navigation filter's NCO corrections close the loops: code Doppler follows code_freq_update directly, carrier_freq_update drives the carrier filter's FLL branch, and the DLL/FLL discriminators are accumulated for the filter to read (reset_code_discr_acc! / reset_carrier_discr_acc!). The state managers come in all-groups and group-scoped forms so PRNs can be disambiguated across constellations. Availability is the receiver's responsibility, not part of this state. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
c49f7f2 to
8ee2ef8
Compare
Benchmark Results (minimum time) — ubuntu-latestReporting the minimum over all samples (robust to shared-runner contention), not the median. Alternative backends vs Float32 (
|
| Scenario | F32 | I16 | 1b | 2b | ×I16 | ×1b | ×2b |
|---|---|---|---|---|---|---|---|
| 4-antenna @ 5 MHz | 11.4 μs | 6.45 μs | 4.81 μs | 9.0 μs | 1.77 ✅ | 2.38 ✅ | 1.27 ✅ |
| GPS L1CA, 8 sats @ 40 MHz | 339.0 μs | 135.0 μs | 65.4 μs | 118.0 μs | 2.51 ✅ | 5.17 ✅ | 2.87 ✅ |
| GPS L1CA, 8 sats @ 5 MHz | 50.3 μs | 26.9 μs | 17.8 μs | 26.3 μs | 1.87 ✅ | 2.82 ✅ | 1.92 ✅ |
| Galileo E1B, 4 sats @ 25 MHz | 125.0 μs | 58.2 μs | 2.15 ✅ | ||||
| dynamic taps @ 5 MHz (kernel) | 6.26 μs | 3.49 μs | 1.78 μs | 2.65 μs | 1.79 ✅ | 3.51 ✅ | 2.36 ✅ |
| multi-signal N=3 @ 5 MHz | 10.2 μs | 5.68 μs | 4.47 μs | 6.84 μs | 1.8 ✅ | 2.28 ✅ | 1.49 ✅ |
Time benchmarks (base vs PR head)
Ratio = 5f03cd2… / 8ee2ef8…: >1 means the PR is faster. ✅ ≥ 5 % faster,
| 5f03cd2… | 8ee2ef8… | 5f03cd2… / 8ee2ef8… | |
|---|---|---|---|
| downconvert and correlate/CPU/Float32 | 2.36 μs | 2.4 μs | 0.986 |
| downconvert and correlate/CPU/Float32 4ant | 4.4 μs | 4.43 μs | 0.993 |
| downconvert and correlate/CPU/Float64 | 2.93 μs | 2.78 μs | 1.06 ✅ |
| downconvert and correlate/CPU/Int16 | 2.47 μs | 2.51 μs | 0.984 |
| downconvert and correlate/CPU/Int16 4ant | 4.93 μs | 4.9 μs | 1.0 |
| downconvert and correlate/CPU/Int32 | 2.4 μs | 2.42 μs | 0.991 |
| fused kernel/1-ant dynamic taps | 2.29 μs | 2.3 μs | 0.998 |
| fused kernel/1-ant static taps | 1.91 μs | 1.91 μs | 1.0 |
| fused kernel/4-ant dynamic taps | 5.58 μs | 5.67 μs | 0.985 |
| fused kernel/4-ant static taps | 3.94 μs | 3.87 μs | 1.02 |
| fused tuple kernel/1-ant N=2 | 2.58 μs | 2.57 μs | 1.01 |
| fused tuple kernel/1-ant N=3 | 3.08 μs | 3.08 μs | 1.0 |
| fused tuple kernel/2-ant N=2 | 3.93 μs | 3.92 μs | 1.0 |
| fused tuple kernel/2-ant N=3 | 4.96 μs | 4.96 μs | 1.0 |
| fused tuple kernel/4-ant N=2 | 6.45 μs | 6.41 μs | 1.01 |
| fused tuple kernel/4-ant N=3 | 10.3 μs | 10.2 μs | 1.01 |
| track/1. Float32/2K – track | 2.62 μs | 2.63 μs | 0.998 |
| track/1. Float32/2K – track! | 2.64 μs | 2.64 μs | 0.999 |
| track/2. L1 8sat/5K – track | 48.6 μs | 48.5 μs | 1.0 |
| track/2. L1 8sat/5K – track! | 47.5 μs | 47.2 μs | 1.01 |
| track/2. L1 8sat/5K – track! Int16 | 27.0 μs | 27.0 μs | 1.0 |
| track/2. L1 8sat/5K – track! OneBit | 18.1 μs | 17.6 μs | 1.03 |
| track/2. L1 8sat/5K – track!-threaded | 47.9 μs | 47.5 μs | 1.01 |
| track/2. L1 8sat/5K – track-threaded | 48.5 μs | 48.2 μs | 1.01 |
| track/3. E1B 4sat/25K – track | 121.0 μs | 121.0 μs | 1.0 |
| track/3. E1B 4sat/25K – track! | 121.0 μs | 121.0 μs | 0.996 |
| track/3. E1B 4sat/25K – track! Int16 | 60.4 μs | 58.7 μs | 1.03 |
| track/3. E1B 4sat/25K – track!-threaded | 121.0 μs | 121.0 μs | 1.0 |
| track/3. E1B 4sat/25K – track-threaded | 121.0 μs | 121.0 μs | 0.999 |
| track/4. 8L1+8E1B/25K – track | 443.0 μs | 445.0 μs | 0.997 |
| track/4. 8L1+8E1B/25K – track! | 442.0 μs | 441.0 μs | 1.0 |
| track/4. 8L1+8E1B/25K – track!-threaded | 442.0 μs | 439.0 μs | 1.01 |
| track/4. 8L1+8E1B/25K – track-threaded | 443.0 μs | 443.0 μs | 1.0 |
| track/5. multi-signal N=1/5K – track | 6.21 μs | 6.22 μs | 0.998 |
| track/5. multi-signal N=1/5K – track! | 6.16 μs | 6.26 μs | 0.985 |
| track/6. multi-signal N=2/5K – track | 8.96 μs | 9.03 μs | 0.992 |
| track/6. multi-signal N=2/5K – track! | 9.14 μs | 9.13 μs | 1.0 |
| track/7. multi-signal N=3/5K – track | 11.4 μs | 11.4 μs | 1.0 |
| track/7. multi-signal N=3/5K – track! | 11.6 μs | 11.7 μs | 0.993 |
| track/8. L1CA presync 2 blk – track! | 11.7 μs | 11.8 μs | 0.993 |
| track/8. L1CA presync 20 blk – track! | 113.0 μs | 113.0 μs | 1.0 |
| track/8. L1CA synced 2 blk – track! | 11.7 μs | 11.7 μs | 0.996 |
| track/8. L1CA synced 20 blk – track! | 112.0 μs | 111.0 μs | 1.0 |
| time_to_load | 119.0 μs | 119.0 μs | 0.997 |
Memory benchmarks (base vs PR head)
Ratio = 5f03cd2… / 8ee2ef8… (bytes allocated): >1 means the PR allocates less. ✅ ≥ 5 % less, ∞/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).
| 5f03cd2… | 8ee2ef8… | 5f03cd2… / 8ee2ef8… | |
|---|---|---|---|
| downconvert and correlate/CPU/Float32 | 2 allocs: 576 B | 2 allocs: 576 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: 576 B | 2 allocs: 576 B | 1.0 |
| downconvert and correlate/CPU/Int16 | 2 allocs: 576 B | 2 allocs: 576 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: 576 B | 2 allocs: 576 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 | — |
| track/1. Float32/2K – track | 9 allocs: 944 B | 9 allocs: 944 B | 1.0 |
| track/1. Float32/2K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/2. L1 8sat/5K – track | 10 allocs: 4536 B | 10 allocs: 4536 B | 1.0 |
| track/2. L1 8sat/5K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/2. L1 8sat/5K – track! Int16 | 13 allocs: 1056 B | 13 allocs: 1056 B | 1.0 |
| track/2. L1 8sat/5K – track! OneBit | 45 allocs: 2736 B | 45 allocs: 2736 B | 1.0 |
| track/2. L1 8sat/5K – track!-threaded | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/2. L1 8sat/5K – track-threaded | 10 allocs: 4536 B | 10 allocs: 4536 B | 1.0 |
| track/3. E1B 4sat/25K – track | 10 allocs: 2808 B | 10 allocs: 2808 B | 1.0 |
| track/3. E1B 4sat/25K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/3. E1B 4sat/25K – track! Int16 | 5 allocs: 416 B | 5 allocs: 416 B | 1.0 |
| track/3. E1B 4sat/25K – track!-threaded | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/3. E1B 4sat/25K – track-threaded | 10 allocs: 2808 B | 10 allocs: 2808 B | 1.0 |
| track/4. 8L1+8E1B/25K – track | 26 allocs: 10352 B | 26 allocs: 10352 B | 1.0 |
| track/4. 8L1+8E1B/25K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/4. 8L1+8E1B/25K – track!-threaded | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/4. 8L1+8E1B/25K – track-threaded | 26 allocs: 10352 B | 26 allocs: 10352 B | 1.0 |
| track/5. multi-signal N=1/5K – track | 9 allocs: 944 B | 9 allocs: 944 B | 1.0 |
| track/5. multi-signal N=1/5K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/6. multi-signal N=2/5K – track | 9 allocs: 1408 B | 9 allocs: 1408 B | 1.0 |
| track/6. multi-signal N=2/5K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/7. multi-signal N=3/5K – track | 9 allocs: 1760 B | 9 allocs: 1760 B | 1.0 |
| track/7. multi-signal N=3/5K – track! | 0 allocs: 0 B | 0 allocs: 0 B | — |
| track/8. L1CA presync 2 blk – track! | 7 allocs: 320 B | 7 allocs: 320 B | 1.0 |
| track/8. L1CA presync 20 blk – track! | 7 allocs: 320 B | 7 allocs: 320 B | 1.0 |
| track/8. L1CA synced 2 blk – track! | 7 allocs: 320 B | 7 allocs: 320 B | 1.0 |
| track/8. L1CA synced 20 blk – track! | 7 allocs: 320 B | 7 allocs: 320 B | 1.0 |
| time_to_load | 145 allocs: 11216 B | 145 allocs: 11216 B | 1.0 |
Summary
Adds
VectorPLLAndDLL, a vector-tracking (VDFLL) Doppler estimator whoseper-satellite carrier and code loops are closed centrally by an external
navigation filter (e.g. GNSSReceiver.jl) instead of by per-satellite loop
filters. It slots into the existing
AbstractDopplerEstimatorextensionpoints, so it works with the current
SignalGroups/TrackedSatarchitecture without touching the signal path.
How it works
TrackedSatas aSatVectorPLLAndDLL, seeded viainit_estimator_state.The per-sat update plugs into the shared
_update_tracked_sat_dopplerthrough a
_process_estimator_driver_signalmethod dispatched on thestate type — no changes to downconvert/correlate.
vt_onunset runs the conventionalFLL-assisted scalar PLL/DLL, auto-sized with the same loop bandwidths as
ConventionalAssistedPLLAndDLL, until it is promoted into the vector loop.close the loops: the code Doppler follows
code_freq_updatedirectly (codeloop filter bypassed) and
carrier_freq_updatedrives the FLL branch ofthe carrier filter while the PLL branch still runs on the satellite's own
discriminator. The DLL/FLL discriminator outputs are accumulated for the
navigation filter to consume.
Receiver-facing API
Membership / NCO-feedback managers, with all-groups and group-scoped forms
(the group selector disambiguates PRNs across constellations):
update_vt_states!,set_vt_on!,set_code_freq_updates!,set_carrier_freq_updates!.update_vt_states!promotion isone-directional; use
set_vt_on!to demote. Accumulator resets(
reset_code_discr_acc!,reset_carrier_discr_acc!) reset every vector-loopsatellite.
mean_code_discriminator/mean_carrier_discriminatorread the(count, sum)accumulators through the averaging convention in one place(
sum / count, ornothingwhen empty) so consumers don't re-implement it.Only state the navigation filter consumes is kept — availability and
signal-amplitude tracking are the receiver's responsibility and are not part
of this estimator's state. NCO corrections are zeroed on release via
reset_loop_filters!(documented on it), so no separate reset is exported.Notes
steady state (0 bytes, matching the conventional estimator); the state
managers are allocation-free.
docs/src/vector_tracking.mdpage, registered in the docsbuild.
test/vector_pll_and_dll.jlcovers the per-sat state, seeding,scalar-fallback parity with
ConventionalAssistedPLLAndDLL, vector loopclosure, the state managers (including group-scoped multi-constellation
addressing), and
reset_loop_filters!. Full suite green; docs build clean.Follow-up
The GNSSReceiver.jl side that drives this estimator lands separately; its
sc/vector-trackingbranch reads/sets availability on the estimator state,which now lives receiver-side and will be reconciled there.
🤖 Generated with Claude Code