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.
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.
HardwareLoopProtocol.Segment — Type
SegmentA mapped protocol segment. Build one with create_segment (the loop process, which owns the layout) or attach_segment (the receiver); close unmaps it. The creator of a file-backed segment may unlink_segment it when the session is over.
HardwareLoopProtocol.create_segment — Function
create_segment(path, config::SegmentConfig) -> SegmentCreate (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.
HardwareLoopProtocol.attach_segment — Function
attach_segment(path) -> SegmentMap 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.
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).
HardwareLoopProtocol.segment_exists — Function
Whether a segment file exists at path.
HardwareLoopProtocol.unlink_segment — Function
unlink_segment(path)Remove the segment file. The loop process does this when it shuts down cleanly; a receiver never does.
HardwareLoopProtocol.segment_config — Function
segment_config(seg) -> SegmentConfigThe configuration the segment was created with, read back from its header.
HardwareLoopProtocol.band_table — Function
band_table(seg) -> Vector{BandEntry}The bands the loop process serves, the reference band first.
HardwareLoopProtocol.PROTOCOL_VERSION — Constant
The protocol revision. A segment whose header carries another is refused.
Rings
HardwareLoopProtocol.Ring — Type
RingA handle on one ring inside a segment: a base pointer plus the geometry, all isbits, so building one costs nothing and it can live in a hot loop's state. Obtain one with command_ring or event_ring.
HardwareLoopProtocol.command_ring — Function
The command ring (receiver → loop).
HardwareLoopProtocol.event_ring — Function
Hardware channel channel's event ring (loop → receiver).
HardwareLoopProtocol.publish! — Function
publish!(ring, tag, body) -> UInt64Append 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.
HardwareLoopProtocol.try_publish! — Function
try_publish!(ring, tag, body) -> BoolAppend 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.
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.
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).
HardwareLoopProtocol.commit! — Function
commit!(ring, view)Consume the peeked slot: advance the consumer's tail past it.
HardwareLoopProtocol.ring_head — Function
Slots the producer has published so far (its next index).
HardwareLoopProtocol.ring_tail — Function
The consumer's next index.
HardwareLoopProtocol.ring_available — Function
Slots published and not yet consumed (may exceed the capacity after a lap).
HardwareLoopProtocol.ring_space — Function
Free slots before the producer would overwrite unconsumed ones.
HardwareLoopProtocol.ring_capacity — Function
The number of slots in the ring (a power of two).
HardwareLoopProtocol.consumer_lost — Function
Slots the consumer had to skip because they had been overwritten.
HardwareLoopProtocol.producer_overruns — Function
Times the producer found the consumer more than a capacity behind.
Snapshots
HardwareLoopProtocol.SnapshotSlot — Type
SnapshotSlotA handle on one channel's seqlock-protected epoch-state snapshot. Obtain it with snapshot_slot.
HardwareLoopProtocol.snapshot_slot — Function
Hardware channel channel's epoch-state snapshot.
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.
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.
Navigation
HardwareLoopProtocol.navigation_mode — Function
navigation_mode(seg) -> UInt8What 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.
HardwareLoopProtocol.set_navigation_mode! — Function
set_navigation_mode!(seg, mode)Record the loop process's navigation mode (see navigation_mode). The loop process sets it once, before set_loop_state! marks it running, whose release store publishes it to a receiver that waits for that state.
HardwareLoopProtocol.nav_ring — Function
nav_ring(seg) -> RingThe loop-wide navigation event ring (loop → receiver): per navigation cycle one NavSatelliteEvent for every satellite the cycle knows, then its NavSolutionEvent, which commits the cycle. Empty unless the loop runs in navigation_mode NAV_VECTOR.
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.
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".
HardwareLoopProtocol.NavSolutionEvent — Type
NavSolutionEventOne 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 withNAV_TIME_VALID;position_std_m,clock_std_m— the filter's 1σ uncertainties (NaNwhile it is not running),time_with_insufficient_meas_sits coasting time;dop— GDOP, PDOP, VDOP, HDOP and TDOP,NaNwhen 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.
HardwareLoopProtocol.NavSatelliteEvent — Type
NavSatelliteEventOne 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_mandrate_residual_mps— from the solution,NaNunlessNAV_SAT_IN_SOLUTION;cn0_dbhz— at the latest epoch the satellite was snapshotted at;flags— theNAV_SAT_*bits;release_reason— one of theNAV_RELEASE_*codes, orNAV_NOT_RELEASED.
Heartbeats, state and process ids
HardwareLoopProtocol.loop_heartbeat! — Function
Stamp the loop process's heartbeat with time_ns() (monotonic within the host).
HardwareLoopProtocol.receiver_heartbeat! — Function
Stamp the receiver's heartbeat.
HardwareLoopProtocol.loop_heartbeat — Function
The loop process's last heartbeat in time_ns() nanoseconds, or 0 if never stamped.
HardwareLoopProtocol.receiver_heartbeat — Function
The receiver's last heartbeat in time_ns() nanoseconds, or 0 if never stamped.
HardwareLoopProtocol.heartbeat_alive — Function
heartbeat_alive(seg, side; stale_after_ns = 500_000_000, now_ns = time_ns()) -> BoolWhether side (:loop or :receiver) has heartbeated within stale_after_ns. A heartbeat of zero (never stamped) is not alive.
HardwareLoopProtocol.loop_state — Function
loop_state(seg) -> UInt64The 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.
HardwareLoopProtocol.set_loop_state! — Function
Publish the loop process's lifecycle state; see loop_state.
HardwareLoopProtocol.loop_pid — Function
The process id the loop process recorded with set_loop_pid!.
HardwareLoopProtocol.receiver_pid — Function
The process id the receiver recorded with set_receiver_pid!.
HardwareLoopProtocol.set_loop_pid! — Function
Record the loop process's process id in the header.
HardwareLoopProtocol.set_receiver_pid! — Function
Record the receiver's process id in the header.
Events
HardwareLoopProtocol.EventTag — Type
EventTagThe 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).
HardwareLoopProtocol.RecordEvent — Type
RecordEventWhat 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.
HardwareLoopProtocol.BitEvent — Type
BitEventOne 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.
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.
HardwareLoopProtocol.StatusEvent — Type
StatusEventA 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.
HardwareLoopProtocol.TapsEvent — Type
TapsEventThe 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).
Commands
HardwareLoopProtocol.CommandTag — Type
CommandTagThe 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.
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.
HardwareLoopProtocol.ConfigureCommand — Type
ConfigureCommandLoop-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.
HardwareLoopProtocol.ReleaseCommand — Type
Release channel k. Carries nothing beyond its tag.
HardwareLoopProtocol.ShutdownCommand — Type
Stop the loop process. Carries nothing beyond its tag.
HardwareLoopProtocol.QueryStateCommand — Type
Ask for a STATUS_CHANNEL_STATE event on every armed channel (adoption).
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.
HardwareLoopProtocol.NAME_BYTES — Constant
Bytes reserved for a fixed-length ASCII name (a signal or group id).