Skip to content

Seed the tracker's secondary-code phase from Acquisition's secondary_code_phase on handoff #120

Description

@zsoerenm

Background

Acquisition v2.5+ added a secondary_code_phase::Union{Int,Nothing} field to AcquisitionResults — the secondary-code chip the acquisition's coherent integration window aligned to, recovered by its secondary-code rotation search. It is nothing when no rotation search ran (no secondary code, use_secondary_code = false, single-period coherent integration, or no detected peak).

This only ever works for short secondary codes. Recovering a secondary-code phase in acquisition means coherently integrating over (at least) one full secondary period. That is fine for a 10 ms code like GPS L5I's NH10, but infeasible for a long overlay like GPS L1C-P's 1800-chip / 18 s code — integrating that long in the acquisition search is far too resource-intensive. So in practice acq.secondary_code_phase is populated for short secondary codes (e.g. L5I) and stays nothing for long ones (e.g. L1C-P), which must continue to self-sync inside the tracker via its own rotation search.

Tracking now recovers the secondary-code phase itself via the unified rotation search (detect_bit_or_secondary_code_sync_secondary_code_search), but only after buffering one full secondary-code period (~10 ms for L5I; ~18 s for L1C-P).

Proposal

When acq.secondary_code_phase !== nothing (i.e. a short secondary code the acquisition stage could resolve), seed the acquisition→tracking handoff so the tracker starts already secondary-synced instead of re-running the search.

The handoff in ext/TrackingAcquisitionExt/TrackingAcquisitionExt.jl currently reads only acq.code_phase and acq.carrier_doppler:

  • TrackedSat(acq::AcquisitionResults; …)
  • add_satellite! / add_satellite(track_state, acq; …)
  • TrackState(acqs::AbstractVector{<:AcquisitionResults}; …)

These should also consume acq.secondary_code_phase and:

  • pre-populate the relevant signal's BitBuffer with found = true, secondary_phase, and polarity, and
  • anchor TrackedSat.code_phase into the widened (primary × secondary) wrap window (cf. _snap_code_phase_from_synced_signal / current_code_wrap in src/sat_state.jl).

Scope / benefit

  • In scope: short secondary codes (e.g. GPS L5I, NH10). The tracker starts with the correct secondary-code alignment immediately at handoff, skipping its own ~10 ms re-search and avoiding the brief window where code_phase is anchored only to the primary period. Modest in wall-clock terms, but it removes redundant work and gives correct anchoring from sample 0.
  • Out of scope: long overlays (e.g. GPS L1C-P, 18 s). Acquisition cannot recover these (integration too long), so secondary_code_phase is nothing and the tracker's own rotation search remains the only path. No change to that flow.

Open questions / considerations

  1. Convention alignment. Tracking's SyncResult.phase / BitBuffer.secondary_phase is the upcoming integration's secondary chip at the moment of sync. Acquisition's secondary_code_phase is "the chip the start of the coherent window aligned to." These must be reconciled (likely an offset by the number of code periods between the acquisition window start and the tracking start sample). Add a test that acquires + tracks a synthetic L5I signal and checks the seeded phase equals what the tracker recovers on its own.
  2. Polarity. Acquisition does not expose the locked polarity, so BitBuffer.polarity (±1) would still need recovery for data-bearing signals — and the short-secondary-code signals this applies to (e.g. L5I) are data-bearing. Either recover polarity from the acquisition correlation sign, or leave it to be resolved on the first decoded data bit.
  3. nothing handling. Fall back to today's behavior (track until self-sync) when secondary_code_phase === nothing — this is the normal case for long-overlay signals like L1C-P.
  4. Multi-signal sats. Seed only the signal the acquisition result corresponds to.

References

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