fix(gps): adapt Doppler bandwidth from board validation
This commit is contained in:
@@ -125,6 +125,20 @@ grafik bu farkı açıkça gösterir.
|
||||
|
||||
Raw `b/c` değerini PI phase error olarak doğrudan kullanmak doğru değildir.
|
||||
|
||||
Klasik GNSS denklemindeki `c*delta_t_r` clock bias ile buradaki `b` aynı datum
|
||||
değildir: capture'ların ortak receiver zamanı önceden çıkarılmıştır. Doppler
|
||||
velocity WLS'nin dördüncü state'i uydulara ortak receiver clock/frequency
|
||||
drift'idir; fakat bu göreli `b` değerinin doğrudan türevi olarak kullanılmaz.
|
||||
PPS modeli datumun değişimini seçilen referans uydunun geometrik range-rate'i,
|
||||
broadcast satellite clock drift'i ve Doppler-WLS receiver velocity'sinden
|
||||
hesaplar:
|
||||
|
||||
```text
|
||||
bias_drift =
|
||||
-reference_geometric_range_rate
|
||||
+ c * reference_satellite_clock_drift
|
||||
```
|
||||
|
||||
## İlk HOW ve PPS başlangıcı
|
||||
|
||||
`parse_subframe()` HOW Z-count alanını `6 s` ile çarpıp `tow.sec` yapar. Bu,
|
||||
@@ -256,6 +270,16 @@ NaN, dört uydunun altına düşme, eşik sınırları ve histerezisi;
|
||||
`test_gps_pps_time_filter.c` drift prediction, gap/re-anchor ve hard-outlier
|
||||
reddini sınar.
|
||||
|
||||
2026-07-26 Genesys2 logu ile kalibre edilen aynı-datum A/B testinde position
|
||||
WLS clock-bias gürültüsü `15.87 m`, referans-datum drift'i yaklaşık
|
||||
`291.75 m/s`, Doppler drift gürültüsü `3.47 m/s` ve oscillator hatası
|
||||
`1.592 ppm` olarak modele verildi. Linux C fixed-point testinde Doppler
|
||||
predictor clock-bias RMS'i `2.75 m`, yalnız son bias'i tutan fallback ise
|
||||
`21.22 m` verdi. Python modeli sekiz seed'de hem phase RMS hem 125 MHz grid'e
|
||||
quantize edilmiş edge-jitter p95 için predictor'un fallback'ten iyi kalmasını
|
||||
regresyon koşulu yapar. Kart status register phase'i fiziksel pin jitter'i
|
||||
değildir; `15-20 ns` çıkış hedefi son olarak scope/TIC ile ölçülmelidir.
|
||||
|
||||
İki zaman eşiği bilinçli olarak ayrıdır: iki saniyelik watchdog gözlem yokken
|
||||
PPS validliğini holdover'a indirir; aynı referans SVID için Doppler-predicted
|
||||
clock-bias ve PI state'i 30 saniyeye kadar korunur. Modelde 15 saniyelik WLS
|
||||
|
||||
@@ -59,6 +59,90 @@ def noisy_observations(
|
||||
|
||||
|
||||
class AdaptivePpsTests(unittest.TestCase):
|
||||
def test_board_calibrated_doppler_predictor_reduces_pps_error(self):
|
||||
"""Genesys2 WLS statistics: predictor-on must beat fallback-only."""
|
||||
quality_metrics = QualityMetrics(
|
||||
1.50, 2.92, 15.87, 40.3, 34.8, 0.35, 0.23, 7
|
||||
)
|
||||
aided_phase_rms = []
|
||||
fallback_phase_rms = []
|
||||
aided_edge_jitter = []
|
||||
fallback_edge_jitter = []
|
||||
for seed in range(8):
|
||||
rng = random.Random(7300 + seed)
|
||||
observations = []
|
||||
for update in range(900):
|
||||
time_s = 0.2 * update
|
||||
long_omega = 2.0 * math.pi / 75.0
|
||||
short_omega = 2.0 * math.pi / 19.0
|
||||
varying_drift_mps = (
|
||||
5.0 * math.sin(long_omega * time_s)
|
||||
+ 2.0 * math.sin(short_omega * time_s)
|
||||
)
|
||||
varying_bias_m = (
|
||||
5.0 * (1.0 - math.cos(long_omega * time_s)) / long_omega
|
||||
+ 2.0 * (1.0 - math.cos(short_omega * time_s))
|
||||
/ short_omega
|
||||
)
|
||||
# 15.87 m / c = 52.94 ns; 3.47 m/s is the measured
|
||||
# board standard deviation of the reference bias drift.
|
||||
noise_s = rng.gauss(0.0, 15.87 / 299_792_458.0)
|
||||
if update % 113 == 0 and update != 0:
|
||||
noise_s += rng.choice((-1.0, 1.0)) * 120.0e-9
|
||||
observations.append(
|
||||
Observation(
|
||||
time_s,
|
||||
noise_s,
|
||||
varying_drift_mps + rng.gauss(0.0, 3.47),
|
||||
quality_metrics,
|
||||
True,
|
||||
-291.75,
|
||||
1,
|
||||
varying_bias_m,
|
||||
)
|
||||
)
|
||||
aided = run_observations(
|
||||
observations,
|
||||
finish_time_s=180.0,
|
||||
oscillator_ppm=1.592,
|
||||
)
|
||||
fallback = run_observations(
|
||||
[
|
||||
Observation(
|
||||
item.time_s,
|
||||
item.timing_error_s,
|
||||
item.clock_drift_error_mps,
|
||||
item.quality,
|
||||
False,
|
||||
item.reference_range_rate_mps,
|
||||
item.reference_id,
|
||||
item.reference_datum_offset_m,
|
||||
)
|
||||
for item in observations
|
||||
],
|
||||
finish_time_s=180.0,
|
||||
oscillator_ppm=1.592,
|
||||
)
|
||||
aided_phase_rms.append(aided.phase_rms(after_s=60.0))
|
||||
fallback_phase_rms.append(fallback.phase_rms(after_s=60.0))
|
||||
aided_edge_jitter.append(
|
||||
aided.one_second_edge_jitter_rms(after_s=60.0)
|
||||
)
|
||||
fallback_edge_jitter.append(
|
||||
fallback.one_second_edge_jitter_rms(after_s=60.0)
|
||||
)
|
||||
self.assertEqual(aided.unlock_events, 0)
|
||||
|
||||
self.assertLess(
|
||||
sum(aided_phase_rms) / len(aided_phase_rms),
|
||||
0.35 * sum(fallback_phase_rms) / len(fallback_phase_rms),
|
||||
)
|
||||
self.assertLess(
|
||||
sum(aided_edge_jitter) / len(aided_edge_jitter),
|
||||
0.70 * sum(fallback_edge_jitter) / len(fallback_edge_jitter),
|
||||
)
|
||||
self.assertLess(max(aided_phase_rms), 30.0e-9)
|
||||
|
||||
def test_quality_hysteresis_rejects_metric_chatter(self):
|
||||
selector = QualitySelector()
|
||||
for _ in range(7):
|
||||
|
||||
Reference in New Issue
Block a user