feat(gps): add timer-based assisted reacquisition
Remove the fixed pseudorange start offset, preserve per-PRN navigation assistance across RF loss and power cycles, and seed coherent navigation measurement phase on reacquisition. Add full LNAV almanac visibility scheduling, power-safe QSPI journaling, RF-domain reset isolation, and PPS Gray CDC hardening with Linux, sanitizer, RTL, and end-to-end coverage.
This commit is contained in:
@@ -30,11 +30,13 @@ davranışlarını taklit eder:
|
||||
|
||||
Model ve C ayni 8 ns integer mailbox yuvarlamasini kullanir.
|
||||
- `gps_unwrap_corrected_abs_sample()` ile aynı signed modulo-32 unwrap.
|
||||
- `gps_ip.c` ile aynı pseudorange datum'u:
|
||||
- `gps_ip.c` ile aynı free-counter pseudorange datum'u:
|
||||
|
||||
```text
|
||||
precise_ms_i = abs_i / 16367.981 - reference + 68.802
|
||||
P_i = precise_ms_i * c / 1000 + satellite_clock_i * c
|
||||
S_i = unwrap32(ABS_SAMPLE_i) - REM_CODE_PHASE_i / 1.023e6
|
||||
P_i/c = (S_i - S_ref) / 16.367981e6
|
||||
- (transmit_i - transmit_ref)
|
||||
+ satellite_clock_i
|
||||
```
|
||||
|
||||
- `NavEquations.c` ile aynı dört state:
|
||||
@@ -132,13 +134,13 @@ Mevcut observation denkleminde WLS bias doğrudan PPS fazı değildir. İlk
|
||||
ilişki:
|
||||
|
||||
```text
|
||||
b/c = STARTOFFSET - signal_flight_time_ref
|
||||
b/c = -signal_flight_time_ref + satellite_clock_ref
|
||||
```
|
||||
|
||||
Bu nedenle doğru GPS referansı:
|
||||
|
||||
```text
|
||||
GPS_TIME_AT_CAPTURE = transmit_epoch_TOW + STARTOFFSET - b/c
|
||||
GPS_TIME_AT_CAPTURE = transmit_epoch_TOW - b/c
|
||||
```
|
||||
|
||||
ile kurulur ve referans kanala ait `tracking_tick_125` ile eşlenir. Referans
|
||||
@@ -176,7 +178,7 @@ teslim edilen subframe için:
|
||||
|
||||
```text
|
||||
transmit_epoch_TOW = HOW_TOW + POSTPONE_TOW
|
||||
GPS_TIME_AT_CAPTURE = transmit_epoch_TOW + STARTOFFSET - b/c
|
||||
GPS_TIME_AT_CAPTURE = transmit_epoch_TOW - b/c
|
||||
```
|
||||
|
||||
`POSTPONE_TOW` signal flight time veya receiver clock bias değildir; hardware
|
||||
@@ -205,8 +207,8 @@ bu üç etkiyi ayrı gösterir.
|
||||
ilk hardware delivery epoch'u için 180 ms sabit readout düzeltmesi ayrıca
|
||||
eklenir.
|
||||
3. İlk coherent WLS çözümünde minimum corrected-ABS kanalı datum olarak
|
||||
seçilir; aynı SVID mevcut kaldığı sürece onun uçuş süresi
|
||||
`STARTOFFSET - b/c` ile elde edilir.
|
||||
seçilir; aynı SVID mevcut kaldığı sürece onun uçuş süresi `-b/c` ile elde
|
||||
edilir (referans satellite clock correction'i dahil).
|
||||
4. Bu receive GPS zamanı aynı kanalın yakalanmış 125 MHz timestamp'ıyla
|
||||
eşlenerek PPS accumulator coarse-align edilir.
|
||||
5. Sonraki çözümler aynı eşleşmeden residual phase error üretip PI'yi besler.
|
||||
|
||||
@@ -300,10 +300,10 @@ def page_executive(report: Report) -> None:
|
||||
0.07,
|
||||
y,
|
||||
"Bu projede konum WLS'i doğru çalışırken elde edilen dördüncü state, doğrudan "
|
||||
"125 MHz receiver clock fazı değildir. Pseudorange'lar her epoch'ta minimum "
|
||||
"ABS_SAMPLE ile normalize edildiği için RF sayacının mutlak fazı observation'dan "
|
||||
"çıkar. WLS bias; 68.802 ms sentetik datum ile minimum kanalın gözlenen uçuş "
|
||||
"süresi arasındaki ortak farkı taşır.",
|
||||
"125 MHz receiver clock fazı değildir. Pseudorange'lar her epoch'ta corrected RF "
|
||||
"sample ve satellite transmit-time farklarıyla kurulduğu için sayacın mutlak fazı "
|
||||
"observation'dan çıkar. WLS bias, referans kanalın negatif uçuş süresini ve "
|
||||
"satellite clock correction'ini taşıyan göreli datumdur.",
|
||||
width=100,
|
||||
fontsize=10.2,
|
||||
)
|
||||
@@ -556,7 +556,7 @@ def page_project_map(report: Report) -> None:
|
||||
(0.77, 0.54, 0.17, 0.10, "Tracking ISR", "event queue +\nshadow counter", LIGHT, NAVY),
|
||||
(0.54, 0.54, 0.18, 0.10, "Linked-list state", "TOW owner\nsequence + sample", LIGHT, NAVY),
|
||||
(0.28, 0.54, 0.20, 0.10, "Coherent snapshot", "25 ms window\nmodulo unwrap", PALE_GREEN, GREEN),
|
||||
(0.06, 0.54, 0.17, 0.10, "Pseudorange", "minimum +\n68.802 ms", PALE_GREEN, GREEN),
|
||||
(0.06, 0.54, 0.17, 0.10, "Pseudorange", "ΔS_RF −\nΔT transmit", PALE_GREEN, GREEN),
|
||||
(0.06, 0.34, 0.17, 0.10, "Satellite model", "TOW + 180 ms\nephemeris/clock", PALE_BLUE, BLUE),
|
||||
(0.32, 0.34, 0.23, 0.10, "Weighted LS", "[X Y Z b]\niono + tropo + URA/el", PALE_GREEN, GREEN),
|
||||
(0.64, 0.34, 0.30, 0.10, "Bugünkü çıkış", "position log + static last_solution[4]\nclock bias dışarı taşınmıyor", "#FBEAEA", RED),
|
||||
@@ -651,14 +651,14 @@ def page_pseudorange(report: Report) -> None:
|
||||
y = 0.84
|
||||
ax.text(0.08, y, "1 Fractional RF sample düzeltmesi", fontsize=11, color=NAVY, weight="bold")
|
||||
y = report.equation(ax, y - 0.025, r"A_i^{corr}=A_i^{raw}-\frac{REM_i}{1.023\times10^6}")
|
||||
ax.text(0.08, y, "2 Wrap-safe coherent unwrap ve minimum seçimi", fontsize=11, color=NAVY, weight="bold")
|
||||
y = report.equation(ax, y - 0.025, r"A_{min}=\min_i\left(A_i^{corr}\right)")
|
||||
ax.text(0.08, y, "3 C kodundaki sentetik propagation datum'u", fontsize=11, color=NAVY, weight="bold")
|
||||
ax.text(0.08, y, "2 Corrected RF sample ve transmit-time farkı", fontsize=11, color=NAVY, weight="bold")
|
||||
y = report.equation(ax, y - 0.025, r"\Delta t_i=\frac{S_i-S_{ref}}{16.367981\,MHz}-(T_{tx,i}-T_{tx,ref})")
|
||||
ax.text(0.08, y, "3 Offset-free göreli pseudorange datum'u", fontsize=11, color=NAVY, weight="bold")
|
||||
y = report.equation(
|
||||
ax,
|
||||
y - 0.025,
|
||||
r"P'_i=c\left[\frac{A_i^{corr}-A_{min}}{16.368\,MHz}+S\right]+c\,\delta t_{SV,i}",
|
||||
"S = 68.802 ms; gps_ip.c içinde ms formunda hesaplanır",
|
||||
r"P'_i=c\left[\Delta t_i+\delta t_{SV,i}\right]",
|
||||
"Free-running counter başlangıcı ve ortak uzun-dönem drift farkta yok olur",
|
||||
face=PALE_ORANGE,
|
||||
)
|
||||
report.box(
|
||||
@@ -667,8 +667,8 @@ def page_pseudorange(report: Report) -> None:
|
||||
0.17,
|
||||
0.40,
|
||||
0.14,
|
||||
"Minimum neden var?",
|
||||
"Raw ABS_SAMPLE'ın receiver açılışına bağlı büyük ortak sayısını çıkarır. Uydular arası arrival-time farkları korunur; konum geometrisi bu farklarda bulunur.",
|
||||
"Referans neden var?",
|
||||
"RF counter'ın receiver açılışına bağlı ortak sayısını wrap-safe çıkarır. Uydular arası arrival/transmit-time farkları korunur; konum geometrisi bu farklarda bulunur.",
|
||||
face=PALE_BLUE,
|
||||
)
|
||||
report.box(
|
||||
@@ -677,8 +677,8 @@ def page_pseudorange(report: Report) -> None:
|
||||
0.17,
|
||||
0.40,
|
||||
0.14,
|
||||
"68.802 ms neden var?",
|
||||
"Göreli range'leri fiziksel 20–26 bin km büyüklüğüne taşır ve WLS common bias'ı sıfıra yakın tutar. Yeni bilgi eklemez; datum seçer.",
|
||||
"Sabit offset neden yok?",
|
||||
"WLS dördüncü state göreli datumun büyük ortak bias'ını zaten çözer. Fiziksel konum için kalibre edilmiş nominal uçuş süresi gerekmez.",
|
||||
face=PALE_GREEN,
|
||||
edge="#9BC9AF",
|
||||
title_color=GREEN,
|
||||
@@ -1136,7 +1136,7 @@ def page_sim_method(report: Report) -> None:
|
||||
("Navigation", "Ortak 20 ms transmit-bit epoch; NAV_SOLVE_TIMER=10 → 200 ms"),
|
||||
("Satellite", "Sentetik gerçekçi ECEF/az-el geometrisi, satellite clock, path delay"),
|
||||
("Corrections", "Earth rotation, Klobuchar, aktif C troposfer modeli, URA/elevation noise"),
|
||||
("Firmware", "Modulo unwrap, minimum + 68.802 ms, clock correction, cached four-state WLS"),
|
||||
("Firmware", "Raw-counter/transmit-time difference, clock correction, cached four-state WLS"),
|
||||
("Latency", "Satellite arrival span, WLS compute, scheduler stall, APB jitter"),
|
||||
("PPS", "125 MHz raw oscillator, coarse mapping, timestamped/untagged PI, 8 ns edge grid"),
|
||||
]
|
||||
@@ -1264,7 +1264,7 @@ def page_verification(report: Report) -> None:
|
||||
fig, ax = report.new_page("Doğrulama matrisi ve beklenen sonuç")
|
||||
tests = [
|
||||
("ABS common +5 ms", "XYZ aynı; normalized b aynı", "Observation common phase iptalini kanıtlar"),
|
||||
("STARTOFFSET +1 ms", "XYZ aynı; b +c×1 ms", "Datum dönüşüm işaretini kanıtlar"),
|
||||
("RF counter common shift", "XYZ ve göreli b aynı", "RF counter origin iptalini kanıtlar"),
|
||||
("32-bit ABS wrap", "Pseudorange süreklidir", "Signed modulo unwrap"),
|
||||
("Reference SVID değişimi", "Raw b step; mapped GPS time continuous", "Min datum ownership"),
|
||||
("APB +50 ms delay", "Timestamped PPS fazı değişmez", "Epoch association"),
|
||||
@@ -1340,7 +1340,7 @@ def page_conclusion(report: Report) -> None:
|
||||
("1", "Raw clocks", "16.368 MHz measurement + 125 MHz monotonic counter"),
|
||||
("2", "Paired capture", "ABS / REM / C125 / sequence aynı event"),
|
||||
("3", "Coherent WLS", "minimum datum + all-satellite [XYZ b]"),
|
||||
("4", "GPS receive time", "T_TX(epoch-aligned) + 68.802 ms − b/c"),
|
||||
("4", "GPS receive time", "T_TX(epoch-aligned) − b/c"),
|
||||
("5", "Atomic mapping", "(C125,k, TGPS,k, seq) shadow + commit"),
|
||||
("6", "Disciplined PPS", "immutable raw counter + low-bandwidth PI accumulator"),
|
||||
]
|
||||
@@ -1414,7 +1414,7 @@ def generate(output_pdf: Path) -> None:
|
||||
report,
|
||||
"İlk HOW neden PPS receive-now değildir?",
|
||||
required_plots[4],
|
||||
"Sentetik acquisition/subframe Monte Carlo: mevcut hardware delivery, dokuzuncu takip eden bit nedeniyle HOW sınırından 180 ms ve signal flight time kadar sonradır. HOW +180 ms +68.802 ms kaba yaklaşım bırakır; WLS flight-time + paired timestamp doğru handover'dır.",
|
||||
"Sentetik acquisition/subframe Monte Carlo: mevcut hardware delivery, dokuzuncu takip eden bit nedeniyle HOW sınırından 180 ms ve signal flight time kadar sonradır. HOW +180 ms tek başına uçuş süresi kadar hata bırakır; offset-free WLS flight-time + paired timestamp doğru handover'dır.",
|
||||
)
|
||||
plot_page(
|
||||
report,
|
||||
|
||||
@@ -5,7 +5,8 @@ mirrors the conventions used by gps_system/src/gps_ip.c and NavEquations.c:
|
||||
|
||||
* corrected_abs = raw_abs - rem_code_phase / CODE_FREQ_BASIS
|
||||
* modulo-32 ABS_SAMPLE values are unwrapped around the newest channel
|
||||
* obs_i = c * ((abs_i - min(abs)) / SAMPLES_PER_CODE + STARTOFFSET) / 1000
|
||||
* obs_i/c = (corrected_abs_i - corrected_abs_ref) / RF_HZ
|
||||
- (transmit_i - transmit_ref)
|
||||
* satellite clock correction is added to the observation
|
||||
* [x, y, z, common_bias_m] are solved together with URA/elevation weights
|
||||
|
||||
@@ -27,8 +28,6 @@ RF_HZ = 16_367_981.0
|
||||
TRACKING_HZ = 125_000_000.0
|
||||
SAMPLES_PER_CODE = 16_367.981 # physical RF samples/ms used by the C code
|
||||
CODE_FREQ_BASIS = 1.023e6
|
||||
START_OFFSET_MS = 68.802
|
||||
START_OFFSET_S = START_OFFSET_MS / 1000.0
|
||||
NAV_SOLVE_PERIOD_S = 10 * 0.020
|
||||
POSTPONE_TOW_S = 0.180
|
||||
OMEGA_EARTH = 7.2921151467e-5
|
||||
@@ -680,10 +679,14 @@ class ReceiverEpochSimulator:
|
||||
corrected_abs = np.asarray(
|
||||
[measurement.corrected_abs_unwrapped for measurement in measurements]
|
||||
)
|
||||
ps_tvl_ms = corrected_abs / SAMPLES_PER_CODE
|
||||
reference_index = int(np.argmin(ps_tvl_ms))
|
||||
precise_tx_ms = ps_tvl_ms - ps_tvl_ms[reference_index] + START_OFFSET_MS
|
||||
observations_m = precise_tx_ms * (C / 1000.0)
|
||||
reference_index = int(np.argmin(corrected_abs))
|
||||
# All channels in this synthetic epoch share one transmit time. Mirror
|
||||
# firmware's wrap-safe corrected RF-sample difference. The fractional
|
||||
# code residue removes the 61 ns integer-sample quantization; the
|
||||
# separately captured 125 MHz counter is used only for PPS epoch mapping.
|
||||
observations_m = (
|
||||
corrected_abs - corrected_abs[reference_index]
|
||||
) * (C / RF_HZ)
|
||||
observations_m += C * np.asarray(
|
||||
[measurement.satellite_clock_s for measurement in measurements]
|
||||
)
|
||||
@@ -711,7 +714,6 @@ class ReceiverEpochSimulator:
|
||||
gps_reference_estimate = (
|
||||
self.gps_tow_start_s
|
||||
+ transmit_true_s
|
||||
+ START_OFFSET_S
|
||||
- wls.clock_bias_m / C
|
||||
)
|
||||
gps_reference_true = self.gps_tow_start_s + reference.arrival_true_s
|
||||
|
||||
@@ -22,7 +22,6 @@ try:
|
||||
DEFAULT_IONO,
|
||||
POSTPONE_TOW_S,
|
||||
RF_HZ,
|
||||
START_OFFSET_S,
|
||||
TRACKING_HZ,
|
||||
ReceiverEpochSimulator,
|
||||
build_clock_trajectory,
|
||||
@@ -41,7 +40,6 @@ except ImportError: # allow: python3 pps_python/simulate_pps.py
|
||||
DEFAULT_IONO,
|
||||
POSTPONE_TOW_S,
|
||||
RF_HZ,
|
||||
START_OFFSET_S,
|
||||
TRACKING_HZ,
|
||||
ReceiverEpochSimulator,
|
||||
build_clock_trajectory,
|
||||
@@ -321,7 +319,7 @@ def simulate_startup_tow(
|
||||
"first_flight_s": first_flight,
|
||||
# Parsed HOW labels transmit subframe time, not receive-now.
|
||||
"how_as_receive_error_s": -(POSTPONE_TOW_S + first_flight),
|
||||
"how_plus_pipeline_startoffset_error_s": START_OFFSET_S - first_flight,
|
||||
"how_plus_pipeline_without_wls_error_s": -first_flight,
|
||||
"wls_aligned_error_s": wls_error,
|
||||
# Old receive reference written as if it were valid at APB-now.
|
||||
"apb_now_error_s": wls_error - sampled_latency,
|
||||
@@ -349,13 +347,13 @@ def plot_startup_tow(output_dir: Path, startup: dict[str, np.ndarray]) -> None:
|
||||
axes[0, 1].grid(True, alpha=0.3)
|
||||
|
||||
axes[1, 0].hist(
|
||||
startup["how_plus_pipeline_startoffset_error_s"] * 1.0e3,
|
||||
startup["how_plus_pipeline_without_wls_error_s"] * 1.0e3,
|
||||
bins=35,
|
||||
color="tab:orange",
|
||||
alpha=0.8,
|
||||
)
|
||||
axes[1, 0].set_xlabel("Phase error [ms]")
|
||||
axes[1, 0].set_title("Provisional: HOW + 180 ms + 68.802 ms")
|
||||
axes[1, 0].set_title("Still wrong: HOW + 180 ms without WLS flight time")
|
||||
axes[1, 0].grid(True, alpha=0.3)
|
||||
|
||||
axes[1, 1].hist(
|
||||
@@ -398,7 +396,7 @@ def build_summary(
|
||||
f"derived GPS reference RMS error: {finite_rms(timing_error)*1e9:.3f} ns",
|
||||
f"capture-to-commit delay mean/p99: {np.mean(latency)*1e3:.3f} / {np.percentile(latency, 99)*1e3:.3f} ms",
|
||||
f"first HOW used as receive-now, median phase error: {np.median(startup['how_as_receive_error_s'])*1e3:.3f} ms",
|
||||
f"first HOW + 180 ms + 68.802 ms, median phase error: {np.median(startup['how_plus_pipeline_startoffset_error_s'])*1e3:.3f} ms",
|
||||
f"first HOW + 180 ms without WLS, median phase error: {np.median(startup['how_plus_pipeline_without_wls_error_s'])*1e3:.3f} ms",
|
||||
]
|
||||
for start, stop, label in [
|
||||
(0.0, 20.0, "4 satellites"),
|
||||
@@ -426,8 +424,8 @@ def build_summary(
|
||||
[
|
||||
"",
|
||||
"Interpretation:",
|
||||
"- bias/c is millisecond-scale because it contains the 68.802 ms synthetic datum and the minimum satellite path.",
|
||||
"- epoch-aligned TOW (first delivery: HOW + 180 ms) + STARTOFFSET - bias/c reconstructs GPS time at the paired timestamp.",
|
||||
"- bias/c is millisecond-scale because the timer-difference datum contains the negative reference-satellite path.",
|
||||
"- epoch-aligned TOW (first delivery: HOW + 180 ms) - bias/c reconstructs GPS time at the paired timestamp.",
|
||||
"- treating that old reference as valid at APB arrival converts software latency into PPS phase error.",
|
||||
"- timestamping removes that phase misassociation; control application delay remains only as loop delay.",
|
||||
"- baremetal applies the fixed 180 ms readout correction once; the PPS peripheral must not add it again.",
|
||||
|
||||
Reference in New Issue
Block a user