Go packages

The Go packages are organized into three main hierarchies: gps/ for GPS processing (no external dependencies), time/ for time synchronization (depends on gps/), and internal/ for satpulsetool subcommands (depends on both).

Within each hierarchy, packages are organized into layers where each layer depends only on packages in the same or lower layers.

Command-line layer provides the user interface to the programs, including the command-line interface and the configuration file.

Application layer provides the main blocks of the applications.

Domain layer provides packages using domain-specific abstractions that are used by the application layer. These packages have mutual dependencies. They do not make use of goroutines nor do they perform logging.

Library layer provides a library of packages, which are potentially useful outside satpulse. There are few mutual dependencies. These packages do not make use of goroutines nor do they perform logging.

The following sections describe each directory that contains packages.

cmd/

These packages provide entry points for the SatPulse executables. They are in the command-line layer.

cmd/satpulsed provides main for satpulsed.

cmd/satpulsetool provides main for satpulsetool.

cmd/ifwait provides a program that waits for a network interface to become ready. It exercises the functionality of the time/lib/ifwait package.

gps/

These packages provide the public API for GPS processing. They are in the domain layer.

gps/ptime provides a Time type that represents time in the PTP timescale (nanoseconds in TAI timescale since 1970-01-01T00:00:00 TAI). This is used throughout the domain layer and higher level layers.

gps/scan provides a Packet type representing a packet of GPS data in some protocol. It also provides a scan function to split up a stream of bytes into packets. It does not interpret the content of the packet.

gps/gpsprot abstracts a GPS protocol such as UBX or NMEA. This operates in two phases: configuration and post-configuration. The post-configuration part of this defines types that represent the information contained in GPS messages in a protocol-independent way.

gps/gpsreg provides a registry for the various implementations of the gps/gpsprot layer. Higher layers avoid interacting with the implementations for specific protocols as much as possible. Generally the command-line layer interacts with gps/gpsreg, and passes the appropriate implementations into lower layers.

gps/gpsdecode decodes binary GPS packets into JSON-serializable maps derived from the Go structs defined in the library layer packages.

gps/nmeasyn synthesizes NMEA sentences from gpsprot messages.

gps/msgfile parses TOML message files that describe GPS messages to send to a receiver. It handles multiple protocol types (UBX, CASBIN, ASBIN, NMEA, line, binary), applies per-type defaults, and converts typed messages into raw bytes ready to send. Messages are organized by tags for selective sending.

