API Reference

Segment

HardwareLoopProtocol.SegmentConfig — Type
SegmentConfig(; channel_count, bands, event_capacity, command_capacity, device_index,
              nav_capacity)

What a loop process creates a segment for: how many hardware channels, which bands (a vector of BandEntrys, the first one the reference band), and how deep the rings are. Capacities are rounded up to powers of two; the event capacity defaults to 8192 per channel, eight seconds of records at a 1 kHz fold rate, and the loop-wide nav ring's to 1024 slots, a few seconds of navigation cycles with their satellite events at 10 Hz.

source
HardwareLoopProtocol.BandEntry — Type
BandEntry(band_id, sampling_freq_hz; intermediate_frequency_hz = 0.0, rf_input = 1, device_index = 1)

One RF band the loop process serves: its id, the rate its channels' sample counters run at, its intermediate frequency, and the RF input and device it arrives on. Written by the loop process, checked by the receiver against its own band plan.

intermediate_frequency_hz is where a signal at zero Doppler sits in the band's samples: the front end's IF, including any fixed offset its tuning leaves (an RTL-SDR's LO synthesizer lands a fixed number of Hz off the requested frequency). Every carrier Doppler in this protocol is relative to it — a channel's carrier NCO runs at the IF plus the Doppler — so a receiver that acquires from the band's samples searches around it.

source
HardwareLoopProtocol.create_segment — Function
create_segment(path, config::SegmentConfig) -> Segment

Create (or re-create) the segment at path — a file, conventionally /dev/shm/gnss-loop-<device> — sized and laid out for config, with every ring empty and the header written last. This is the loop process's side. Pass nothing for path to back the segment with a heap buffer instead, for the in-process transport and the tests.

source
HardwareLoopProtocol.attach_segment — Function
attach_segment(path) -> Segment

Map the segment at path and verify its header: magic, protocol version and layout hash must match this package's, or an ArgumentError names what did not. This is the receiver's side.

source
HardwareLoopProtocol.open_or_attach_segment — Function
open_or_attach_segment(path, config; stale_after_ns) -> (segment, attached::Bool)

Attach to the segment at path if one exists whose loop heartbeat is younger than stale_after_ns, otherwise create a fresh one for config. Returns the segment and whether it was attached (true) or created (false).

source

Rings

HardwareLoopProtocol.publish! — Function
publish!(ring, tag, body) -> UInt64

Append one slot and return its sequence index. Never blocks: if the consumer is more than a capacity behind, the oldest unconsumed slot is overwritten and the ring's overruns counter is incremented — the consumer will see the gap as lost history. For event rings.

source
HardwareLoopProtocol.try_publish! — Function
try_publish!(ring, tag, body) -> Bool

Append one slot unless that would overwrite an unconsumed one, in which case nothing is written and false is returned. For command rings, where losing an entry silently is the one unacceptable outcome.

source
HardwareLoopProtocol.peek! — Function
peek!(ring, ::Type{Tag}) -> (status, view::SlotView{Tag}, lost::UInt64)

Look at the consumer's next slot without consuming it. status is :empty (nothing published), :ok (the view is valid) or :lost (the consumer had fallen a whole capacity behind — lost slots were skipped, the ring's consumer_lost counter was advanced and the view is the oldest slot still intact). A slot the producer overwrote while it was being read is skipped and counted the same way, so a caller only ever sees a consistent tag.

source
HardwareLoopProtocol.payload — Function
payload(::Type{P}, ring, view) -> Union{Nothing,P}

Read the payload of a peeked slot as a P, or nothing if the producer overwrote the slot in the meantime (the caller then treats the slot as lost and calls commit! to move past it).

source

Snapshots

HardwareLoopProtocol.write_snapshot! — Function
write_snapshot!(slot, tag, state)

Publish a new epoch state. The sequence word is odd while the write is in progress and even once it is complete; readers retry on an odd or changed word.

source
HardwareLoopProtocol.read_snapshot — Function
read_snapshot(slot) -> Union{Nothing,Tuple{EventTag,EpochStateEvent}}

The newest epoch state, or nothing while none has been written. Retries a bounded number of times if the writer is mid-update.

source
HardwareLoopProtocol.navigation_mode — Function
navigation_mode(seg) -> UInt8

What the loop process does about the navigation solution: NAV_NONE (it publishes none; the receiver decodes the bits and solves it) or NAV_VECTOR (it runs vector tracking and publishes the solution on the nav_ring). Written by the loop process before it marks itself running.

source
HardwareLoopProtocol.publish_nav_solution! — Function
publish_nav_solution!(seg, tag::EventTag, solution::NavSolutionEvent)

Publish a navigation cycle's solution: onto the nav ring, after the cycle's satellite events, and into the nav snapshot.

source
HardwareLoopProtocol.read_nav_snapshot — Function
read_nav_snapshot(seg) -> Union{Nothing,Tuple{EventTag,NavSolutionEvent}}

The newest navigation solution, or nothing while none has been published (always, for a loop in NAV_NONE). For a reader that lost the nav ring's history, or only wants "now".

source
HardwareLoopProtocol.NavSolutionEvent — Type
NavSolutionEvent

