fix(pps): align firmware TOW with current RTL

This commit is contained in:
2026-07-28 21:53:33 +03:00
parent 988f9686e7
commit a697284bff
18 changed files with 1002 additions and 960 deletions
+17 -10
View File
@@ -76,9 +76,9 @@ python3 -m unittest pps_end_to_end_validation/test_pps_end_to_end.py
```text
scenario bits correction reference error DUT - trusted PPS
current-rtl/180ms 9 180 ms -0.000032 ms +0.000032 ms
current-rtl/160ms 9 160 ms -20.000032 ms +20.000032 ms
current-bit/160ms 8 160 ms -0.000032 ms +0.000032 ms
current-rtl/180ms 9 180 ms -0.000032 ms +0.000032 ms
```
Kısa veya farklı PI koşusu:
@@ -167,10 +167,11 @@ bias_drift =
uydunun transmit subframe sınırı etiketidir. Full-subframe interrupt'ının
receiver'a ulaştığı an değildir.
Mevcut hardware, subframe doğrulama/parity akışında takip eden subframe'den
8 bit daha bekler. Navigation data 50 bit/s olduğu için bu sabit readout
gecikmesi `8 × 20 ms = 160 ms` olur ve yazılımda `POSTPONE_TOW=0.160` ile
temsil edilir. Bu yüzden ilk teslim edilen subframe için:
Mevcut hardware, subframe doğrulama/parity akışında eski shift register'i
okuduğu için takip eden subframe'in dokuzuncu bitinde teslim yapar. Navigation
data 50 bit/s olduğu için sabit readout gecikmesi `9 × 20 ms = 180 ms` olur ve
production yazılımda `POSTPONE_TOW=0.180` ile temsil edilir. Bu yüzden ilk
teslim edilen subframe için:
```text
transmit_epoch_TOW = HOW_TOW + POSTPONE_TOW
@@ -187,6 +188,10 @@ epoch-aligned transmit time'ı baremetal navigation katmanında tam bir kez
oluşturmalıdır. PPS APB periferiki kendisine verilen GPS reference time'a
`POSTPONE_TOW` değerini tekrar eklememelidir.
Tracking RTL ileride current-bit candidate window ile sekizinci bitte validate
edecek şekilde düzeltilirse aynı release'de firmware değeri 160 ms'ye geri
alınmalıdır; RTL ve firmware düzeltmeleri birbirinden bağımsız uygulanamaz.
İlk HOW'un hangi kanaldan geleceği sadece uydunun yakınlığına bağlı değildir.
Acquisition tamamlanma anı, bit lock ve bir sonraki tam subframe sınırı çoğu
zaman propagation delay farkından daha baskındır. Monte Carlo startup modeli
@@ -195,8 +200,9 @@ bu üç etkiyi ayrı gösterir.
Önerilen ownership:
1. Raw 125 MHz counter resetten itibaren free-running çalışır.
2. İlk geçerli HOW week/6-second mutlak zaman etiketini sağlar; ilk hardware
delivery epoch'u için 160 ms sabit readout düzeltmesi ayrıca eklenir.
2. İlk geçerli HOW week/6-second mutlak zaman etiketini sağlar; mevcut RTL'nin
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.
@@ -325,9 +331,10 @@ yapar.
çözümde minimum corrected ABS kanalı seçiliyor ve aynı SVID coherent
snapshot'ta kaldığı sürece datum sabit tutuluyor. O kanalın TOW'u ve aynı
nav-solve olayındaki C125 capture'ı birlikte taşınıyor.
5. `POSTPONE_TOW=0.160`, hardware'ın mevcut subframe'i doğrulamak için takip
eden subframe'den 8 bit daha beklemesinden oluşan sabit readout gecikmesidir.
Uydu konum/saat epoch'u buna göre 160 ms ileri alınır. Bu değer signal
5. `POSTPONE_TOW=0.180`, hardware'ın mevcut subframe'i eski shift register
üzerinden doğrulayıp dokuzuncu takip eden bitte teslim etmesinden oluşan
sabit readout gecikmesidir. Uydu konum/saat epoch'u buna göre 180 ms ileri
alınır. Bu değer signal
flight time veya WLS receiver clock bias ile birleştirilmemelidir. Mevcut
`satpos()` zaten kullanım anında bir kez ekler; PPS peripheral tekrar eklemez.
6. 125 MHz timestamp `ABS_SAMPLE` ve `REM_CODE_PHASE` ile aynı hardware
+8 -8
View File
@@ -338,7 +338,7 @@ def page_executive(report: Report) -> None:
ax,
0.55,
r"T_{GPS,k}=T_{TX,k}+S-\frac{b_k}{c}",
"Minimum corrected-ABS kanalının receive GPS zamanı; ilk delivery için TTX,k = HOW + 0.160 s",
"Minimum corrected-ABS kanalının receive GPS zamanı; ilk delivery için TTX,k = HOW + 0.180 s",
)
report.equation(
ax,
@@ -455,7 +455,7 @@ def page_lnav_timeline(report: Report) -> None:
ax.text(x + 0.039, 0.58, label, color="white" if index < 2 else INK, ha="center", va="center", fontsize=7.5, weight="bold" if index < 2 else "normal")
report.arrow(ax, (0.205, 0.545), (0.86, 0.49), color=ORANGE, text="HOW Z-count: sonraki 6 s subframe sınırının transmit-time etiketi", rad=-0.12)
ax.add_patch(FancyBboxPatch((0.18, 0.445), 0.64, 0.045, boxstyle="round,pad=0.006", facecolor=PALE_ORANGE, edgecolor=ORANGE))
ax.text(0.50, 0.467, "Hardware takip eden subframe'den 8 bit bekler: 8 × 20 ms = POSTPONE_TOW = 160 ms", fontsize=7.6, color=ORANGE, ha="center", va="center", weight="bold")
ax.text(0.50, 0.467, "Mevcut hardware 9. takip eden bitte teslim eder: 9 × 20 ms = POSTPONE_TOW = 180 ms", fontsize=7.6, color=ORANGE, ha="center", va="center", weight="bold")
report.box(
ax,
@@ -518,7 +518,7 @@ def page_tx_rx(report: Report) -> None:
color=ORANGE,
)
report.equation(ax, 0.42, r"T_{RX,i}=T_{HOW}+\frac{\rho_i+I_i+T_i}{c}-\delta t_{SV,i}", "Aynı transmit sınırı her kanalda farklı receive timestamp üretir")
ax.text(0.50, 0.325, r"Hardware delivery epoch: $T_{event,i}=T_{HOW}+0.160\,s+\tau_i$", fontsize=9.2, color=ORANGE, ha="center", weight="bold")
ax.text(0.50, 0.325, r"Hardware delivery epoch: $T_{event,i}=T_{HOW}+0.180\,s+\tau_i$", fontsize=9.2, color=ORANGE, ha="center", weight="bold")
report.box(
ax,
0.07,
@@ -557,7 +557,7 @@ def page_project_map(report: Report) -> None:
(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.34, 0.17, 0.10, "Satellite model", "TOW + 160 ms\nephemeris/clock", PALE_BLUE, BLUE),
(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),
]
@@ -796,7 +796,7 @@ def page_counter_mapping(report: Report) -> None:
fig, ax = report.new_page("GPS time ile 125 MHz counter mapping'i")
ax.text(0.08, 0.84, "İlk coherent WLS epoch'u k", fontsize=11, color=NAVY, weight="bold")
boxes = [
(0.07, 0.68, 0.25, 0.11, "Transmit etiketi", "İlk delivery: HOW + 0.160 s\nsonra nav-solve epoch", PALE_BLUE, BLUE),
(0.07, 0.68, 0.25, 0.11, "Transmit etiketi", "İlk delivery: HOW + 0.180 s\nsonra nav-solve epoch", PALE_BLUE, BLUE),
(0.375, 0.68, 0.25, 0.11, "WLS flight time", "τmin = S − b/c\nall-satellite solution", PALE_GREEN, GREEN),
(0.68, 0.68, 0.25, 0.11, "Receive reference", "T_GPS,k = T_TX,k + τmin", PALE_ORANGE, ORANGE),
(0.375, 0.47, 0.25, 0.11, "Hardware timestamp", "C125,k\naynı ABS/REM event", PALE_BLUE, BLUE),
@@ -1304,7 +1304,7 @@ def page_open_points(report: Report) -> None:
fig, ax = report.new_page("Projede ölçülerek kapatılacak açık noktalar")
points = [
("HOW boundary semantiği", "parse_subframe Z-count × 6 yapıyor. Full-subframe interrupt, bit_gen ABS_SAMPLE ve gerçek transmit next-subframe sınırı arasındaki sabit pipeline cycle sayısı scope/ILA ile ölçülmeli."),
("POSTPONE_TOW = 0.160", "Doğrulanmış hardware readout gecikmesidir: takip eden subframe'den 8 bit beklenir; 50 bit/s hızda 160 ms. Mevcut satpos() kullanım anında bir kez ekler; PPS peripheral tekrar eklemez."),
("POSTPONE_TOW = 0.180", "Mevcut RTL readout gecikmesidir: doğrulama dokuzuncu takip eden bitte yapılır; 50 bit/s hızda 180 ms. Mevcut satpos() kullanım anında bir kez ekler; PPS peripheral tekrar eklemez."),
("RF ↔ 125 MHz bağı", "Aynı PLL/reference mı, bağımsız mı? Ortak drift ve independent jitter ölçülmeli; cross-timestamp uncertainty buna göre bütçelenmeli."),
("Cold-start WLS timing", "Dört uyduda vertical/clock korelasyonu yüksek. Coarse alignment yapılabilir; PPS quality düşük işaretlenmeli ve PI bandwidth agresif seçilmemeli."),
("Atmosfer/multipath", "Iono/tropo modele girse de residual b ve position'a sızar. Timing-only mode veya sabit/smoothed position ileride clock çözümünü iyileştirebilir."),
@@ -1361,7 +1361,7 @@ def page_conclusion(report: Report) -> None:
0.80,
0.09,
"En önemli mimari karar",
"İlk uydu PPS'i başlatmaz. Baremetal HOW'a 160 ms readout düzeltmesini bir kez uygular; ilk coherent WLS bu etiketi free-running 125 MHz counter'a bağlar. PPS peripheral 160 ms'yi tekrar eklemez.",
"İlk uydu PPS'i başlatmaz. Baremetal HOW'a 180 ms readout düzeltmesini bir kez uygular; ilk coherent WLS bu etiketi free-running 125 MHz counter'a bağlar. PPS peripheral 180 ms'yi tekrar eklemez.",
face=NAVY,
edge=NAVY,
title_color="#8ED6E8",
@@ -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: hardware delivery, takip eden subframe'in 8 biti nedeniyle HOW sınırından 160 ms ve signal flight time kadar sonradır. HOW +160 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 +68.802 ms kaba yaklaşım bırakır; WLS flight-time + paired timestamp doğru handover'dır.",
)
plot_page(
report,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 KiB

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 130 KiB

File diff suppressed because it is too large Load Diff
+6 -6
View File
@@ -4,18 +4,18 @@ epochs: 900
raw WLS bias/c range: -5.888015 .. 0.673753 ms
derived GPS reference RMS error: 60.522 ns
capture-to-commit delay mean/p99: 21.600 / 31.529 ms
first HOW used as receive-now, median phase error: -229.876 ms
first HOW + 160 ms + 68.802 ms, median phase error: -1.074 ms
first HOW used as receive-now, median phase error: -249.876 ms
first HOW + 180 ms + 68.802 ms, median phase error: -1.074 ms
4 satellites: position RMS=103.877 m, timing RMS=144.920 ns
5 satellites: position RMS=20.194 m, timing RMS=45.603 ns
7 satellites / drop-reacquisition: position RMS=14.626 m, timing RMS=36.888 ns
timestamped PI: tail phase RMS=274.599 ns, |phase| p95=409.200 ns, PPS RMS=815.734 ns, rejected stale commits=0
timestamped, PI disabled: tail phase RMS=233591.232 ns, |phase| p95=302909.699 ns, PPS RMS=181359.923 ns, rejected stale commits=0
timestamped PI: tail phase RMS=274.256 ns, |phase| p95=410.422 ns, PPS RMS=815.642 ns, rejected stale commits=0
timestamped, PI disabled: tail phase RMS=233589.285 ns, |phase| p95=302907.720 ns, PPS RMS=181358.257 ns, rejected stale commits=0
untagged APB-now PI: tail phase RMS=18651473.186 ns, |phase| p95=19146158.593 ns, PPS RMS=18060541.154 ns, rejected stale commits=0
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 + 160 ms) + STARTOFFSET - bias/c reconstructs GPS time at the paired timestamp.
- epoch-aligned TOW (first delivery: HOW + 180 ms) + STARTOFFSET - 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 160 ms readout correction once; the PPS peripheral must not add it again.
- baremetal applies the fixed 180 ms readout correction once; the PPS peripheral must not add it again.
+1 -1
View File
@@ -30,7 +30,7 @@ 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.160
POSTPONE_TOW_S = 0.180
OMEGA_EARTH = 7.2921151467e-5
WEEK_S = 604_800.0
+7 -6
View File
@@ -298,8 +298,9 @@ def simulate_startup_tow(
acquisition_ready = rng.uniform(0.0, 18.0, size=(trial_count, len(satellites)))
complete_subframe_tx = (np.floor(acquisition_ready / 6.0) + 2.0) * 6.0
# The current hardware validates/delivers the completed subframe only
# after receiving eight bits from the following subframe. At 50 bit/s
# this is a deterministic 160 ms readout delay (POSTPONE_TOW), distinct
# on the ninth following bit because validation reads the old shift
# register. At 50 bit/s
# this is a deterministic 180 ms readout delay (POSTPONE_TOW), distinct
# from satellite signal flight time.
how_interrupt_rx = (
complete_subframe_tx + POSTPONE_TOW_S + flight_times[None, :]
@@ -354,7 +355,7 @@ def plot_startup_tow(output_dir: Path, startup: dict[str, np.ndarray]) -> None:
alpha=0.8,
)
axes[1, 0].set_xlabel("Phase error [ms]")
axes[1, 0].set_title("Provisional: HOW + 160 ms + 68.802 ms")
axes[1, 0].set_title("Provisional: HOW + 180 ms + 68.802 ms")
axes[1, 0].grid(True, alpha=0.3)
axes[1, 1].hist(
@@ -397,7 +398,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 + 160 ms + 68.802 ms, median phase error: {np.median(startup['how_plus_pipeline_startoffset_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",
]
for start, stop, label in [
(0.0, 20.0, "4 satellites"),
@@ -426,10 +427,10 @@ 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 + 160 ms) + STARTOFFSET - bias/c reconstructs GPS time at the paired timestamp.",
"- epoch-aligned TOW (first delivery: HOW + 180 ms) + STARTOFFSET - 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 160 ms readout correction once; the PPS peripheral must not add it again.",
"- baremetal applies the fixed 180 ms readout correction once; the PPS peripheral must not add it again.",
]
)
return "\n".join(lines) + "\n"