Implement Galileo E1B I/NAV navigation decoding

This commit is contained in:
2026-07-31 00:15:34 +03:00
parent 2a6ccfc23d
commit 814a788b36
104 changed files with 6351 additions and 10 deletions
+54
View File
@@ -0,0 +1,54 @@
from __future__ import annotations
import numpy as np
from galileo_rx.navigation.e1b_symbol_sync import NavigationSymbol
from galileo_rx.navigation.inav_page_sync import INAV_SYNC_BITS, InavPageSynchronizer
def _symbols(bits: np.ndarray, *, invert: bool = False, prefix: int = 17) -> list[NavigationSymbol]:
raw = np.concatenate((np.zeros(prefix, dtype=np.int8), bits))
if invert:
raw = 1 - raw
return [NavigationSymbol(1, i, i * 4, (i + 1) * 4, 1.0 if bit else -1.0, int(bit), 1.0, False, True) for i, bit in enumerate(raw)]
def test_page_sync_rejects_wrong_boundary_and_requires_repetition() -> None:
rng = np.random.default_rng(4)
part = np.concatenate((INAV_SYNC_BITS, rng.integers(0, 2, 240, dtype=np.int8)))
stream = _symbols(np.tile(part, 3))
sync = InavPageSynchronizer(required_confirmations=2)
first = sync.process_many(stream[:267])
assert first == []
output = sync.process_many(stream[267:])
assert len(output) == 3
assert all(item.sync_valid for item in output)
def test_page_sync_records_global_inversion() -> None:
body = np.zeros(240, dtype=np.int8)
parts = InavPageSynchronizer().process_many(_symbols(np.tile(np.concatenate((INAV_SYNC_BITS, body)), 3), invert=True))
assert len(parts) == 3
assert all(item.polarity_inverted for item in parts)
assert np.array_equal(parts[0].hard_symbols[:10], INAV_SYNC_BITS)
def test_page_sync_loses_and_reacquires_after_corrupted_sync() -> None:
body = np.zeros(240, dtype=np.int8)
part = np.concatenate((INAV_SYNC_BITS, body))
bits = np.tile(part, 6)
bits[3 * 250] ^= 1
output = InavPageSynchronizer().process_many(_symbols(bits, prefix=0))
assert len(output) == 5
assert [item.receive_sample_timestamp // (250 * 4) for item in output] == [0, 1, 2, 4, 5]
def test_page_sync_does_not_lock_through_symbol_erasure() -> None:
body = np.zeros(240, dtype=np.int8)
symbols = _symbols(np.tile(np.concatenate((INAV_SYNC_BITS, body)), 3), prefix=0)
erased = symbols[250]
symbols[250] = NavigationSymbol(
erased.prn, erased.symbol_index, erased.start_sample_timestamp,
erased.end_sample_timestamp, 0.0, 0, 0.0, False, False,
)
assert InavPageSynchronizer().process_many(symbols) == []