gps/ts generates TypeScript type definitions for the JSON values serialized from types in the gps/* packages.

gps/app/

These packages provide GPS orchestration and CLI infrastructure. They are in the application layer.

gps/app/gpsio implements a goroutine that reads input from the GPS, splits it into packets and sends those packets to a channel. It provides an abstraction for performing IO on a GPS, which can work either over a serial connection (using gps/lib/term) or over a network connection. The input is split into packets using the gps/scan package.

gps/app/gpscfg drives the GPS configuration process. It combines gps/app/gpsio and gps/gpsprot.

gps/app/cmd provides some common functionality used for command-line interfaces.

gps/app/logfile provides utility functions for opening and reopening log files.

gps/app/bcast provides a concurrency abstraction that broadcasts a channel to multiple other channels. This is used for routing packets inside the application. At the moment it is used by satpulsed rather than satpulsetool, but it is useful for applications dealing with GPS packets.

gps/app/stream manages correction and packet streams. It pulls RTCM streams from an Ntrip or TCP network endpoint and feeds them to the GPS receiver over the serial port, pushes streams from the GPS receiver to a remote Ntrip network endpoint, and provides selected-GGA helpers for consumers that need current receiver position.

gps/app/ntrip implements an Ntrip caster for serving RTCM packet streams from a GPS receiver to Ntrip clients. It includes an STR record generation capability, which is also used by gps/app/stream.

gps/internal/

These packages implement the gpsprot interface for specific protocols. They are in the domain layer and are not importable outside gps/.

gps/internal/ubx implements gps/gpsprot abstractions for the UBX protocol. It uses gps/lib/ubxbin and gps/lib/ubxcfgval to do this.

gps/internal/nmea implements gps/gpsprot abstractions for the NMEA protocol.

gps/internal/rtcm implements gps/gpsprot abstractions for the RTCM protocol. It uses gps/lib/rtcmbin for field extraction.

gps/internal/spartn implements gps/gpsprot abstractions for the SPARTN protocol. It uses gps/lib/spartnbin for field extraction.

gps/internal/casic implements gps/gpsprot abstractions for the CASIC binary protocol. It uses gps/lib/casbin to do this.

gps/internal/unc implements gps/gpsprot abstractions for the Unicore protocol. It uses gps/lib/uncmsg to parse Unicore binary and ASCII message formats.

gps/internal/nov implements gps/gpsprot abstractions for the NovAtel protocol. It uses gps/lib/novmsg to parse binary and ASCII NovAtel packets.

gps/internal/sino provides satellite numbering schemes for SinoGNSS receivers, defining NMEA satellite ID mappings for GLONASS, NavIC, Galileo, QZSS, BeiDou, and SBAS.

gps/internal/as provides NMEA satellite numbering configuration for Allystar GPS receivers.

gps/internal/quectel converts PQTM NMEA messages from Quectel GPS receivers into gps/gpsprot message format.

gps/internal/scantest provides utility functions for testing GPS packet format implementations. It includes functions to find packets within buffers and insert random data prefixes for robustness testing.

gps/lib/

These packages are reusable libraries for GPS processing. They are in the library layer.

gps/lib/ubxbin translates binary packets in the UBX protocol to and from Go structs.

gps/lib/ubxcfgval handles the 9th generation UBX format for configuration data that is payload for UBX-CFG-VALGET/VALSET messages.

gps/lib/ubxcfgval/cfgschema contains a YAML schema for configuration data handled by gps/lib/ubxcfgval. This is used to generate code in the gps/lib/ubxcfgval package.

gps/lib/casbin translates binary packets in the CASIC protocol to and from Go structs.

gps/lib/asbin translates binary packets in the Allystar binary protocol to and from Go structs.

gps/lib/bitsenc provides reflection-based decoding of bit-packed binary data into Go structs. It supports unsigned and signed integers, bools, and embedded structs.

gps/lib/rtcmbin parses and serializes RTCM binary packets using gps/lib/bitsenc, including message types 1005/1006, 1230, and MSM. It also provides MSM7-to-MSM4 conversion.

gps/lib/spartnbin parses the SPARTN transport frame envelope and computes its CRCs using gps/lib/bitsenc, returning the (possibly encrypted) message payload as opaque bytes.

gps/lib/rinex defines an intermediate, RINEX-adjacent representation of observation data as JSON-serializable Go types, and reads and writes it as RINEX observation files.

gps/lib/rnxrtcm converts RTCM MSM7 observation messages to gps/lib/rinex records. It uses gps/lib/rtcmbin to decode the source messages.

gps/lib/rnxubx converts u-blox raw observation messages to gps/lib/rinex records. It uses gps/lib/ubxbin to decode the source messages.

gps/lib/rnxunc converts Unicore raw observation messages to gps/lib/rinex records. It uses gps/lib/uncmsg to decode the source messages.

gps/lib/novmsg provides parsing and serialization of NovAtel GPS receiver messages in binary and ASCII formats. It defines message header and body types and implements CRC32 validation.

gps/lib/uncmsg parses Unicore protocol messages in binary and ASCII formats. It defines message structures and provides parsing/serialization using gps/lib/novmsg.

gps/lib/nmeamsg analyzes NMEA sentence syntax, computes checksums, and decodes and serializes typed approved GNSS-talker sentences such as GGA and RMC.

gps/lib/airmsg classifies responses to Airoha proprietary PAIR NMEA commands.

gps/lib/qtmmsg parses Quectel NMEA PQTM messages.

gps/lib/opt provides a generic optional value type for use in serialized structs.

gps/lib/geopos converts between positions in the ECEF and LLH geodetic coordinate systems. This is used by the web interface to link to Google maps.

gps/lib/fieldenc provides reflection-based encoding and decoding of Go structs to and from ordered string field arrays. It supports standard types and custom TextMarshaler/TextUnmarshaler implementations.

gps/lib/ntptime reads the Linux kernel’s NTP synchronization state via the adjtimex syscall and exposes it as platform-independent types. It provides information about system clock synchronization and leap second status.

gps/lib/term provides access to the Linux terminal interface, which provides access to serial devices. This is similar to github.com/pkg/term, but provides additional Linux-specific functionality.

gps/lib/decconv converts between base-10 decimal strings and int64 scaled integers without floating point. It is used for exact parsing and formatting of physical quantities like angles and lengths.

gps/lib/latin1z provides fixed-size byte-array types (StringZ5, StringZ10, …) for nul-terminated ISO 8859-1 (Latin-1) strings, with JSON serialization. The typed arrays are generated by mksizes.go.

gps/lib/ascii provides ctypes-style classification and conversion of ASCII bytes (the C <ctype.h> repertoire restricted to ASCII), using lookup tables built in init(). It supersedes the ad-hoc digit/hex/alpha/printable helpers that were scattered across the GPS protocol parsers.

time/

These packages provide the public API for time synchronization. They are in the domain layer.

time/phc provides low level abstractions to access the PTP hardware clock. It is highly Linux dependent. It uses gps/ptime.

time/sockrefclock implements the chrony refclock protocol. It uses gps/ptime.

time/lib/ntpshm implements the ntpd/NTPsec SHM refclock writer. It uses gps/ptime.

time/clocksim provides discrete-time simulation of PTP hardware clocks and GNSS PPS signals. It includes simulator functions for oscillators (modeling frequency errors like white noise, flicker noise, random walk, drift) and for GPS/PPS timing errors (jitter, sawtooth, sinusoids, colored noise).

time/phctime provides an Era type used for managing stepping of a PHC and types that combine Era with ptime.Time.

time/app/

These packages provide daemon orchestration and CLI. They are in the command layer.

time/app/daemon implements the satpulsed daemon. It orchestrates all the parts of the satpulsed program. It also handles the TOML config file and provides HTTP endpoints for the web interface and metrics.

time/app/syncsimcmd implements the syncsim subcommand of satpulsetool. It parses configuration and command-line arguments, then orchestrates a discrete-event simulation of the synchronization system using time/internal/syncsim.

time/internal/

These packages are the main building blocks for satpulsed; they are in the application layer and are not importable outside time/.

time/internal/ts implements a goroutine that reads external timestamps from the PTP hardware clock and sends those to a channel. These external timestamps are the time pulses emitted by the GPS receiver.

time/internal/gpsevent provides the main event handling loop after GPS configuration is done. It receives GPS packets from gps/app/gpsio and then uses the appropriate protocol implementation to construct protocol-independent messages that it passes to time/internal/phcsync. It also receives timestamps from time/internal/ts and passes them to time/internal/phcsync.

time/internal/phcsync provides the core functionality of synchronizing the PTP hardware clock. It receives timestamps from gpsevent and accesses GPS messages via time/internal/timemsg.

time/internal/timemsg buffers recent GPS time messages from a receiver and provides methods to retrieve consecutive, second-aligned messages and pulse offset corrections for time synchronization. It receives messages from time/internal/gpsevent and provides them to time/internal/phcsync. It isolates time/internal/phcsync from the complexities of gps/gpsprot.

time/internal/ptpgm manages the PTP grandmaster state and synchronization status as seen by ptp4l. It provides a worker goroutine that sends updates to ptp4l via the PTP management protocol.

time/internal/refclock provides abstractions for sending clock synchronization samples to external time synchronization services like chrony. It includes a worker goroutine that processes samples from a channel and delivers them to configured refclock implementations.

time/internal/syncsim provides a discrete-event simulator for testing the phcsync controller with configurable error models for GPS PPS timing and PHC oscillator characteristics. It generates synthetic pulses, messages, and ticks under various fault conditions and measures controller performance.

time/internal/obs provides unified observability interfaces including Observer (which extends phcsync.Sampler and gpsprot.Handler) for receiving both clock synchronization samples and GPS protocol messages.

time/internal/sseobs implements the Observer interface to generate Server-Sent Events data that the daemon uses for the web interface.

time/internal/promobs implements the Observer interface to collect Prometheus metrics that the daemon exposes via HTTP handlers.

time/internal/logobs implements the Observer interface to record clock synchronization samples to log files and emit statistical summaries via structured logging. It provides ClockLogObserver for per-sample clock data and StatsLogObserver for interval-based summaries.

time/internal/statsobs accumulates clock synchronization statistics including phase offset (maximum, mean, RMS), frequency deviation (mean, standard deviation), and frequency delta characteristics. It implements phcsync.Sampler for data collection.

time/internal/proxy implements proxying of GPS packets to TCP and Unix domain sockets.

time/lib/

These packages are reusable libraries for time synchronization. They are in the library layer.

time/lib/pmc implements a PTP management client.

time/lib/ifwait uses the Linux kernel’s netlink subsystem to wait for changes in a network interface’s status.

time/lib/devnotify uses the Linux kernel’s netlink subsystem to listen for creation of new devices by udev. (This is not used currently.)

time/lib/sse marshals data into the format of HTML SSE (server-sent events).

time/lib/allan computes Allan deviations. (This is not used currently.)

time/lib/check validates struct fields against constraints specified in struct tags using reflection. It supports numeric types with comparison operators (>, >=, <, <=) and recursively validates nested structs.

time/lib/circbuf provides a generic circular buffer that maintains a sliding window of recent samples. It supports appending with automatic overflow handling and reverse chronological iteration.

time/lib/fuser finds processes that have a specific file or directory open by examining the Linux /proc filesystem. It provides functionality similar to the Unix fuser utility.

time/lib/median provides efficient median computation for a fixed-size moving window using a circular buffer with a sorted index array. It supports 64-bit integers, floats, and time.Duration.

time/lib/ntime provides a domain-neutral nanosecond timestamp type for the refclock sample path. The domain of a value (UTC, TAI, PHC-raw) is determined by the producer and consumer, not by the type. It depends only on the standard library.

internal/

These packages implement subcommands of satpulsetool. They are in the command-line layer and can import from both gps/ and time/.

internal/gpscmd implements gps subcommand of satpulsetool.

internal/annotatecmd implements annotate subcommand of satpulsetool. It annotates JSONL packet logs with decoded payload fields (header, payload, cfgData).

internal/convobscmd implements convobs subcommand of satpulsetool. It converts raw and JSON observation streams.

internal/decodecmd implements decode subcommand of satpulsetool. It decodes a single GPS packet from hex or ASCII data into JSON.

internal/ntripcmd implements ntrip subcommand of satpulsetool. It fetches data from an Ntrip caster and writes either a JSONL packet log or raw bytes to stdout.

internal/packcmd implements pack subcommand of satpulsetool. It reads a JSONL packet log and writes selected packets as a raw byte stream, optionally preserving inter-packet timing.

internal/scancmd implements scan subcommand of satpulsetool. It reads a raw packet byte stream, splits it using the GPS packet scanner, and writes a JSONL packet log.

internal/pmccmd implements pmc subcommand of satpulsetool.

internal/replaycmd implements replay subcommand of satpulsetool. It replays a JSONL packet log, generating JSONL events similar to an event log.

internal/sdpcmd implements the sdp subcommand of satpulsetool. It provides interfaces to manage software-defined pins (SDPs) on PTP hardware clocks, including listing available interfaces and pins, capturing external timestamps, configuring periodic output, and disabling pins.

web/

This package provides the web interface. It is in the application layer.

web embeds the HTML/JavaScript code for the web interface. This code is transpiled from TypeScript and uses Preact JavaScript library.

Test harnesses

gpshwtest is a stdlib-only Python program that tests GPS high-level configuration against real receivers. Because receivers are diverse and high-level configuration has best-effort semantics, it characterizes how device-independent configuration is realized on each receiver rather than rendering pass/fail verdicts; vetted characterizations are checked in and compared on later runs. All receiver I/O goes through satpulsetool gps --json. The goal and success criteria are defined in gpshwtest/GOAL.md (#310).