Initial Galileo receiver and SignalSim integration
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
# Algorithms and conventions
|
||||
|
||||
## Signal and downconversion
|
||||
|
||||
The analytic sampled signal uses
|
||||
|
||||
\[
|
||||
x[n]=A\,c(\tau_n)b(\tau_n)s_{BOC}(\tau_n)
|
||||
e^{j(\phi_0+2\pi(f_{IF}+f_D)n/f_s+\pi\dot f_D(n/f_s)^2)}+w[n].
|
||||
\]
|
||||
|
||||
The generator defines a positive Doppler as positive complex phase rotation.
|
||||
Digital downconversion and a candidate Doppler wipeoff therefore use
|
||||
|
||||
\[
|
||||
y[n]=x[n]e^{-j2\pi(f_{IF}+f_{bin})n/f_s}.
|
||||
\]
|
||||
|
||||
The phase-accumulator NCO has modulus \(2^P\), frequency-control word
|
||||
\(FCW=\operatorname{round}(f_o2^P/f_s)\), and update
|
||||
\(phase_{n+1}=(phase_n+FCW)\bmod 2^P\).
|
||||
|
||||
## Code and FFT acquisition
|
||||
|
||||
Fractional code phase advances by \(f_c/f_s\) chips per sample and indexes the
|
||||
chip with `floor(phase) mod code_length`. The BOC(1,1) sine-phased square wave
|
||||
is +1 for the first half of a chip and -1 for the second half. This convention
|
||||
is shared by generation, acquisition, and tracking.
|
||||
|
||||
The circular correlation is
|
||||
|
||||
\[
|
||||
R_f[k]=\operatorname{IFFT}\{\operatorname{FFT}(y_f[n])
|
||||
\operatorname{FFT}(c[n])^*\}[k].
|
||||
\]
|
||||
|
||||
For coherent interval \(T_c\), the complex correlator output is retained before
|
||||
power formation. Noncoherent accumulation is explicitly
|
||||
|
||||
\[
|
||||
P_f[k]=\sum_{m=0}^{M-1}|R_{f,m}[k]|^2,
|
||||
\quad T_{obs}=M T_c.
|
||||
\]
|
||||
|
||||
Differential combining uses adjacent complex results before magnitude. E1B
|
||||
hypothesis mode enumerates primary-epoch signs (limited to eight periods to
|
||||
bound exponential work). E1C search enumerates secondary phase; known-phase
|
||||
wipeoff uses phase zero at the first observation epoch. Delay results are
|
||||
reported over one 4 ms primary period even for longer coherent integrations.
|
||||
|
||||
Zero-padding can provide a convenient FFT size or a denser interpolation of a
|
||||
particular numerical representation, but it does not increase signal
|
||||
bandwidth, physical information, or fundamental delay resolution.
|
||||
|
||||
## Detection
|
||||
|
||||
After circularly excluding a guard interval around the main peak, the model
|
||||
computes
|
||||
|
||||
\[
|
||||
M_2=P_{max}/P_{second},\qquad M_\mu=P_{max}/\bar P_N,
|
||||
\]
|
||||
|
||||
plus `(peak-noise_mean)/(sidelobe-noise_mean)` and a CFAR-style
|
||||
`(peak-noise_median)/noise_std`. The acquisition C/N0 diagnostic is
|
||||
|
||||
\[
|
||||
\widehat{C/N_0}=10\log_{10}\left((P_{max}-P_N)/(P_NT_c)\right).
|
||||
\]
|
||||
|
||||
It is an estimator, not a calibrated RF measurement. Half-chip competitive BOC
|
||||
neighbors set a side-peak flag. `flag` is the implemented mitigation; an
|
||||
unambiguous BOC/CBOC strategy plugs into the detector without changing results.
|
||||
Parabolic interpolation preserves raw Doppler and sample bins.
|
||||
|
||||
## Tracking
|
||||
|
||||
Each component channel owns carrier/code NCO phase, loop states, correlation
|
||||
history, lock counters, C/N0 history, and state-machine state. Phases advance at
|
||||
every accepted sample and never reset on Python block boundaries.
|
||||
|
||||
Carrier discriminators are
|
||||
|
||||
\[
|
||||
e_{PLL,2}=\tan^{-1}(Q/I),\qquad e_{PLL,4}=\operatorname{atan2}(Q,I)
|
||||
\]
|
||||
|
||||
in radians. The cross/phase-difference FLL is
|
||||
|
||||
\[
|
||||
e_{FLL}=\angle(P_kP_{k-1}^*)/(2\pi T)\quad[Hz].
|
||||
\]
|
||||
|
||||
The normalized envelope DLL reports a locally calibrated chip error
|
||||
|
||||
\[
|
||||
e_{DLL}=d\frac{|E|-|L|}{|E|+|L|}\quad[chips],
|
||||
\]
|
||||
|
||||
with power and dot-product alternatives. A BOC false-lock flag compares VEVL
|
||||
and prompt magnitudes.
|
||||
|
||||
For a second-order filter, noise bandwidth and damping generate
|
||||
|
||||
\[
|
||||
\omega_n=\frac{8\zeta B_n}{4\zeta^2+1},\quad
|
||||
k_p=2\zeta\omega_n,\quad k_i=\omega_n^2.
|
||||
\]
|
||||
|
||||
The third-order starting model places three repeated stable poles derived from
|
||||
the same \(\omega_n\): correction
|
||||
\(3\omega_ne+3\omega_n^2\int e+\omega_n^3\iint e\). These are physical
|
||||
starting coefficients, not final optimized receiver-loop values.
|
||||
|
||||
Carrier NCO frequency is corrected in hertz; code NCO frequency is nominal code
|
||||
rate plus DLL correction and optional carrier aiding
|
||||
\(f_{code,aid}=f_{carrier}f_c/f_{RF}\). Prompt C/N0 uses coherent versus total
|
||||
prompt moments over a sliding window and divides the ratio by integration time.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Configuration
|
||||
|
||||
`ReceiverConfig` owns RF, acquisition, tracking, front-end, and fixed-point
|
||||
dataclasses. YAML loading is strict: a misspelled field is an error.
|
||||
|
||||
Important validation rules include positive sampling/loop rates, a real IF
|
||||
strictly below Nyquist, integer sample counts per integration interval,
|
||||
ordered correlator tap spacing, explicit FFT length when requested, E1B
|
||||
bit-transition protection beyond 4 ms, and E1C secondary-code handling beyond
|
||||
4 ms. E1B tracking is conservatively limited to 4 ms until navigation-symbol
|
||||
synchronization is implemented.
|
||||
|
||||
`intermediate_frequency_hz` may be nonzero for either real or analytic input.
|
||||
The front end always outputs complex baseband and divides the effective sample
|
||||
rate by the integer decimation factor. Acquisition/tracking must use that output
|
||||
rate.
|
||||
|
||||
Acquisition parallelism fields expose future hardware/software scheduling but
|
||||
do not change numerical results. `zero_padding_mode: none` provides the exact
|
||||
N-sample circular correlation. A longer FFT changes the sampled numerical
|
||||
correlation/edge convention; it is not additional physical delay information.
|
||||
|
||||
Default YAML files:
|
||||
|
||||
- `e1_default.yaml`: requested 16.368 MHz nominal defaults.
|
||||
- `acquisition_example.yaml`: faster 4.092 MHz executable search.
|
||||
- `tracking_example.yaml`: 4.092 MHz tracking defaults.
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Phase gates and reproducible commands
|
||||
|
||||
The project was implemented in six gates. These are the exact command shapes
|
||||
used to validate each gate; the final regression reruns their union.
|
||||
|
||||
## Phase 1
|
||||
|
||||
```bash
|
||||
python -m pytest tests/unit/test_config.py tests/unit/test_signals.py
|
||||
python examples/generate_e1_signal.py
|
||||
```
|
||||
|
||||
## Phase 2
|
||||
|
||||
```bash
|
||||
python -m pytest tests/unit/test_frontend.py tests/unit/test_acquisition_metrics.py tests/unit/test_acquisition_parallelism.py tests/integration/test_acquisition.py tests/integration/test_long_integration.py
|
||||
python examples/run_acquisition.py
|
||||
```
|
||||
|
||||
## Phases 3 and 4
|
||||
|
||||
```bash
|
||||
python -m pytest tests/unit/test_discriminators.py tests/unit/test_loop_filters.py tests/unit/test_correlator.py tests/integration/test_tracking.py
|
||||
python examples/run_tracking.py
|
||||
python examples/run_complete_receiver.py
|
||||
```
|
||||
|
||||
## Phase 5
|
||||
|
||||
```bash
|
||||
python -m pytest tests/unit/test_fixed_point.py tests/unit/test_fixed_fft.py tests/integration/test_fixed_acquisition.py tests/regression/test_complete_receiver.py
|
||||
python -m galileo_rx.cli compare-fixed --config configs/acquisition_example.yaml --input samples.bin
|
||||
```
|
||||
|
||||
## Phase 6 and final regression
|
||||
|
||||
```bash
|
||||
python -m pytest tests/unit/test_rtl_model.py tests/unit/test_vector_export.py
|
||||
python -m pytest
|
||||
```
|
||||
|
||||
The CLI smoke sequence is:
|
||||
|
||||
```bash
|
||||
python -m galileo_rx.cli generate --config configs/acquisition_example.yaml --duration-ms 60 --prn 1 --output samples.bin
|
||||
python -m galileo_rx.cli acquire --config configs/acquisition_example.yaml --input samples.bin --output acquisition.json
|
||||
python -m galileo_rx.cli track --config configs/tracking_example.yaml --input samples.bin --acquisition-result acquisition.json --output tracking.csv
|
||||
python -m galileo_rx.cli run --config configs/acquisition_example.yaml --input samples.bin
|
||||
python -m galileo_rx.cli compare-fixed --config configs/acquisition_example.yaml --input samples.bin
|
||||
```
|
||||
@@ -0,0 +1,45 @@
|
||||
# Fixed-point plan
|
||||
|
||||
The fixed framework represents signed two's-complement integers with declared
|
||||
total/fractional widths. Every quantizer selects saturation or wrap and nearest,
|
||||
truncate, or convergent ties-to-even rounding. Quantizers count overflow and
|
||||
accumulate RMS/peak quantization error.
|
||||
|
||||
Proposed starting points (not final widths):
|
||||
|
||||
| Stage | Width | Fraction | Policy |
|
||||
|---|---:|---:|---|
|
||||
| input sample | 8 | 7 | saturate |
|
||||
| sine/cosine | 16 | 15 | saturate |
|
||||
| mixer output | 18 | 15 | saturate |
|
||||
| FFT input | 18 | 15 | saturate |
|
||||
| FFT twiddle | 16 | 15 | convergent |
|
||||
| FFT stage | 20 | 15 | configured scaling |
|
||||
| correlation product | 36 | 30 | full precision then requantize |
|
||||
| IFFT output | 28 | 15 | saturate |
|
||||
| coherent accumulator | 40 | 15 | saturate |
|
||||
| noncoherent accumulator | unsigned 56 | 20 | saturate |
|
||||
| carrier phase | unsigned 32 | 0 | natural wrap |
|
||||
| code phase | unsigned 48 | 32 | natural wrap |
|
||||
| loop state | 32 | 20 | saturate |
|
||||
| FCW | signed 32 | 0 | wrap in phase accumulator |
|
||||
| discriminator | 24 | 18 | saturate |
|
||||
|
||||
`FixedNco` and `FixedCorrelator` are integer bit-true models. Fixed PCPS models
|
||||
declared quantization at input, normalized FFT output, pointwise product, IFFT
|
||||
output/power, and accumulation. Exact butterfly-level identity depends on the
|
||||
chosen streaming FFT IP scaling schedule and is explicitly unresolved until an
|
||||
RTL FFT architecture is selected.
|
||||
|
||||
`FixedRadix2Fft` supplies a fully stage-quantized power-of-two oracle with
|
||||
quantized twiddles, convergent butterfly rounding, saturation counters, and one
|
||||
bit of scaling per stage. It is used for standalone RTL FFT vectors. The
|
||||
runtime PCPS boundary model remains able to use arbitrary transform lengths;
|
||||
matching a vendor FFT at those lengths requires configuring that core's exact
|
||||
schedule.
|
||||
|
||||
The sweep utility varies input/FFT widths and reports mean absolute/relative,
|
||||
RMS, peak error, acquisition decision, Doppler bin, code phase, and overflows.
|
||||
Tracking qualification additionally compares convergence, steady-state jitter,
|
||||
and loss-of-lock decisions; these metrics must become release gates once loop
|
||||
state formats are frozen.
|
||||
@@ -0,0 +1,104 @@
|
||||
# RTL mapping and architecture tradeoffs
|
||||
|
||||
All streaming datapaths use `data`, `valid`, `ready`, optional `first/last`,
|
||||
`channel_id`, and sideband metadata. State advances only on `valid && ready`.
|
||||
Backpressure must propagate or terminate in a sized sample FIFO. Reset clears
|
||||
valid/state/overflow counters and sets NCO phases to software-programmed start
|
||||
values; it must not occur between arbitrary host blocks.
|
||||
|
||||
## Acquisition formulas
|
||||
|
||||
Let \(N_s=\operatorname{round}(f_sT_c)\), FFT size \(N\ge N_s\), PRNs \(P\),
|
||||
Doppler bins \(D\), noncoherent intervals \(M\), Doppler lanes \(L\), FFT
|
||||
latency/interval \(C_{FFT}\), and clock \(f_{clk}\).
|
||||
|
||||
- observation buffer: \(MN_s\) complex samples;
|
||||
- transforms per PRN: \(2DM\) when code FFTs are precomputed;
|
||||
- code FFT store: \(PN\) complex words (or \(N\) for on-demand PRN loading);
|
||||
- pointwise complex multiplications: \(PDMN\);
|
||||
- approximate single-engine search cycles:
|
||||
\(P\lceil D/L\rceil M(2C_{FFT}+N)\);
|
||||
- acquisition time is those cycles divided by \(f_{clk}\), plus fill and control
|
||||
overhead.
|
||||
|
||||
### A — buffered FFT PCPS
|
||||
|
||||
One coherent block is buffered. A mixer iterates Doppler bins, a forward FFT is
|
||||
multiplied by conjugated code FFT memory, the same or a second engine performs
|
||||
IFFT, magnitude squared accumulates in Doppler/code RAM, and peak search scans
|
||||
the result. It minimizes mixers/FFT engines/DSPs and maximizes search latency.
|
||||
Ping-pong buffers hide capture behind search. One FFT engine needs arbitration
|
||||
and doubles transform serialization; two remove that conflict at extra DSP/BRAM.
|
||||
|
||||
### B — parallel Doppler lanes
|
||||
|
||||
`NUM_DOPPLER_LANES=L` replicates carrier mixers and either feeds batched FFTs or
|
||||
replicates FFT engines. With one reused FFT, lanes improve mixing but not the FFT
|
||||
bottleneck. With L FFT/IFFT paths, ideal Doppler latency approaches 1/L while
|
||||
DSP, twiddle ROM, transpose memory, and noncoherent RAM ports grow roughly with
|
||||
L. Intermediate choices use L mixers, a smaller number of FFT engines, and
|
||||
FIFOs. Resource budgets and required reacquisition time select L; neither
|
||||
architecture is universally preferred.
|
||||
|
||||
## Tracking
|
||||
|
||||
Fully parallel mode instantiates carrier/code NCOs and VE/E/P/L/VL complex
|
||||
MACs per channel. It provides one-sample-per-clock latency independence and
|
||||
uses the most DSPs and accumulators.
|
||||
|
||||
Time-multiplexed mode stores phase words, FCWs, five complex accumulators, loop
|
||||
states, counters, and secondary phase per channel in `channel_state_ram`.
|
||||
`NUM_CORRELATOR_LANES` engines visit all active channels for every input sample.
|
||||
The no-loss condition is
|
||||
|
||||
\[
|
||||
\left\lceil\frac{NUM\_TRACKING\_CHANNELS}{NUM\_CORRELATOR\_LANES}\right\rceil
|
||||
C_{channel}\le f_{clk}/f_s.
|
||||
\]
|
||||
|
||||
If it fails, more lanes, a faster clock, a lower post-decimation sample rate, or
|
||||
fewer active channels are required. A sample FIFO cannot repair a sustained
|
||||
rate violation.
|
||||
|
||||
Parameters are `NUM_TRACKING_CHANNELS`, `NUM_CORRELATOR_LANES`, `SAMPLE_WIDTH`,
|
||||
`NCO_PHASE_WIDTH`, `CODE_PHASE_WIDTH`, `ACCUMULATOR_WIDTH`, and
|
||||
`MAX_INTEGRATION_SAMPLES`. The Python `TrackingScheduler` evaluates this exact
|
||||
condition.
|
||||
|
||||
The detailed per-module signal, width, latency, storage, reset, saturation, and
|
||||
verification contract is in `src/galileo_rx/rtl_model/architecture.md`.
|
||||
|
||||
## SignalSim gate before Acquisition IP / Tracking IP implementation
|
||||
|
||||
The validated external stimulus is complex IQ8 at 8.184 MHz with +2.046 MHz
|
||||
IF. `validate-signalsim` exports the 20 ms acquisition observation as separate
|
||||
two's-complement I/Q hex files, a per-sample timestamp CSV, and symbolic
|
||||
register values under `reports/signalsim_validation/rtl_vectors/`.
|
||||
|
||||
The fixed acquisition boundary model agrees with the float64/complex128
|
||||
reference on Doppler bin, code-phase sample, and acquisition decision with zero
|
||||
overflow. Its normalized correlation-map RMS error is 0.00219 and peak error is
|
||||
0.01996 for the proposed comparison formats. This is an input/FFT/product/power
|
||||
boundary result; it does not yet include a selected vendor FFT butterfly shift
|
||||
schedule.
|
||||
|
||||
Measured requirements feeding the RTL plan are:
|
||||
|
||||
- complex IQ input and `IQ` ordering must be a first-class sample-input mode;
|
||||
- code ROM import must discard one padding bit per 1024-bit SignalSim E1 table
|
||||
sector—no unexplained chip offset is permitted;
|
||||
- acquisition-to-tracking handoff carries IF center separately from Doppler so
|
||||
code aiding never uses the IF frequency;
|
||||
- E1B pull-in supports 2 ms dumps and data-insensitive FLL, followed by paired
|
||||
4 ms raw-symbol outputs;
|
||||
- E1C channel state includes secondary phase (measured phase 10 for this
|
||||
vector) and supports wipeoff before PLL/discriminator processing;
|
||||
- tracking measurement/APB visibility includes prompt I/Q, carrier/code NCOs,
|
||||
state, lock metrics, secondary phase, and symbol-valid/data;
|
||||
- APB addresses are still unassigned. The exported metadata uses symbolic
|
||||
names and therefore blocks a bus-level RTL run until the register map exists.
|
||||
|
||||
Native real-IF SignalSim stimulus is unavailable, so the real-input DDC path
|
||||
remains covered only by internal Python vectors. Full CBOC, IQ2, full I/NAV
|
||||
decoding, nominal/weak C/N0, signed-Doppler, and multi-PRN external regressions
|
||||
remain gates before claiming broad Galileo/RTL compatibility.
|
||||
@@ -0,0 +1,241 @@
|
||||
# SignalSim integration and measured compatibility
|
||||
|
||||
## Repository identity and immutable baseline
|
||||
|
||||
The detected project is the GNSS simulator by Jun Mo (`globsky/SignalSim`), not
|
||||
another repository with a similar name.
|
||||
|
||||
| Item | Detected value |
|
||||
|---|---|
|
||||
| Local path | `/home/onur/Desktop/galileo/signalsim/SignalSim` |
|
||||
| Remote | `https://github.com/globsky/SignalSim` |
|
||||
| Commit | `80b262113e77c4482ca9f5cbfef9bcada402ed28` |
|
||||
| Branch/status before integration | `main...origin/main`, clean |
|
||||
| License evidence | No LICENSE/COPYING file; source headers say “All rights reserved”; README identifies some commercially licensed features |
|
||||
|
||||
SignalSim itself was not modified. The receiver parses its code tables from the
|
||||
external checkout at run time, so the SignalSim commit remains part of the test
|
||||
vector provenance and its copyrighted tables are not copied into this project.
|
||||
|
||||
The identity commands were:
|
||||
|
||||
```bash
|
||||
git -C signalsim/SignalSim remote -v
|
||||
git -C signalsim/SignalSim rev-parse HEAD
|
||||
git -C signalsim/SignalSim status --short --branch
|
||||
```
|
||||
|
||||
## Reproducible build
|
||||
|
||||
```bash
|
||||
tools/build_signalsim.sh /home/onur/Desktop/galileo/signalsim/SignalSim
|
||||
ctest --test-dir build/signalsim --output-on-failure
|
||||
```
|
||||
|
||||
The build used CMake 3.31.9, GCC 11.4.0, Ninja 1.11.1, C++20, Release mode,
|
||||
OpenMP, and `USE_NATIVE_OPT=OFF`. It produced `build/signalsim/IFdataGen`.
|
||||
CTest reported `No tests were found!!!`; the repository does not define
|
||||
IFdataGen CTest tests at this commit. The documented `-v`/`--version` options do
|
||||
not print a version and instead fall through to configuration handling.
|
||||
|
||||
## Source-to-sample trace
|
||||
|
||||
The executed Galileo E1 path is:
|
||||
|
||||
```text
|
||||
JSON output.systemSelect "Galileo"/"E1"
|
||||
-> IFdataGen frequency selection
|
||||
-> PrnGenerate(GalileoSystem, SIGNAL_INDEX_E1, SVID)
|
||||
E1B = E1MemoryCode[(SVID-1)*128]
|
||||
E1C = E1MemoryCode[(SVID+49)*128]
|
||||
-> SatelliteSignal
|
||||
E1B = -I/NAV symbol / sqrt(2)
|
||||
E1C = 25-symbol pilot sign / sqrt(2)
|
||||
-> SatIfSignal BOC half-chip sign and carrier rotation
|
||||
-> complex Gaussian noise + AGC
|
||||
-> IQ8/IQ16/IQ4/IQ2 quantizer
|
||||
-> binary file, I before Q
|
||||
```
|
||||
|
||||
`GetMemorySequence()` reads four storage sectors per E1 code. Within each
|
||||
32-word/1024-bit sector it consumes 1023 bits MSB first and discards the last
|
||||
padding bit. The receiver adapter mirrors that exact behavior. Treating the
|
||||
table as 4092 contiguous bits was experimentally shown to reduce peak/second
|
||||
from about 7 to about 1; the sector-aware implementation restores acquisition.
|
||||
|
||||
SignalSim models E1 as equal-amplitude E1B and E1C on the same real baseband
|
||||
axis. It flips every second 2.046-Mchip/s half-chip, which is BOC(1,1). It does
|
||||
not synthesize the BOC(6,1) term or full Galileo E1 OS CBOC in this path. E1B
|
||||
and E1C cannot be selected separately by the JSON/CLI; `E1` always generates
|
||||
the combined signal.
|
||||
|
||||
Source-inspected Galileo selection (only E1 was generated in this validation):
|
||||
|
||||
| Band selector | Components implemented in open source path | Data layer / pilot overlay | Validation status |
|
||||
|---|---|---|---|
|
||||
| E1 | E1B data + E1C pilot, inseparable | I/NAV / common CS25 | Generated and received |
|
||||
| E5a | data I + pilot Q codes | F/NAV / PRN-specific CS100 | Source inspected only |
|
||||
| E5b | data I + pilot Q codes | I/NAV / PRN-specific CS100 | Source inspected only |
|
||||
| E6 | pilot output (data amplitude set to zero) | no open data path / CS100 | Source inspected only |
|
||||
| E5 AltBOC | Listed as commercially licensed | N/A | Not available/tested |
|
||||
|
||||
### Navigation and secondary data
|
||||
|
||||
E1B is not a fixed or random placeholder bit stream. `INavBit::GetFrameData()`
|
||||
constructs Galileo I/NAV words from the selected RINEX ephemeris/almanac/UTC
|
||||
data, adds CRC-24Q, convolutionally encodes, interleaves, inserts the 10-symbol
|
||||
sync pattern, and emits 500 encoded symbols per 2-second page. Word types above
|
||||
20 are forced to dummy word 63; reserved/SAR/spare fields include zero-filled
|
||||
placeholders. Reed-Solomon ephemeris parity words 17–20 are implemented.
|
||||
|
||||
E1C uses a common 25-symbol value held in `PilotBit.cpp` as `0x9b501c`, addressed
|
||||
LSB first. This work verifies the sequence SignalSim actually emits; it does
|
||||
not independently certify that constant against a particular Galileo ICD
|
||||
revision.
|
||||
|
||||
See [the bit-layer report](../reports/signalsim_bit_generation_report.md) for a
|
||||
stage-by-stage distinction between navigation fields, channel symbols,
|
||||
spreading chips, and samples.
|
||||
|
||||
## Output formats and controls
|
||||
|
||||
`sampleFreq` and `centerFreq` are configurable JSON values in MHz and are
|
||||
converted internally to integer kHz. A signal's complex IF is
|
||||
`RF carrier - centerFreq`; out-of-band signals are disabled. Native output is
|
||||
complex IQ only—there is no real-only IF binary mode.
|
||||
|
||||
| Format | Exact packing | Quantization at AGC gain `G` | Status |
|
||||
|---|---|---|---|
|
||||
| IQ16 | little-endian signed I16, signed Q16 | truncate `3277*G*x`, saturate −32768…32767 | usable |
|
||||
| IQ8 | signed I8, signed Q8 | truncate `25*G*x`, saturate −128…127 | validated |
|
||||
| IQ4 | one byte; I high nibble, Q low; sign-magnitude | truncate `3*G*abs(x)`, magnitude saturates at 7 | adapter tested |
|
||||
| IQ2 | two complex samples/byte, low then high nibble; sign/magnitude | threshold levels | broken in this commit: computed byte is never assigned to output |
|
||||
|
||||
The adapter in `galileo_rx.io.signalsim_reader` validates file length, signed
|
||||
conversion, byte order, IQ order, offsets, counts, memory mapping, and streaming.
|
||||
All decoded samples are promoted to `numpy.complex128`; quantized integer widths
|
||||
remain explicit input boundaries.
|
||||
|
||||
## Generated vector and executable results
|
||||
|
||||
The deterministic vector configuration is
|
||||
`verification/vectors/signalsim/high_cn0_single_prn/config.json`. It requests
|
||||
one visible Galileo PRN 1, combined E1B/E1C, 55 dB-Hz configured C/N0, 8.184
|
||||
MHz complex IQ8, center 1573.374 MHz (IF +2.046 MHz), static receiver, bundled
|
||||
RINEX navigation data, and 2.2 seconds duration.
|
||||
|
||||
```bash
|
||||
build/signalsim/IFdataGen \
|
||||
-c verification/vectors/signalsim/high_cn0_single_prn/config.json \
|
||||
-o verification/vectors/signalsim/high_cn0_single_prn/high_cn0_single_prn_iq8.bin \
|
||||
-t -st
|
||||
|
||||
MPLBACKEND=Agg python -m galileo_rx.cli validate-signalsim \
|
||||
--signalsim-path signalsim/SignalSim \
|
||||
--config configs/signalsim_validation.yaml \
|
||||
--output-dir reports/signalsim_validation \
|
||||
--mode quick
|
||||
```
|
||||
|
||||
The sample file is 36,009,600 bytes (18,004,800 complex samples) with SHA-256
|
||||
`3e72a5ad2d35ee68273b77587a66847b0fb328650b39c9e39272f0dc54e39e3c`.
|
||||
SignalSim's validation summary reports PRN 1 Doppler `+1828 Hz`; this is rounded
|
||||
to integer hertz. SignalSim does not emit exact sample-side code-delay truth.
|
||||
|
||||
Measured quick-mode results:
|
||||
|
||||
| Quantity | E1B | E1C |
|
||||
|---|---:|---:|
|
||||
| PCPS detected PRN | 1 | 1 |
|
||||
| Raw Doppler bin | +1750 Hz | +1750 Hz |
|
||||
| Interpolated Doppler | +1766.36 Hz | +1776.64 Hz |
|
||||
| Code phase | 18309.410 samples | 18309.256 samples |
|
||||
| Peak/second metric | 7.511 | 6.903 |
|
||||
| Final tracking Doppler | +1828.251 Hz | +1828.284 Hz |
|
||||
| Tracking Doppler jitter | 1.326 Hz RMS | 1.082 Hz RMS |
|
||||
| DLL error | 0.0963 chips RMS | 0.0849 chips RMS |
|
||||
| Final state | PLL_TRACK | PLL_TRACK |
|
||||
| Reacquisition epochs | 0 | 0 |
|
||||
|
||||
E1B uses 2 ms pull-in integrations. A 4 ms BPSK-squared FLL has an unambiguous
|
||||
range of only ±62.5 Hz, almost exactly the residual between the interpolated
|
||||
estimate and the +1828 Hz truth. Two milliseconds expands that range to ±125
|
||||
Hz, after which the channel converges and 2 ms prompt pairs form one 4 ms
|
||||
symbol. Two exact I/NAV sync patterns were recovered 250 symbols (one second)
|
||||
apart after a 300 ms tracking transient. This proves raw symbol recovery and
|
||||
page-boundary synchronization, not FEC/page/CRC decoding.
|
||||
|
||||
E1C synchronization jointly searched secondary phase and residual carrier
|
||||
frequency. It found phase 10 and +52.0 Hz residual, correlation 0.9830, and
|
||||
0/100 sign errors. Wiping the secondary code yielded 13.72 dB of 25-period
|
||||
coherent gain versus a 4 ms period, and 14.58 dB improvement relative to the
|
||||
unwiped 25-period sum.
|
||||
|
||||
Full mode adds three generated vectors. The 50 dB-Hz configured nominal vector
|
||||
passes E1C acquisition (metric 12.49), secondary synchronization (correlation
|
||||
0.9947, 0/100 errors), and pilot tracking at +1828.672 Hz with 0.817 Hz jitter.
|
||||
The negative-Doppler PRN 27 vector passes
|
||||
acquisition at −2750 Hz versus −2637 Hz truth and, after pull-in, tracks at
|
||||
−2637.776 Hz with 0.531 Hz jitter and 0.0615-chip DLL RMS. Five-PRN acquisition
|
||||
detects PRNs 1, 4, 19, 21, and 27; every raw Doppler result is within one 250 Hz
|
||||
bin. The negative channel raised five reacquisition flags during initial pull-in
|
||||
but none after epoch 500, and finished in `PLL_TRACK`; that transient is retained
|
||||
in the report rather than hidden.
|
||||
|
||||
Detailed machine results and plots are in
|
||||
`reports/signalsim_validation/`, with acquisition-only results in
|
||||
`reports/signalsim_acquisition_report.{json,md}`.
|
||||
|
||||
## Direct answers and limitations
|
||||
|
||||
1. Galileo generation: **yes**.
|
||||
2. Galileo bands: E1, E5a, E5b, E6; E5 AltBOC is not in the open path.
|
||||
3. E1B: **yes, only as part of combined E1**.
|
||||
4. E1C: **yes, only as part of combined E1**.
|
||||
5. Modulation: BOC(1,1) approximation, not CBOC.
|
||||
6. Navigation data: complete encoded/interleaved I/NAV symbols are generated,
|
||||
with the field limitations described above.
|
||||
7. E1C secondary symbols: **yes**, 25 symbols.
|
||||
8. Configurable IF: **yes**, through center frequency, kHz granularity.
|
||||
9. Configurable sample rate: **yes**, kHz granularity.
|
||||
10. Output: complex IQ only; no native real IF.
|
||||
11. Binary formats: exact table above; IQ8 is the validated integration format.
|
||||
12. PCPS acquisition: **pass** for E1B and E1C.
|
||||
13. Carrier/code tracking: **pass** for E1B and E1C over the available vector.
|
||||
14. E1B symbols: **raw symbols and sync recovered**; navigation decoder absent.
|
||||
15. E1C secondary: **phase synchronized and long-coherent gain measured**.
|
||||
16. Failures: no component-only E1, no CBOC, no real IF, broken IQ2 writer,
|
||||
no exported exact delay/symbol truth, no receiver I/NAV FEC/page decoder,
|
||||
and the remaining signal-format/component limitations listed above.
|
||||
|
||||
## Compatibility and RTL readiness
|
||||
|
||||
“Ready” below means the external stimulus, convention adapter, Python golden
|
||||
result, and automated check exist. It does not claim the RTL IP itself is built.
|
||||
|
||||
| Capability | SignalSim support | Python receiver result | RTL readiness |
|
||||
|---|---:|---:|---:|
|
||||
| Galileo E1B generation | Partial (combined E1 only) | Pass | Ready |
|
||||
| Galileo E1C generation | Partial (combined E1 only) | Pass | Ready |
|
||||
| Real IF | No | Not tested | Blocked |
|
||||
| Complex IQ | Yes | Pass | Ready |
|
||||
| Configurable sample rate | Yes | Pass at 8.184 MHz | Ready |
|
||||
| Configurable IF | Yes | Pass at +2.046 MHz | Ready |
|
||||
| PCPS acquisition | N/A | Pass | Ready |
|
||||
| Carrier tracking | N/A | Pass | Ready |
|
||||
| Code tracking | N/A | Pass | Ready |
|
||||
| E1B symbol recovery | Generated | Pass (raw + sync) | Ready |
|
||||
| Full I/NAV decoding | Generated | Not implemented | Blocked |
|
||||
| E1C secondary-code synchronization | Generated | Pass | Ready |
|
||||
|
||||
## Regression commands
|
||||
|
||||
```bash
|
||||
pytest -m signalsim -v
|
||||
pytest tests/integration/test_signalsim_acquisition.py -v
|
||||
pytest tests/integration/test_signalsim_tracking.py -v
|
||||
```
|
||||
|
||||
`--mode full` additionally regenerates/validates nominal-C/N0 acquisition,
|
||||
negative-Doppler acquisition and tracking, and five-PRN acquisition. E1B-only
|
||||
and E1C-only cases remain blocked because SignalSim cannot generate them.
|
||||
Reference in New Issue
Block a user