Skip to content

Allow different code chipping rates within a single SignalGroup #151

Description

@zsoerenm

Summary

Allow a single SignalGroup to contain signals with different code chipping rates (different get_code_frequency). Today the SignalGroup constructor rejects this: _validate_signal_group (src/sat_state.jl) requires every signal in a group to share one chip rate (added in #129).

Motivation

There is no fundamental reason mixed chip rates can't share a satellite: all signals from one SV share an arrival time, so each signal's code phase and code Doppler are recoverable from a shared time reference by scaling with that signal's chip rate. The restriction is an implementation simplification, not a physical limit. Relaxing it would let a group co-track signals that share a band but not a chip rate.

Why it's currently blocked (load-bearing assumptions)

The shared per-satellite code state is stored as a single scalar in the driver signal's (signals[1]) chip units and consumed by every signal without rate scaling:

  1. Phase advanceupdate (src/downconvert_and_correlate.jl) advances the shared code_phase at get_code_frequency(signals[1]) and wraps it with current_code_wrap.
  2. Per-signal replica generation (src/downconvert_and_correlate_cpu.jl):
    • per_signal_phase = mod(code_phase, <signal's own wrap>) — uses the driver-chip phase directly as each signal's chip phase.
    • code_frequency = code_doppler + get_code_frequency(s) — adds the shared code_doppler straight onto each signal's base frequency, but code Doppler scales with chip rate, so the driver's value is wrong for a different-rate signal.
  3. Wrap computationcurrent_code_wrap / max_code_length (src/sat_state.jl) take a max over each signal's length in its own chips, which is only coherent when all signals share the driver's chip units.

Consequently, simply removing the #129 guard would make any non-driver signal with a different rate build its replica at the wrong phase and the wrong code Doppler → silent mistracking (the exact failure #129 prevents).

What a correct implementation requires

  • Convert the shared code_phase to each signal's chips by rate_i / rate_driver at replica-generation sites (and _snap_code_phase_from_synced_signal).
  • Scale the shared code_doppler by rate_i / rate_driver per signal when forming code_frequency.
  • Make current_code_wrap / max_code_length express every signal's wrap in the driver's chip units (or move the shared phase to a rate-independent unit, e.g. time/seconds).
  • Decide the reference convention (keep signals[1] as the chip-rate reference, or store the shared phase as time).
  • Add a synthetic mixed-chip-rate test signal to validate, since no real same-band GNSS pair has different chip rates that are co-tracked on a shared code phase (L1 C/A + L1C are both 1.023 Mcps; L5 I/Q both 10.23 Mcps; Galileo E1B/E1C both 1.023 Mcps).

Acceptance criteria

  • SignalGroup accepts mixed-chip-rate signals (relax/remove the #129 chip-rate check) only after the per-signal rate scaling above is in place.
  • A synthetic mixed-rate group tracks each signal correctly (correlator peaks, code-phase wrap, Doppler aiding) in tests.
  • _longest_code_signal (in TrackingAcquisitionExt) switches from comparing get_code_length (chips) to code period (get_code_length / get_code_frequency); the equal-chip-rate dependency is noted in a comment there today.

References

Found while reviewing acquisition routing in #146 (issue #134). Related: #113 (multi-signal/multi-band design), #129 (band + chip-rate invariant), #132 (API naming).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions