gps

package
v0.13.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2026 License: GPL-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package gps provides a unified GPS position cache with NMEA-serial and gpsd (TCP JSON) reader implementations. Beacon schedulers, SmartBeaconing, and REST endpoints read the latest fix through the PositionCache interface; reader goroutines push updates via Update.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ReadNMEAStream

func ReadNMEAStream(ctx context.Context, r io.Reader, cache PositionCache, logger *slog.Logger, opts NMEAOptions) error

ReadNMEAStream consumes NMEA sentences from r line-by-line, parses them, and pushes accepted fixes into cache. It handles partial lines across reads (bufio.Scanner) and logs malformed sentences at debug level, with a 1-minute rate-limited warn log for parse failures so an operator sees the first one of each surge without the log flooding. It returns when ctx is cancelled or r hits EOF.

func RunGPSD

func RunGPSD(ctx context.Context, cfg GPSDConfig, cache PositionCache, logger *slog.Logger) error

RunGPSD dials gpsd, issues ?WATCH={"enable":true,"json":true}, and feeds TPV reports into cache until ctx is cancelled.

func RunSerial

func RunSerial(ctx context.Context, cfg SerialConfig, cache PositionCache, logger *slog.Logger) error

RunSerial opens the serial port and feeds NMEA sentences into cache until ctx is cancelled. The port is always closed on return. On I/O error it returns the error; callers (cmd/graywolf) implement retry with backoff.

Types

type Fix

type Fix struct {
	Latitude  float64 // degrees, north positive
	Longitude float64 // degrees, east positive
	Altitude  float64 // metres above MSL
	HasAlt    bool
	Speed     float64   // knots (APRS/NMEA canonical unit)
	Heading   float64   // degrees true, 0..360
	HasCourse bool      // true if Speed/Heading are valid for this fix
	Timestamp time.Time // UTC
	FixMode   int       // 0=unknown, 1=no fix, 2=2D, 3=3D (from GSA)
	PDOP      float64   // position dilution of precision
	HDOP      float64   // horizontal dilution of precision
	VDOP      float64   // vertical dilution of precision
	HasDOP    bool      // true if DOP values are valid
}

Fix is a single GPS observation. Zero Timestamp indicates "no fix yet".

func ParseNMEA

func ParseNMEA(line string) (Fix, bool, error)

ParseNMEA parses a single NMEA sentence into a Fix. The input may optionally include the leading '$' and trailing "*HH" checksum; both styles are accepted. Returns an error if the checksum is invalid or the sentence type is unsupported/malformed. The returned bool reports whether the sentence contained an active fix (some RMC sentences are status='V' for "void").

type GPSDConfig

type GPSDConfig struct {
	Host string // default "localhost"
	Port int    // default 2947
	// OnParseError, if non-nil, is invoked for every JSON line that
	// fails to unmarshal into a TPV report. source is always "gpsd"
	// for the caller's convenience so the same callback can be shared
	// between the gpsd and NMEA readers.
	OnParseError func(source string)
}

GPSDConfig configures a gpsd (TCP JSON) reader.

type MemCache

type MemCache struct {
	// contains filtered or unexported fields
}

MemCache is a sync.RWMutex-protected in-memory PositionCache. The zero value is a valid empty cache.

func NewMemCache

func NewMemCache() *MemCache

NewMemCache returns an empty MemCache.

func (*MemCache) Get

func (c *MemCache) Get() (Fix, bool)

Get returns a copy of the latest fix.

func (*MemCache) GetSatellites

func (c *MemCache) GetSatellites() (SatelliteView, bool)

GetSatellites returns the latest satellite view.

func (*MemCache) Update

func (c *MemCache) Update(f Fix)

Update replaces the stored fix. A Fix with zero Timestamp is stamped with time.Now() so downstream code always sees a monotonic freshness.

func (*MemCache) UpdateSatellites

func (c *MemCache) UpdateSatellites(v SatelliteView)

UpdateSatellites replaces the stored satellite view.

type NMEAOptions

type NMEAOptions struct {
	OnParseError func(source string)
}

NMEAOptions configures ReadNMEAStream. OnParseError is optional and, when non-nil, is invoked once per malformed sentence — wired to the shared gps parse-errors counter in production. Kept as a separate option struct so adding more knobs later doesn't require a breaking signature change on every caller.

type PositionCache

type PositionCache interface {
	// Get returns the latest fix and whether any fix has been stored.
	Get() (Fix, bool)
	// Update stores a new fix. Readers call this from their goroutines.
	Update(Fix)
}

