# 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.