One navigation cycle's solution, the last event of the cycle on the nav ring and the value in the nav snapshot. The tag's channel is 0 (loop-wide) and its device_sample the cycle's epoch on the reference band's counter.

  • cycle — the estimator's navigation-cycle count;
  • position_ecef_m, velocity_ecef_mps — ECEF, metres and metres per second;
  • clock_bias_m — the receiver clock bias of the solution's reference system, in metres; clock_drift — the relative clock drift (s/s). Inter-system and inter-frequency biases are not published;
  • time_tai_s, time_tai_frac — the solution's time as whole TAI seconds since J2000 and the fraction of the second, meaningful with NAV_TIME_VALID;
  • position_std_m, clock_std_m — the filter's 1σ uncertainties (NaN while it is not running), time_with_insufficient_meas_s its coasting time;
  • dop — GDOP, PDOP, VDOP, HDOP and TDOP, NaN when absent;
  • num_members — satellites in the vector loop, num_sats — satellites in the solution;
  • flags — NAV_RUNNING, NAV_SEEDED, NAV_FELL_BACK, NAV_RELEASED, NAV_VALID, NAV_TIME_VALID.
source
HardwareLoopProtocol.NavSatelliteEvent — Type
NavSatelliteEvent

One satellite as a navigation cycle saw it. A cycle publishes one per satellite it knows, before its NavSolutionEvent; the tag carries the satellite's prn and band, channel 0 and the cycle's epoch.

  • cycle — the cycle it belongs to; channel — the loop channel that drives the satellite; signal — the signal's id;
  • sat_position_ecef_m, sat_time_s (transmit time), residual_m and rate_residual_mps — from the solution, NaN unless NAV_SAT_IN_SOLUTION;
  • cn0_dbhz — at the latest epoch the satellite was snapshotted at;
  • flags — the NAV_SAT_* bits; release_reason — one of the NAV_RELEASE_* codes, or NAV_NOT_RELEASED.
source

Heartbeats, state and process ids

HardwareLoopProtocol.heartbeat_alive — Function
heartbeat_alive(seg, side; stale_after_ns = 500_000_000, now_ns = time_ns()) -> Bool

Whether side (:loop or :receiver) has heartbeated within stale_after_ns. A heartbeat of zero (never stamped) is not alive.

source
HardwareLoopProtocol.loop_state — Function
loop_state(seg) -> UInt64

The loop process's lifecycle state, one of HardwareLoopProtocol.LOOP_STATE_STARTING (set by create_segment), LOOP_STATE_RUNNING, LOOP_STATE_STOPPING, LOOP_STATE_STOPPED or LOOP_STATE_FAULT.

source

Events

HardwareLoopProtocol.EventTag — Type
EventTag

The 16-byte tag every event carries after its sequence word: what kind of event it is, which hardware channel, band and satellite it belongs to, and the device sample it refers to (a record's end, an epoch boundary, the sample a status change took effect at).

source
HardwareLoopProtocol.RecordEvent — Type
RecordEvent

What Tracking._apply_correlator_output needs of a record and nothing more — the complete history the receiver's lock detectors and decoder rest on: the filtered prompt, the record's length, the blocks it credits to the bit clock, the C/N₀ after it, and the NCO words it really ran under.

source
HardwareLoopProtocol.BitEvent — Type
BitEvent

One navigation soft bit as the bit buffer emitted it: its sign is the hard decision, its magnitude the confidence. bit_index counts bits since the channel's bit clock was last (re)started.

source
HardwareLoopProtocol.EpochStateEvent — Type
EpochStateEvent

"Newest wins": the channel's loop state at the common fold boundary the tag's device_sample names — the Dopplers, the absolute code phase referenced to that boundary, the carrier phase, the sync state and block count, and the NCO words in effect. PVT and the dashboard read this; the same value is written into the channel's snapshot slot.

source
HardwareLoopProtocol.StatusEvent — Type
StatusEvent

A command acknowledgement ("armed at device sample S", "rejected, reason R") or a channel or loop status change. sequence is the command's sequence number for an acknowledgement, or the event sequence from which history was lost for STATUS_HISTORY_LOST. STATUS_CHANNEL_STATE carries the full seed a receiver needs to adopt the channel's satellite after re-attaching: signal, group, PRN, Dopplers and code phase at sample.

source
HardwareLoopProtocol.TapsEvent — Type
TapsEvent

The full correlator accumulators of one record, latest tap first, antenna-major (taps[(antenna - 1) * num_taps + tap]), on the host's amplitude scale. Only published for channels configured with want_taps (the dashboard, a replay recording, a future vector-tracking path).

source

Commands

HardwareLoopProtocol.CommandTag — Type
CommandTag

The 16-byte tag every command carries: its kind, the channel it addresses (0 for the whole loop) and the receiver's sequence number, which the acknowledging status event echoes.

source
HardwareLoopProtocol.ArmCommand — Type
ArmCommand

"Arm channel k with this configuration": GNSSReceiver's HardwareChannelConfig with every Julia object replaced by a plain value — the signal and group ids as fixed names, the tap offsets as a fixed tuple. The loop executes the arm; the allocation policy stays with the receiver.

carrier_loop_bandwidth_hz / code_loop_bandwidth_hz of 0 mean the signal's defaults. valid_at_sample is on the channel's band counter, like every sample in this protocol.

source
HardwareLoopProtocol.ConfigureCommand — Type
ConfigureCommand

Loop-wide configuration: the fold epoch, the predicted commit lead, the coherent accumulation ceiling, the record integration ceiling and which events are published. Zero leaves a field unchanged.

source

Names

HardwareLoopProtocol.FixedName — Type
FixedName(s)

A fixed-length, zero-padded ASCII name — how a Symbol such as :GPSL1CA or a tracking-group key crosses the segment, since neither side can share a symbol table. String(name) / Symbol(name) recover it; a name longer than NAME_BYTES is an ArgumentError when built on the receiver side.

source