PositionCache is the read/write contract shared by readers and consumers. Implementations MUST be safe for concurrent use.

type PositionSource

type PositionSource int

PositionSource indicates where a position came from.

const (
	SourceNone  PositionSource = iota // no position available
	SourceGPS                         // live GPS receiver
	SourceFixed                       // static beacon coordinates
)

type SatelliteCache

type SatelliteCache interface {
	GetSatellites() (SatelliteView, bool)
	UpdateSatellites(SatelliteView)
}

SatelliteCache provides read/write access to satellite visibility data. Implementations MUST be safe for concurrent use.

type SatelliteInfo

type SatelliteInfo struct {
	PRN       int // satellite PRN/ID number
	Elevation int // degrees above horizon (0-90)
	Azimuth   int // degrees from true north (0-359)
	SNR       int // signal-to-noise dB-Hz (0-99), -1 if not tracking
}

SatelliteInfo describes a single satellite visible to the receiver.

type SatelliteView

type SatelliteView struct {
	Satellites []SatelliteInfo
	UpdatedAt  time.Time
}

SatelliteView is a snapshot of all satellites visible to the receiver, assembled from one or more complete GSV sentence cycles.

type SerialConfig

type SerialConfig struct {
	Device   string // e.g. /dev/ttyUSB0
	BaudRate int    // e.g. 4800, 9600, 38400
	// OnParseError, if non-nil, is invoked for every NMEA sentence
	// that fails to parse. source is always "nmea".
	OnParseError func(source string)
}

SerialConfig configures a NMEA-over-serial reader.

type SerialPortInfo

type SerialPortInfo struct {
	Path         string `json:"path"`        // device path, e.g. /dev/cu.usbserial-110
	Name         string `json:"name"`        // basename of path
	Description  string `json:"description"` // human-readable description
	IsUSB        bool   `json:"is_usb"`
	VID          string `json:"vid,omitempty"`
	PID          string `json:"pid,omitempty"`
	SerialNumber string `json:"serial_number,omitempty"`
	Product      string `json:"product,omitempty"`
	// Recommended is true for the device path users should pick. On macOS
	// we recommend the /dev/cu.* callout device over /dev/tty.* (which
	// blocks until DCD is asserted).
	Recommended bool `json:"recommended"`
	// Warning is set when there's a known gotcha with this path (e.g. the
	// macOS tty.* / cu.* distinction).
	Warning string `json:"warning,omitempty"`
}

SerialPortInfo describes one detected serial port for the web UI. Fields mirror the JSON shape returned by GET /api/gps/available.

func EnumerateSerialPorts

func EnumerateSerialPorts() ([]SerialPortInfo, error)

EnumerateSerialPorts returns the list of serial ports visible to the OS. Implementation is pure Go (no cgo) so it cross-compiles cleanly. On Linux we read /sys/class/tty/*/device to enrich USB devices with VID/PID/product strings; other platforms get the path-based heuristics only.

type StationPos

type StationPos struct {
	// contains filtered or unexported fields
}

StationPos is a PositionCache that layers a GPS cache over an optional fixed fallback position. Get returns the GPS fix when available; otherwise it returns the fallback (typically the station's fixed beacon coordinates). Update delegates to the underlying GPS cache.

func NewStationPos

func NewStationPos(gps *MemCache) *StationPos

NewStationPos wraps a GPS cache with an optional fixed-position fallback. The fallback is initially empty; call SetFallback to populate it from beacon configs.

func (*StationPos) Get

func (s *StationPos) Get() (Fix, bool)

Get returns the latest GPS fix if available, otherwise the fallback.

func (*StationPos) GetSatellites

func (s *StationPos) GetSatellites() (SatelliteView, bool)

GetSatellites delegates to the underlying GPS cache.

func (*StationPos) GetWithSource

func (s *StationPos) GetWithSource() (Fix, PositionSource)

GetWithSource is like Get but also reports the position source.

func (*StationPos) SetFallback

func (s *StationPos) SetFallback(f *Fix)

SetFallback sets the fixed-position fallback from a beacon's static coordinates. Pass nil to clear.

func (*StationPos) Update

func (s *StationPos) Update(f Fix)

Update delegates to the underlying GPS cache.

func (*StationPos) UpdateSatellites

func (s *StationPos) UpdateSatellites(v SatelliteView)

UpdateSatellites delegates to the underlying GPS cache.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL