lidar

package
v0.4.0 Latest Latest
Warning

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

Go to latest
Published: Jan 29, 2026 License: Apache-2.0 Imports: 22 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// Height thresholds (meters)
	BirdHeightMax       = 0.5 // Birds are typically small
	PedestrianHeightMin = 1.0 // Pedestrians are at least 1m tall
	PedestrianHeightMax = 2.2 // Pedestrians are typically under 2.2m
	VehicleHeightMin    = 1.2 // Vehicles are at least 1.2m
	VehicleLengthMin    = 3.0 // Vehicles are at least 3m long
	VehicleWidthMin     = 1.5 // Vehicles are at least 1.5m wide

	// Speed thresholds (m/s)
	BirdSpeedMax       = 1.0 // Birds detected at low speeds
	PedestrianSpeedMax = 3.0 // Pedestrians walk up to ~3 m/s (10.8 km/h)
	VehicleSpeedMin    = 5.0 // Vehicles typically move faster than 5 m/s
	StationarySpeedMax = 0.5 // Stationary threshold

	// Confidence levels
	HighConfidence   = 0.85
	MediumConfidence = 0.70
	LowConfidence    = 0.50

	// Minimum observations for classification
	MinObservationsForClassification = 5
)

Classification thresholds (configurable for tuning)

View Source
const (
	// DefaultDBSCANEps is the default neighborhood radius in meters for DBSCAN
	DefaultDBSCANEps = 0.6
	// DefaultDBSCANMinPts is the default minimum points to form a cluster
	DefaultDBSCANMinPts = 12
	// EstimatedPointsPerCell is used for initial spatial index capacity estimation
	EstimatedPointsPerCell = 4
)

Constants for clustering configuration

View Source
const (
	// DefaultClosenessSensitivityMultiplier is the default multiplier for closeness threshold
	DefaultClosenessSensitivityMultiplier = 3.0
	// DefaultNeighborConfirmationCount is the default number of neighbors needed for confirmation
	DefaultNeighborConfirmationCount = 3
	// FreezeThresholdMultiplier is the multiplier applied to closeness threshold to trigger cell freeze
	FreezeThresholdMultiplier = 3.0
	// DefaultReacquisitionBoostMultiplier is the default multiplier for fast re-acquisition
	DefaultReacquisitionBoostMultiplier = 5.0
	// DefaultMinConfidenceFloor is the minimum TimesSeenCount to preserve during foreground
	DefaultMinConfidenceFloor = 3
	// ThawGracePeriodNanos is the minimum time after freeze expiry before thaw detection triggers.
	// This prevents false triggers when FreezeDurationNanos=0 causes immediate "expiry".
	ThawGracePeriodNanos = int64(1_000_000) // 1ms
	// DefaultLockedBaselineThreshold is the minimum observations before locking baseline
	DefaultLockedBaselineThreshold = 50
	// DefaultLockedBaselineMultiplier defines the acceptance window as LockedSpread * multiplier
	DefaultLockedBaselineMultiplier = 4.0
)

Constants for foreground extraction configuration

View Source
const (
	// MinAzimuthCoverage is the minimum azimuth coverage (degrees) required for a valid frame
	// Must cover at least 340° of a full 360° rotation to be considered complete
	MinAzimuthCoverage = 340.0

	// MinFramePointsForCompletion is the minimum number of points required for frame completion
	// Ensures substantial data before declaring a rotation complete (typical full rotation: ~70k points)
	MinFramePointsForCompletion = 10000
)

Frame detection constants for azimuth-based rotation detection

View Source
const (
	// MinDeterminantThreshold is the minimum determinant for covariance matrix inversion
	MinDeterminantThreshold = 1e-6
	// SingularDistanceRejection is the distance returned when covariance is singular
	SingularDistanceRejection = 1e9
	// MaxSpeedHistoryLength is the maximum number of speed samples kept for percentile computation
	MaxSpeedHistoryLength = 100
	// DefaultDeletedTrackGracePeriod is how long to keep deleted tracks before cleanup
	DefaultDeletedTrackGracePeriod = 5 * time.Second
	// MaxReasonableSpeedMps is the maximum reasonable speed for any tracked object (m/s)
	// Used to reject spurious associations that would imply impossible velocities
	MaxReasonableSpeedMps = 30.0 // ~108 km/h, ~67 mph
	// MaxPositionJumpMeters is the maximum allowed position jump between consecutive observations
	// Observations beyond this distance are rejected as likely false associations
	MaxPositionJumpMeters = 5.0
)

Constants for tracker configuration

View Source
const CompactPointSize = 8

CompactPointSize is the size in bytes of a single compact point.

Variables

View Source
var IdentityTransform4x4 = [16]float64{1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1}

IdentityTransform4x4 is a 4x4 identity matrix for pose transforms. T is row-major: [m00,m01,m02,m03, m10,m11,m12,m13, m20,m21,m22,m23, m30,m31,m32,m33]

Functions

func ApplyPose

func ApplyPose(x, y, z float64, T [16]float64) (wx, wy, wz float64)

ApplyPose applies a 4x4 row-major transform T to point (x,y,z). T is expected as [16]float64 row-major: m00,m01,m02,m03, m10,...

func ClearTracks

func ClearTracks(db *sql.DB, sensorID string) error

ClearTracks removes all tracks, observations, and clusters for a sensor. This is intended for development/debug resets and should not be exposed in production without auth.

func ComputeSpeedPercentiles

func ComputeSpeedPercentiles(speedHistory []float32) (p50, p85, p95 float32)

ComputeSpeedPercentiles computes speed percentiles from a track's speed history. Uses floor-based indexing for percentiles. For small arrays (n<3), all percentiles may return similar values. For production use with precise percentile requirements, consider using linear interpolation between neighboring values.

func Debugf

func Debugf(format string, args ...interface{})

Debugf is an exported helper for callers outside the lidar package.

func EncodeForegroundBlob

func EncodeForegroundBlob(points []PointPolar) []byte

EncodeForegroundBlob encodes foreground points to a compact binary blob. Format: Each point is 8 bytes: distance(2) + azimuth(2) + elevation(2) + intensity(1) + ring(1)

func EncodePandar40PPacket

func EncodePandar40PPacket(points []PointPolar, blockAzimuth float64, config *SensorConfig) ([]byte, error)

EncodePandar40PPacket encodes polar points into a Pandar40P-compatible UDP packet format. This allows exported tracks to be loaded in LidarView for visual inspection.

Pandar40P packet structure (1262 bytes): - 10 data blocks × 124 bytes each - Each block: 2-byte preamble (0xFFEE) + 2-byte azimuth + 40 channels × 3 bytes - 22-byte tail with timestamp and metadata

func ExportBgSnapshotToASC

func ExportBgSnapshotToASC(snap *BgSnapshot, ringElevations []float64) (string, error)

ExportBgSnapshotToASC decodes a BgSnapshot's grid blob, constructs a temporary BackgroundGrid and BackgroundManager, supplies per-ring elevations (preferring a live BackgroundManager and falling back to embedded parser config), and exports the resulting points to an ASC file. Returns the path where the file was written.

func ExportForegroundSnapshotToASC

func ExportForegroundSnapshotToASC(snap *ForegroundSnapshot) (string, error)

ExportForegroundSnapshotToASC writes only the foreground points to an ASC file. This is intended for quick inspection of live/replayed foreground extraction. Returns the path where the file was written.

func ExportPointsToASC

func ExportPointsToASC(points []PointASC, extraHeader string) (string, error)

ExportPointsToASC exports a slice of PointASC to a CloudCompare-compatible .asc file. The export path is generated internally using a timestamp and random suffix to prevent path traversal attacks. Returns the actual path where the file was written. extraHeader is a string describing extra columns (optional)

func InsertCluster

func InsertCluster(db *sql.DB, cluster *WorldCluster) (int64, error)

InsertCluster inserts a cluster into the database and returns its ID.

func InsertTrack

func InsertTrack(db *sql.DB, track *TrackedObject, worldFrame string) error

InsertTrack inserts a new track into the database.

func InsertTrackObservation

func InsertTrackObservation(db *sql.DB, obs *TrackObservation) error

InsertTrackObservation inserts a track observation into the database.

func RegisterAnalysisRunManager

func RegisterAnalysisRunManager(sensorID string, manager *AnalysisRunManager)

RegisterAnalysisRunManager registers a manager for a sensor ID.

func RegisterBackgroundManager

func RegisterBackgroundManager(sensorID string, mgr *BackgroundManager)

RegisterBackgroundManager registers a BackgroundManager for a sensor ID.

func RegisterFrameBuilder

func RegisterFrameBuilder(sensorID string, fb *FrameBuilder)

RegisterFrameBuilder registers a FrameBuilder for a sensor ID.

func SetDebugLogger

func SetDebugLogger(w io.Writer)

SetDebugLogger installs a debug logger that receives verbose LiDAR diagnostics. Pass nil to disable debug logging.

func SphericalToCartesian

func SphericalToCartesian(distance, azimuthDeg, elevationDeg float64) (x, y, z float64)

SphericalToCartesian converts distance (meters), azimuth (degrees) and elevation (degrees) into Cartesian sensor-frame coordinates. Coordinate convention: X=right, Y=forward, Z=up (matches existing code).

func StoreForegroundSnapshot

func StoreForegroundSnapshot(sensorID string, ts time.Time, foreground []PointPolar, background []PointPolar, totalPoints int, foregroundPoints int)

StoreForegroundSnapshot saves the latest foreground/background projections for a sensor. Points are projected in sensor frame (using az/el) to align with background polar charts.

func UpdateTrack

func UpdateTrack(db *sql.DB, track *TrackedObject, worldFrame string) error

UpdateTrack updates an existing track in the database.

func WriteNetworkStream

func WriteNetworkStream(packets [][]byte, destAddr string, config *SensorConfig) error

WriteNetworkStream sends packets to a UDP destination for real-time inspection. Allows streaming isolated track data to LidarView without intermediate files.

func WritePCAPFile

func WritePCAPFile(packets [][]byte, outputPath string, config *SensorConfig) error

WritePCAPFile writes a sequence of packets to a PCAP file for LidarView inspection. This is a stub - actual implementation requires pcap library integration.

Types

type AcceptanceMetrics

type AcceptanceMetrics struct {
	BucketsMeters []float64
	AcceptCounts  []int64
	RejectCounts  []int64
}

AcceptanceMetrics exposes the acceptance/rejection counts per range bucket.

type AnalysisRun

type AnalysisRun struct {
	RunID            string          `json:"run_id"`
	CreatedAt        time.Time       `json:"created_at"`
	SourceType       string          `json:"source_type"` // "pcap" or "live"
	SourcePath       string          `json:"source_path,omitempty"`
	SensorID         string          `json:"sensor_id"`
	ParamsJSON       json.RawMessage `json:"params_json"`
	DurationSecs     float64         `json:"duration_secs"`
	TotalFrames      int             `json:"total_frames"`
	TotalClusters    int             `json:"total_clusters"`
	TotalTracks      int             `json:"total_tracks"`
	ConfirmedTracks  int             `json:"confirmed_tracks"`
	ProcessingTimeMs int64           `json:"processing_time_ms"`
	Status           string          `json:"status"` // "running", "completed", "failed"
	ErrorMessage     string          `json:"error_message,omitempty"`
	ParentRunID      string          `json:"parent_run_id,omitempty"`
	Notes            string          `json:"notes,omitempty"`
}

AnalysisRun represents a complete analysis session with parameters. All LIDAR parameters are stored in ParamsJSON for full reproducibility.

type AnalysisRunManager

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

AnalysisRunManager coordinates analysis run lifecycle and track collection. It is safe for concurrent use and provides hooks for the tracking pipeline.

func GetAnalysisRunManager

func GetAnalysisRunManager(sensorID string) *AnalysisRunManager

GetAnalysisRunManager retrieves the manager for a sensor ID.

func NewAnalysisRunManager

func NewAnalysisRunManager(db *sql.DB, sensorID string) *AnalysisRunManager

NewAnalysisRunManager creates a new manager for tracking analysis runs.

func (*AnalysisRunManager) CompleteRun

func (m *AnalysisRunManager) CompleteRun() error

CompleteRun finalizes the current analysis run with statistics.

func (*AnalysisRunManager) CurrentRunID

func (m *AnalysisRunManager) CurrentRunID() string

CurrentRunID returns the current run ID, or empty string if no run is active.

func (*AnalysisRunManager) FailRun

func (m *AnalysisRunManager) FailRun(errMsg string) error

FailRun marks the current run as failed with an error message.

func (*AnalysisRunManager) GetCurrentRunParams

func (m *AnalysisRunManager) GetCurrentRunParams() (RunParams, bool)

GetCurrentRunParams retrieves the current run's parameters for display.

func (*AnalysisRunManager) IsRunActive

func (m *AnalysisRunManager) IsRunActive() bool

IsRunActive returns true if there's an active analysis run.

func (*AnalysisRunManager) RecordClusters

func (m *AnalysisRunManager) RecordClusters(count int)

RecordClusters increments the cluster count for the current run.

func (*AnalysisRunManager) RecordFrame

func (m *AnalysisRunManager) RecordFrame()

RecordFrame increments the frame count for the current run.

func (*AnalysisRunManager) RecordTrack

func (m *AnalysisRunManager) RecordTrack(track *TrackedObject) bool

RecordTrack records a track for the current analysis run. This inserts a RunTrack record and returns true if this is a new track.

func (*AnalysisRunManager) StartRun

func (m *AnalysisRunManager) StartRun(sourcePath string, params RunParams) (string, error)

StartRun begins a new analysis run for PCAP processing. It returns the run ID that can be used for track association.

type AnalysisRunStore

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

AnalysisRunStore provides persistence for analysis runs.

func NewAnalysisRunStore

func NewAnalysisRunStore(db *sql.DB) *AnalysisRunStore

NewAnalysisRunStore creates a new AnalysisRunStore.

func (*AnalysisRunStore) CompleteRun

func (s *AnalysisRunStore) CompleteRun(runID string, stats *AnalysisStats) error

CompleteRun marks a run as completed with final statistics.

func (*AnalysisRunStore) GetLabelingProgress

func (s *AnalysisRunStore) GetLabelingProgress(runID string) (total, labeled int, byClass map[string]int, err error)

GetLabelingProgress returns labeling statistics for a run.

func (*AnalysisRunStore) GetRun

func (s *AnalysisRunStore) GetRun(runID string) (*AnalysisRun, error)

GetRun retrieves an analysis run by ID.

func (*AnalysisRunStore) GetRunTracks

func (s *AnalysisRunStore) GetRunTracks(runID string) ([]*RunTrack, error)

GetRunTracks retrieves all tracks for an analysis run.

func (*AnalysisRunStore) GetUnlabeledTracks

func (s *AnalysisRunStore) GetUnlabeledTracks(runID string, limit int) ([]*RunTrack, error)

GetUnlabeledTracks returns tracks that need labeling.

func (*AnalysisRunStore) InsertRun

func (s *AnalysisRunStore) InsertRun(run *AnalysisRun) error

InsertRun creates a new analysis run.

func (*AnalysisRunStore) InsertRunTrack

func (s *AnalysisRunStore) InsertRunTrack(track *RunTrack) error

InsertRunTrack inserts a track for an analysis run. Uses retry logic to handle SQLITE_BUSY errors from concurrent writes.

func (*AnalysisRunStore) ListRuns

func (s *AnalysisRunStore) ListRuns(limit int) ([]*AnalysisRun, error)

ListRuns retrieves recent analysis runs.

func (*AnalysisRunStore) UpdateRunStatus

func (s *AnalysisRunStore) UpdateRunStatus(runID, status, errorMsg string) error

UpdateRunStatus updates the status of an analysis run.

func (*AnalysisRunStore) UpdateTrackLabel

func (s *AnalysisRunStore) UpdateTrackLabel(runID, trackID, userLabel string, confidence float32, labelerID string) error

UpdateTrackLabel updates the user label for a track.

func (*AnalysisRunStore) UpdateTrackQualityFlags

func (s *AnalysisRunStore) UpdateTrackQualityFlags(runID, trackID string, isSplit, isMerge bool, linkedIDs []string) error

UpdateTrackQualityFlags updates the split/merge flags for a track.

type AnalysisStats

type AnalysisStats struct {
	DurationSecs     float64
	TotalFrames      int
	TotalClusters    int
	TotalTracks      int
	ConfirmedTracks  int
	ProcessingTimeMs int64
}

AnalysisStats holds statistics for a completed analysis run.

type BackgroundCell

type BackgroundCell struct {
	AverageRangeMeters   float32
	RangeSpreadMeters    float32
	TimesSeenCount       uint32
	LastUpdateUnixNanos  int64
	FrozenUntilUnixNanos int64
	// RecentForegroundCount tracks consecutive foreground observations.
	// Used for fast re-acquisition: when this is >0 and observation matches
	// background, we apply boosted alpha for faster convergence.
	RecentForegroundCount uint16

	// LockedBaseline is the stable reference distance that only updates when
	// we have high confidence (TimesSeenCount > LockedThreshold). This protects
	// against transit corruption where EMA average drifts during occlusion.
	LockedBaseline float32
	// LockedSpread is the acceptable variance around LockedBaseline. Observations
	// within LockedBaseline ± (LockedSpread * multiplier) are considered background.
	// This allows per-cell variance (trees, glass have more variance).
	LockedSpread float32
	// LockedAtCount is the TimesSeenCount when baseline was last locked.
	// Used to detect when we should update the locked values.
	LockedAtCount uint32
}

BackgroundCell matches the compressed storage format for schema persistence

type BackgroundGrid

type BackgroundGrid struct {
	SensorID    string
	SensorFrame FrameID // e.g., "sensor/hesai-01"

	Rings       int // e.g., 40 - matches schema rings INTEGER NOT NULL
	AzimuthBins int // e.g., 1800 for 0.2° - matches schema azimuth_bins INTEGER NOT NULL

	Cells []BackgroundCell // len = Rings * AzimuthBins

	Params BackgroundParams

	// Enhanced persistence tracking matching schema lidar_bg_snapshot table
	Manager              *BackgroundManager
	LastSnapshotTime     time.Time
	ChangesSinceSnapshot int
	SnapshotID           *int64 // tracks last persisted snapshot_id from schema

	// Performance tracking for system_events table integration
	LastProcessingTimeUs  int64
	WarmupFramesRemaining int
	SettlingComplete      bool

	// Telemetry for monitoring (feeds into system_events)
	ForegroundCount int64
	BackgroundCount int64

	// Simple range-bucketed acceptance metrics to help tune NoiseRelativeFraction.
	// Buckets are upper bounds in meters; counts are number of accepted/rejected
	// observations that fell into that distance bucket. These are incremented
	// inside ProcessFramePolar while holding g.mu, and can be read via
	// BackgroundManager.GetAcceptanceMetrics().
	AcceptanceBucketsMeters []float64
	AcceptByRangeBuckets    []int64
	RejectByRangeBuckets    []int64

	// Optional per-ring elevation angles (degrees) for converting polar->cartesian.
	// If populated (len == Rings) ToASCPoints will use these to compute Z = r*sin(elev).
	RingElevations []float64
	// LastObservedNoiseRel tracks the last noise_relative value observed by
	// ProcessFramePolar so we can log when the runtime value changes.
	LastObservedNoiseRel float32

	// RegionMgr handles adaptive parameter regions for different settling characteristics
	RegionMgr *RegionManager
	// contains filtered or unexported fields
}

BackgroundGrid enhanced for schema persistence and 100-track performance

func (*BackgroundGrid) Idx

func (g *BackgroundGrid) Idx(ring, azBin int) int

Helper to index Cells: idx = ring*AzimuthBins + azBin

type BackgroundManager

type BackgroundManager struct {
	Grid            *BackgroundGrid
	SettlingTimer   *time.Timer
	PersistTimer    *time.Timer
	HasSettled      bool
	LastPersistTime time.Time
	StartTime       time.Time

	// Persistence callback to main app - should save to schema lidar_bg_snapshot table
	PersistCallback func(snapshot *BgSnapshot) error
	// EnableDiagnostics controls whether this manager emits diagnostic messages
	// via the shared monitoring logger. Default: false.
	EnableDiagnostics bool
	// contains filtered or unexported fields
}

BackgroundManager handles automatic persistence following schema lidar_bg_snapshot pattern

func GetBackgroundManager

func GetBackgroundManager(sensorID string) *BackgroundManager

GetBackgroundManager returns a registered manager or nil

func NewBackgroundManager

func NewBackgroundManager(sensorID string, rings, azBins int, params BackgroundParams, store BgStore) *BackgroundManager

NewBackgroundManager creates a BackgroundGrid and manager, registers it under sensorID, and optionally wires a BgStore for persistence (sets PersistCallback to call Persist).

func (*BackgroundManager) ExportBackgroundGridToASC

func (bm *BackgroundManager) ExportBackgroundGridToASC() (string, error)

ExportBackgroundGridToASC exports the background grid using the shared ASC export utility. Returns the actual path where the file was written.

func (*BackgroundManager) GetAcceptanceMetrics

func (bm *BackgroundManager) GetAcceptanceMetrics() *AcceptanceMetrics

GetAcceptanceMetrics returns a snapshot of the acceptance metrics. The returned slices are copies and safe for the caller to inspect without further synchronization.

func (*BackgroundManager) GetGridCells

func (bm *BackgroundManager) GetGridCells() []ExportedCell

GetGridCells returns all non-empty cells from the grid.

func (*BackgroundManager) GetGridHeatmap

func (bm *BackgroundManager) GetGridHeatmap(azimuthBucketDeg float64, settledThreshold uint32) *GridHeatmap

GetGridHeatmap aggregates the fine-grained grid into coarse spatial buckets for visualization and analysis. Returns nil if the manager or grid is nil.

Parameters:

  • azimuthBucketDeg: size of each azimuth bucket in degrees (e.g., 3.0 for 120 buckets)
  • settledThreshold: minimum TimesSeenCount to consider a cell "settled"

func (*BackgroundManager) GetParams

func (bm *BackgroundManager) GetParams() BackgroundParams

GetParams returns a copy of the BackgroundParams for the manager's grid.

func (*BackgroundManager) GetRegionDebugInfo

func (bm *BackgroundManager) GetRegionDebugInfo(includeCells bool) *RegionDebugInfo

GetRegionDebugInfo returns comprehensive region information for debugging

func (*BackgroundManager) GridStatus

func (bm *BackgroundManager) GridStatus() map[string]interface{}

GridStatus returns a simple snapshot of grid-level statistics useful for debugging settling behavior. The returned map includes total_cells, frozen_cells, a times-seen distribution (string->count) and foreground/background counters.

func (*BackgroundManager) Persist

func (bm *BackgroundManager) Persist(store BgStore, reason string) error

Persist serializes the BackgroundGrid and writes a BgSnapshot via the provided store. It updates grid snapshot metadata on success.

func (*BackgroundManager) ProcessFramePolar

func (bm *BackgroundManager) ProcessFramePolar(points []PointPolar)

ProcessFramePolar ingests sensor-frame polar points and updates the BackgroundGrid. Behavior:

  • Bins points by ring (channel) and azimuth bin.
  • Uses an EMA (BackgroundUpdateFraction) to update AverageRangeMeters and RangeSpreadMeters.
  • Tracks a simple two-level confidence via TimesSeenCount (increment on close matches, decrement on mismatches). When a cell deviates strongly repeatedly it is frozen for FreezeDurationNanos to avoid corrupting the background model.
  • Uses neighbor confirmation: updates are applied more readily when adjacent cells agree (helps suppress isolated noise).

func (*BackgroundManager) ProcessFramePolarWithMask

func (bm *BackgroundManager) ProcessFramePolarWithMask(points []PointPolar) (foregroundMask []bool, err error)

ProcessFramePolarWithMask classifies each point as foreground/background in polar coordinates. Returns a mask where true indicates foreground (object), false indicates background (static). This is Phase 2.9 of the foreground tracking pipeline.

Unlike ProcessFramePolar which aggregates points per cell, this operates per-point for finer-grained foreground detection suitable for downstream clustering.

func (*BackgroundManager) ResetAcceptanceMetrics

func (bm *BackgroundManager) ResetAcceptanceMetrics() error

ResetAcceptanceMetrics zeros the acceptance/rejection counters for the grid. This is intended for clean A/B testing when tuning parameters.

func (*BackgroundManager) ResetGrid

func (bm *BackgroundManager) ResetGrid() error

ResetGrid zeros per-cell stats (AverageRangeMeters, RangeSpreadMeters, TimesSeenCount, LastUpdateUnixNanos, FrozenUntilUnixNanos) and acceptance counters. Intended for testing and A/B sweeps only.

func (*BackgroundManager) SetClosenessSensitivityMultiplier

func (bm *BackgroundManager) SetClosenessSensitivityMultiplier(v float32) error

SetClosenessSensitivityMultiplier safely updates the ClosenessSensitivityMultiplier parameter.

func (*BackgroundManager) SetEnableDiagnostics

func (bm *BackgroundManager) SetEnableDiagnostics(v bool)

SetEnableDiagnostics toggles emission of diagnostics for this manager.

func (*BackgroundManager) SetForegroundClusterParams

func (bm *BackgroundManager) SetForegroundClusterParams(minPts int, eps float32) error

SetForegroundClusterParams updates the minimum cluster size and eps used for foreground gating.

func (*BackgroundManager) SetNeighborConfirmationCount

func (bm *BackgroundManager) SetNeighborConfirmationCount(v int) error

SetNeighborConfirmationCount safely updates the NeighborConfirmationCount parameter.

func (*BackgroundManager) SetNoiseRelativeFraction

func (bm *BackgroundManager) SetNoiseRelativeFraction(v float32) error

SetNoiseRelativeFraction safely updates the NoiseRelativeFraction parameter.

func (*BackgroundManager) SetParams

func (bm *BackgroundManager) SetParams(p BackgroundParams) error

SetParams replaces the manager's BackgroundParams atomically.

func (*BackgroundManager) SetPostSettleUpdateFraction

func (bm *BackgroundManager) SetPostSettleUpdateFraction(v float32) error

SetPostSettleUpdateFraction updates the post-settle alpha.

func (*BackgroundManager) SetRingElevations

func (bm *BackgroundManager) SetRingElevations(elevations []float64) error

SetRingElevations sets per-ring elevation angles (degrees) on the BackgroundGrid. The provided slice must have length equal to the grid's Rings; values are copied.

func (*BackgroundManager) SetSeedFromFirstObservation

func (bm *BackgroundManager) SetSeedFromFirstObservation(v bool) error

SetSeedFromFirstObservation toggles seeding empty cells from the first observation.

func (*BackgroundManager) SetWarmupParams

func (bm *BackgroundManager) SetWarmupParams(durationNanos int64, minFrames int) error

SetWarmupParams updates settle duration/frame requirements.

func (*BackgroundManager) ToASCPoints

func (bm *BackgroundManager) ToASCPoints() []PointASC

ToASCPoints converts the background grid to a slice of PointASC for export.

type BackgroundParams

type BackgroundParams struct {
	BackgroundUpdateFraction       float32 // e.g., 0.02
	ClosenessSensitivityMultiplier float32 // e.g., 3.0
	SafetyMarginMeters             float32 // e.g., 0.5
	FreezeDurationNanos            int64   // e.g., 5e9 (5s)
	NeighborConfirmationCount      int     // e.g., 5 of 8 neighbors
	WarmupDurationNanos            int64   // optional extra settle time before emitting foreground
	WarmupMinFrames                int     // optional minimum frames before considering settled
	PostSettleUpdateFraction       float32 // optional lower alpha after settle for stability
	ForegroundMinClusterPoints     int     // min points for a cluster to be forwarded/considered
	ForegroundDBSCANEps            float32 // clustering radius for foreground gating
	// NoiseRelativeFraction is the fraction of range (distance) to treat as
	// expected measurement noise. This allows closeness thresholds to grow
	// with distance so that farther returns (which naturally have larger
	// absolute noise) aren't biased as foreground. Typical values: 0.01
	// (1%) to 0.02 (2%). If zero, a sensible default (0.01) is used.
	NoiseRelativeFraction float32

	// SeedFromFirstObservation, when true, will initialize empty background cells
	// from the first observation seen for that cell. This is useful for PCAP
	// replay mode where there is no prior live-warmup data; default: false.
	SeedFromFirstObservation bool

	// ReacquisitionBoostMultiplier controls how much faster cells re-acquire
	// background after a foreground event. When a cell that recently saw foreground
	// receives an observation matching the prior background, the effective alpha
	// is multiplied by this factor for faster convergence. Default: 5.0.
	// Set to 1.0 to disable the boost.
	ReacquisitionBoostMultiplier float32

	// MinConfidenceFloor is the minimum TimesSeenCount to preserve during foreground
	// observations. This prevents cells from completely "forgetting" their settled
	// background when a vehicle passes through. Default: 3.
	MinConfidenceFloor uint32

	// LockedBaselineThreshold is the minimum TimesSeenCount before we lock the
	// baseline. Once locked, the baseline only updates slowly. Default: 50.
	LockedBaselineThreshold uint32
	// LockedBaselineMultiplier controls how many times the LockedSpread defines
	// the acceptance window. Observations within LockedBaseline ± (LockedSpread *
	// LockedBaselineMultiplier) are considered background. Default: 4.0.
	LockedBaselineMultiplier float32

	// Debug logging region (only active if EnableDiagnostics is true)
	DebugRingMin int     // Min ring index (inclusive)
	DebugRingMax int     // Max ring index (inclusive)
	DebugAzMin   float32 // Min azimuth degrees (inclusive)
	DebugAzMax   float32 // Max azimuth degrees (inclusive)

	// Additional params for persistence matching schema requirements
	SettlingPeriodNanos        int64 // 5 minutes before first snapshot
	SnapshotIntervalNanos      int64 // 2 hours between snapshots
	ChangeThresholdForSnapshot int   // min changed cells to trigger snapshot
}

BackgroundParams configuration matching the param storage approach in schema

func (BackgroundParams) HasDebugRange

func (p BackgroundParams) HasDebugRange() bool

HasDebugRange returns true if any debug range parameters are set.

func (BackgroundParams) IsInDebugRange

func (p BackgroundParams) IsInDebugRange(ring int, az float64) bool

IsInDebugRange returns true if the given ring and azimuth fall within the configured debug range. If no range is configured, it returns false.

type BackgroundParamsExport

type BackgroundParamsExport struct {
	BackgroundUpdateFraction       float32 `json:"background_update_fraction"`
	ClosenessSensitivityMultiplier float32 `json:"closeness_sensitivity_multiplier"`
	SafetyMarginMeters             float32 `json:"safety_margin_meters"`
	NeighborConfirmationCount      int     `json:"neighbor_confirmation_count"`
	NoiseRelativeFraction          float32 `json:"noise_relative_fraction"`
	SeedFromFirstObservation       bool    `json:"seed_from_first_observation"`
	FreezeDurationNanos            int64   `json:"freeze_duration_nanos"`
}

BackgroundParamsExport is the JSON-serializable background params.

func FromBackgroundParams

func FromBackgroundParams(p BackgroundParams) BackgroundParamsExport

FromBackgroundParams creates export params from BackgroundParams.

type BgSnapshot

type BgSnapshot struct {
	SnapshotID         *int64 // will be set by database after insert
	SensorID           string // matches sensor_id TEXT NOT NULL
	TakenUnixNanos     int64  // matches taken_unix_nanos INTEGER NOT NULL
	Rings              int    // matches rings INTEGER NOT NULL
	AzimuthBins        int    // matches azimuth_bins INTEGER NOT NULL
	ParamsJSON         string // matches params_json TEXT NOT NULL
	RingElevationsJSON string // matches ring_elevations_json TEXT NULL - optional per-ring elevation JSON
	GridBlob           []byte // matches grid_blob BLOB NOT NULL (compressed BackgroundCell data)
	ChangedCellsCount  int    // matches changed_cells_count INTEGER
	SnapshotReason     string // matches snapshot_reason TEXT ('settling_complete', 'periodic_update', 'manual')
}

BgSnapshot exactly matches schema lidar_bg_snapshot table structure

type BgStore

type BgStore interface {
	InsertBgSnapshot(s *BgSnapshot) (int64, error)
}

BgStore is an interface required to persist BgSnapshot records. Implemented by lidardb.LidarDB.

type ClassificationFeatures

type ClassificationFeatures struct {
	// Spatial features
	AvgHeight float32 // Average bounding box height
	AvgLength float32 // Average bounding box length
	AvgWidth  float32 // Average bounding box width
	HeightP95 float32 // Maximum P95 height

	// Kinematic features
	AvgSpeed  float32 // Average speed
	PeakSpeed float32 // Peak speed
	P50Speed  float32 // Median speed
	P85Speed  float32 // 85th percentile speed
	P95Speed  float32 // 95th percentile speed

	// Temporal features
	ObservationCount int
	DurationSecs     float32
}

ClassificationFeatures holds the features used for classification.

type ClassificationParamsExport

type ClassificationParamsExport struct {
	ModelType  string                 `json:"model_type"`
	Thresholds map[string]interface{} `json:"thresholds,omitempty"`
}

ClassificationParamsExport is the JSON-serializable classification params.

type ClassificationResult

type ClassificationResult struct {
	Class      ObjectClass
	Confidence float32
	Model      string // Model version used
	Features   ClassificationFeatures
}

ClassificationResult holds the result of track classification.

type ClusteringParamsExport

type ClusteringParamsExport struct {
	Eps      float64 `json:"eps"`
	MinPts   int     `json:"min_pts"`
	CellSize float64 `json:"cell_size,omitempty"`
}

ClusteringParamsExport is the JSON-serializable clustering params.

func FromDBSCANParams

func FromDBSCANParams(p DBSCANParams) ClusteringParamsExport

FromDBSCANParams creates export params from DBSCANParams.

type CoarseBucket

type CoarseBucket struct {
	Ring            int     `json:"ring"`
	AzimuthDegStart float64 `json:"azimuth_deg_start"`
	AzimuthDegEnd   float64 `json:"azimuth_deg_end"`
	TotalCells      int     `json:"total_cells"`
	FilledCells     int     `json:"filled_cells"`
	SettledCells    int     `json:"settled_cells"`
	FrozenCells     int     `json:"frozen_cells"`
	MeanTimesSeen   float64 `json:"mean_times_seen"`
	MeanRangeMeters float64 `json:"mean_range_meters"`
	MinRangeMeters  float64 `json:"min_range_meters"`
	MaxRangeMeters  float64 `json:"max_range_meters"`
}

CoarseBucket represents aggregated metrics for a spatial bucket

type DBSCANParams

type DBSCANParams struct {
	Eps    float64 // Neighborhood radius in meters
	MinPts int     // Minimum points to form a cluster
}

DBSCANParams contains parameters for the DBSCAN clustering algorithm.

func DefaultDBSCANParams

func DefaultDBSCANParams() DBSCANParams

DefaultDBSCANParams returns default DBSCAN parameters suitable for vehicle detection.

type Event

type Event struct {
	When    time.Time              // event timestamp
	Level   string                 // "info", "warn", "error", "debug"
	Message string                 // human-readable message
	Context map[string]interface{} // structured context data

	// Schema integration fields
	SensorID  *string // sensor that generated the event (if applicable)
	EventType string  // maps to system_events.event_type for persistence
}

Event represents a system event with structured context for debugging and monitoring

func NewPerformanceEvent

func NewPerformanceEvent(sensorID *string, metricName string, metricValue float64) *Event

Helper constructors for common event types

func NewTrackInitiateEvent

func NewTrackInitiateEvent(trackID string, sensorID string, initialPos [2]float32) *Event

func NewTrackTerminateEvent

func NewTrackTerminateEvent(trackID string, sensorID string, finalStats map[string]interface{}) *Event

type ExportedCell

type ExportedCell struct {
	Ring        int
	AzimuthDeg  float32
	Range       float32
	Spread      float32
	TimesSeen   uint32
	LastUpdate  int64
	FrozenUntil int64
}

ExportedCell represents a background cell for API consumption

type ForegroundForwarder

type ForegroundForwarder interface {
	ForwardForeground(points []PointPolar)
}

ForegroundForwarder interface allows forwarding foreground points without importing network package.

type ForegroundFrame

type ForegroundFrame struct {
	SensorID         string       // Sensor that captured this frame
	TSUnixNanos      int64        // Timestamp of the frame
	SequenceID       string       // Optional sequence grouping (e.g., "seq_20251130_001")
	ForegroundPoints []PointPolar // Foreground points in polar (sensor) frame

	// Frame statistics
	TotalPoints      int
	BackgroundPoints int
}

ForegroundFrame represents a single frame of foreground points for ML training. Points are stored in sensor frame (polar coordinates) for pose independence.

func ExportForegroundFrame

func ExportForegroundFrame(polarPoints []PointPolar, mask []bool, sensorID string, ts time.Time) *ForegroundFrame

ExportForegroundFrame creates a ForegroundFrame from classified points. Points are stored in polar (sensor) coordinates for pose independence.

func (*ForegroundFrame) ForegroundCount

func (f *ForegroundFrame) ForegroundCount() int

ForegroundCount returns the number of foreground points.

func (*ForegroundFrame) ForegroundFraction

func (f *ForegroundFrame) ForegroundFraction() float64

ForegroundFraction returns the ratio of foreground to total points.

func (*ForegroundFrame) SetSequenceID

func (f *ForegroundFrame) SetSequenceID(sequenceID string)

SetSequenceID assigns this frame to a sequence for grouping.

type ForegroundSnapshot

type ForegroundSnapshot struct {
	SensorID         string
	Timestamp        time.Time
	ForegroundPoints []ProjectedPoint
	BackgroundPoints []ProjectedPoint
	TotalPoints      int
	ForegroundCount  int
	BackgroundCount  int
}

ForegroundSnapshot stores the latest foreground/background projections for a sensor. Points are kept in sensor frame (X=right, Y=forward) to match the background polar chart.

func GetForegroundSnapshot

func GetForegroundSnapshot(sensorID string) *ForegroundSnapshot

GetForegroundSnapshot returns a copy of the latest foreground snapshot for a sensor.

type FrameBuilder

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

FrameBuilder accumulates points from multiple packets into complete rotational frames Uses azimuth-based rotation detection and UDP sequence tracking for completeness

func GetFrameBuilder

func GetFrameBuilder(sensorID string) *FrameBuilder

GetFrameBuilder returns a registered FrameBuilder or nil

func NewFrameBuilder

func NewFrameBuilder(config FrameBuilderConfig) *FrameBuilder

NewFrameBuilder creates a new FrameBuilder with the specified configuration

func NewFrameBuilderWithDebugLogging

func NewFrameBuilderWithDebugLogging(sensorID string, debug bool) *FrameBuilder

NewFrameBuilderWithDebugLogging creates a FrameBuilder with optional debug logging

func NewFrameBuilderWithDebugLoggingAndInterval

func NewFrameBuilderWithDebugLoggingAndInterval(sensorID string, debug bool, logInterval time.Duration) *FrameBuilder

NewFrameBuilderWithDebugLoggingAndInterval creates a FrameBuilder with optional debug logging and export interval

func NewFrameBuilderWithLogging

func NewFrameBuilderWithLogging(sensorID string) *FrameBuilder

NewFrameBuilderWithLogging creates a FrameBuilder that logs completed frames This is a convenience function for common use cases where you want to log frame completion

func (*FrameBuilder) AddPointsPolar

func (fb *FrameBuilder) AddPointsPolar(polar []PointPolar)

AddPointsPolar accepts polar points (sensor-frame) and converts them to cartesian Points before processing. This is used by network listeners that parse into polar form.

func (*FrameBuilder) EnableTimeBased

func (fb *FrameBuilder) EnableTimeBased(enable bool)

EnableTimeBased enables or disables time-based frame detection

func (*FrameBuilder) GetCurrentFrameStats

func (fb *FrameBuilder) GetCurrentFrameStats() (frameCount int, oldestAge time.Duration, newestAge time.Duration)

GetCurrentFrameStats returns statistics about the frames currently being built

func (*FrameBuilder) RequestExportFrameBatchASC

func (fb *FrameBuilder) RequestExportFrameBatchASC(count int)

RequestExportFrameBatchASC schedules export of the next N completed frames. Export paths are generated internally for security.

func (*FrameBuilder) RequestExportNextFrameASC

func (fb *FrameBuilder) RequestExportNextFrameASC()

RequestExportNextFrameASC schedules export of the next completed frame to ASC format. The export path is generated internally for security.

func (*FrameBuilder) Reset

func (fb *FrameBuilder) Reset()

Reset clears all buffered frame state. This should be called when switching data sources (e.g., live to PCAP) to prevent stale frames from contaminating the new data stream.

func (*FrameBuilder) SetDebug

func (fb *FrameBuilder) SetDebug(enabled bool)

SetDebug enables or disables lightweight debug logging for frame completion

func (*FrameBuilder) SetMotorSpeed

func (fb *FrameBuilder) SetMotorSpeed(rpm uint16)

SetMotorSpeed updates the expected frame duration based on motor speed (RPM) This enables time-based frame detection for accurate motor speed handling

type FrameBuilderConfig

type FrameBuilderConfig struct {
	SensorID              string            // sensor identifier
	FrameCallback         func(*LiDARFrame) // callback when frame is complete
	AzimuthTolerance      float64           // tolerance for azimuth wrap detection (default: 10°)
	MinFramePoints        int               // minimum points required for valid frame (default: 1000)
	MaxBackfillDelay      time.Duration     // max time to wait for backfill packets (default: 100ms)
	FrameBufferSize       int               // max frames to buffer (default: 10)
	BufferTimeout         time.Duration     // how long to wait before finalizing frame (default: 1s)
	CleanupInterval       time.Duration     // how often to check for frames to finalize (default: 250ms)
	ExpectedFrameDuration time.Duration     // expected duration per frame based on motor speed (default: 0 = azimuth-only)
	EnableTimeBased       bool              // true to use time-based detection with azimuth validation
}

FrameBuilderConfig contains configuration for the FrameBuilder

type FrameID

type FrameID string

FrameID is a human-readable name like "sensor/hesai-01" or "site/main-st-001".

type FrameMetrics

type FrameMetrics struct {
	TotalPoints        int     `json:"total_points"`
	ForegroundPoints   int     `json:"foreground_points"`
	BackgroundPoints   int     `json:"background_points"`
	ForegroundFraction float64 `json:"foreground_fraction"`
	ProcessingTimeUs   int64   `json:"processing_time_us"`
}

FrameMetrics contains per-frame statistics about foreground extraction.

func ComputeFrameMetrics

func ComputeFrameMetrics(mask []bool, processingTimeUs int64) FrameMetrics

ComputeFrameMetrics computes metrics from a foreground mask.

type FrameStats

type FrameStats struct {
	TSUnixNanos      int64
	PacketsReceived  int
	PointsTotal      int
	ForegroundPoints int
	ClustersFound    int
	TracksActive     int
	ProcessingTimeUs int64

	// Additional metrics for 100-track monitoring
	MemoryUsageMB   int64
	CPUUsagePercent float32
	DroppedPackets  int64
}

Performance tracking for system_events table integration

type GridHeatmap

type GridHeatmap struct {
	SensorID      string                 `json:"sensor_id"`
	Timestamp     time.Time              `json:"timestamp"`
	GridParams    map[string]interface{} `json:"grid_params"`
	HeatmapParams map[string]interface{} `json:"heatmap_params"`
	Summary       map[string]interface{} `json:"summary"`
	Buckets       []CoarseBucket         `json:"buckets"`
}

GridHeatmap represents the full aggregated grid state

type LiDARFrame

type LiDARFrame struct {
	FrameID        string    // unique identifier for this frame
	SensorID       string    // which sensor generated this frame
	StartTimestamp time.Time // timestamp of first point in frame
	EndTimestamp   time.Time // timestamp of last point in frame
	StartWallTime  time.Time // wall-clock time when frame started (ingest time)
	EndWallTime    time.Time // wall-clock time when last point was ingested
	Points         []Point   // all points in this complete rotation
	MinAzimuth     float64   // minimum azimuth angle observed
	MaxAzimuth     float64   // maximum azimuth angle observed
	PointCount     int       // total number of points in frame
	SpinComplete   bool      // true when full 360° rotation detected

	// Completeness tracking
	ExpectedPackets   map[uint32]bool // expected UDP sequence numbers
	ReceivedPackets   map[uint32]bool // received UDP sequence numbers
	MissingPackets    []uint32        // sequence numbers of missing packets
	PacketGaps        int             // count of missing packets
	CompletenessRatio float64         // ratio of received/expected packets
	AzimuthCoverage   float64         // degrees of azimuth covered (0-360)
}

LiDARFrame represents one complete 360° rotation of LiDAR data

type NoiseCoverageMetrics

type NoiseCoverageMetrics struct {
	TotalTracks         int                `json:"total_tracks"`
	TracksWithHighNoise int                `json:"tracks_with_high_noise"`       // noise_ratio > 0.3
	TracksUnknownClass  int                `json:"tracks_unknown_class"`         // object_class == "other"
	TracksLowConfidence int                `json:"tracks_low_confidence"`        // object_confidence < 0.6
	UnknownRatioBySpeed map[string]float32 `json:"unknown_ratio_by_speed"`       // "slow"/"medium"/"fast"
	UnknownRatioBySize  map[string]float32 `json:"unknown_ratio_by_size"`        // "small"/"medium"/"large"
	NoiseRatioHistogram []int              `json:"noise_ratio_histogram_counts"` // Counts for bins [0-0.1, 0.1-0.2, ...]
}

NoiseCoverageMetrics quantifies "unknown" classification coverage. Phase 3: Scaffolding for coverage analysis.

func ComputeNoiseCoverageMetrics

func ComputeNoiseCoverageMetrics(tracks []*TrackedObject) *NoiseCoverageMetrics

ComputeNoiseCoverageMetrics calculates coverage metrics for a set of tracks. Phase 3: Placeholder implementation.

type ObjectClass

type ObjectClass string

ObjectClass represents the classification of a tracked object.

const (
	// ClassPedestrian indicates a pedestrian or person
	ClassPedestrian ObjectClass = "pedestrian"
	// ClassCar indicates a car or vehicle
	ClassCar ObjectClass = "car"
	// ClassBird indicates a bird or small flying object
	ClassBird ObjectClass = "bird"
	// ClassOther indicates an unclassified object
	ClassOther ObjectClass = "other"
)

type Point

type Point struct {
	// 3D Cartesian coordinates (computed from spherical measurements)
	X float64 `json:"x"` // X coordinate in meters (forward direction from sensor)
	Y float64 `json:"y"` // Y coordinate in meters (right direction from sensor)
	Z float64 `json:"z"` // Z coordinate in meters (upward direction from sensor)

	// Measurement metadata
	Intensity uint8     `json:"intensity"` // Laser return intensity/reflectivity (0-255)
	Distance  float64   `json:"distance"`  // Radial distance from sensor in meters
	Azimuth   float64   `json:"azimuth"`   // Horizontal angle in degrees (0-360, corrected)
	Elevation float64   `json:"elevation"` // Vertical angle in degrees (corrected for channel)
	Channel   int       `json:"channel"`   // Laser channel number (1-40)
	Timestamp time.Time `json:"timestamp"` // Point acquisition time (with firetime correction)
	BlockID   int       `json:"block_id"`  // Data block index within packet (0-9)

	// Packet tracking for completeness validation
	UDPSequence     uint32 `json:"udp_sequence"`      // UDP sequence number for gap detection
	RawBlockAzimuth uint16 `json:"raw_block_azimuth"` // Original block azimuth from packet (0.01 deg units)
}

Point represents a single 3D LiDAR measurement point in Cartesian coordinates Each point contains both the processed 3D coordinates and raw measurement data

type PointASC

type PointASC struct {
	X, Y, Z   float64
	Intensity int
	Extra     []interface{}
}

PointASC is a cartesian point with optional extra columns for export (X, Y, Z, Intensity, ...extra)

type PointPolar

type PointPolar struct {
	Channel         int
	Azimuth         float64
	Elevation       float64
	Distance        float64
	Intensity       uint8
	Timestamp       int64 // unix nanos if needed; keep small to avoid heavy time usage
	BlockID         int
	UDPSequence     uint32
	RawBlockAzimuth uint16 // Original block azimuth from packet (0.01 deg units)
}

PointPolar is a compact representation of a LiDAR return in polar terms. It can be used where sensor-frame operations are preferred (background model).

func DecodeForegroundBlob

func DecodeForegroundBlob(blob []byte) []PointPolar

DecodeForegroundBlob decodes a compact binary blob back to polar points.

func ExtractForegroundPoints

func ExtractForegroundPoints(points []PointPolar, mask []bool) []PointPolar

ExtractForegroundPoints returns only the foreground points from the input slice based on the provided mask. Points where mask[i] == true are included.

type PolarPointCompact

type PolarPointCompact struct {
	DistanceCm        uint16 // Distance in centimeters (0-655.35m range)
	AzimuthCentideg   uint16 // Azimuth in centidegrees (0-36000 = 0-360°)
	ElevationCentideg int16  // Elevation in centidegrees (-18000 to +18000 = -180° to +180°)
	Intensity         uint8  // Laser return intensity
	Ring              uint8  // Ring/channel number
}

PolarPointCompact is a compact binary representation of a polar point. Total: 8 bytes per point (vs ~40+ bytes for PointPolar struct)

type Pose

type Pose struct {
	PoseID                    int64       // matches pose_id INTEGER PRIMARY KEY
	SensorID                  string      // matches sensor_id TEXT NOT NULL
	FromFrame                 FrameID     // matches from_frame TEXT NOT NULL
	ToFrame                   FrameID     // matches to_frame TEXT NOT NULL
	T                         [16]float64 // matches T_rowmajor_4x4 BLOB (16 floats)
	ValidFromNanos            int64       // matches valid_from_ns INTEGER NOT NULL
	ValidToNanos              *int64      // matches valid_to_ns INTEGER (NULL = current)
	Method                    string      // matches method TEXT
	RootMeanSquareErrorMeters float32     // matches root_mean_square_error_meters REAL
}

Pose is a rigid transform (sensor -> world) with versioning. T is 4x4 row-major (m00..m03, m10..m13, m20..m23, m30..m33).

type PoseCache

type PoseCache struct {
	BySensorID map[string]*Pose
	WorldFrame FrameID // canonical site frame (e.g., "site/main-st-001")

}

PoseCache holds the current pose used for realtime transforms.

type ProjectedPoint

type ProjectedPoint struct {
	X         float64
	Y         float64
	Z         float64
	Intensity uint8
}

ProjectedPoint is a lightweight 3D sensor-frame projection used for debug charts.

type Region

type Region struct {
	ID       int          // unique region identifier
	CellMask []bool       // len = Rings * AzimuthBins; true if cell belongs to this region
	Params   RegionParams // parameters for this region
	CellList []int        // list of cell indices in this region (for efficient iteration)
	// Statistics for region characterization
	MeanVariance float64 // mean variance of cells in this region during settling
	CellCount    int     // number of cells in this region
}

Region represents a contiguous spatial region with distinct parameters

type RegionDebugInfo

type RegionDebugInfo struct {
	SensorID               string       `json:"sensor_id"`
	Timestamp              time.Time    `json:"timestamp"`
	IdentificationComplete bool         `json:"identification_complete"`
	IdentificationTime     time.Time    `json:"identification_time,omitempty"`
	FramesSampled          int          `json:"frames_sampled"`
	RegionCount            int          `json:"region_count"`
	Regions                []RegionInfo `json:"regions"`
	// Grid mapping: for each cell, which region it belongs to
	GridMapping []int `json:"grid_mapping"` // maps cell index to region ID
}

RegionDebugInfo contains full region visualization data

type RegionInfo

type RegionInfo struct {
	ID           int          `json:"id"`
	CellCount    int          `json:"cell_count"`
	MeanVariance float64      `json:"mean_variance"`
	Params       RegionParams `json:"params"`
	Cells        []struct {
		Ring       int     `json:"ring"`
		AzimuthDeg float32 `json:"azimuth_deg"`
	} `json:"cells,omitempty"` // Optional: can be large, include only on request
}

RegionInfo represents a region for API export

type RegionManager

type RegionManager struct {
	Regions         []*Region // list of identified regions
	CellToRegionID  []int     // maps cell index to region ID (-1 if unassigned)
	SettlingMetrics struct {
		VariancePerCell []float64 // variance observed per cell during settling
		FramesSampled   int       // frames sampled for variance calculation
	}
	IdentificationComplete bool      // true once regions are identified
	IdentificationTime     time.Time // when regions were identified
}

RegionManager handles dynamic region identification and management

func NewRegionManager

func NewRegionManager(rings, azBins int) *RegionManager

NewRegionManager creates a RegionManager for the grid

func (*RegionManager) GetRegionForCell

func (rm *RegionManager) GetRegionForCell(cellIdx int) int

GetRegionForCell returns the region ID for a given cell index

func (*RegionManager) GetRegionParams

func (rm *RegionManager) GetRegionParams(regionID int) *RegionParams

GetRegionParams returns the parameters for a given region ID

func (*RegionManager) IdentifyRegions

func (rm *RegionManager) IdentifyRegions(grid *BackgroundGrid, maxRegions int) error

IdentifyRegions performs clustering based on variance characteristics and creates contiguous regions with distinct parameters

func (*RegionManager) UpdateVarianceMetrics

func (rm *RegionManager) UpdateVarianceMetrics(cells []BackgroundCell)

UpdateVarianceMetrics accumulates variance data during settling Computes running mean of RangeSpreadMeters values per cell

type RegionParams

type RegionParams struct {
	NoiseRelativeFraction     float32 `json:"noise_relative_fraction"`     // noise threshold for this region
	NeighborConfirmationCount int     `json:"neighbor_confirmation_count"` // neighbor confirmation for this region
	SettleUpdateFraction      float32 `json:"settle_update_fraction"`      // alpha during settling for this region
}

RegionParams defines parameters that can vary per region

type RetentionConfig

type RetentionConfig struct {
	MaxConcurrentTracks          int           // 100 - matches design target
	MaxTrackObservationsPerTrack int           // 1000 observations per track - ring buffer size
	MaxRecentClusters            int           // 10,000 recent clusters - memory management
	MaxTrackAge                  time.Duration // 30 minutes for inactive tracks
	BgSnapshotInterval           time.Duration // 2 hours - matches schema automatic persistence
	BgSnapshotRetention          time.Duration // 48 hours - cleanup old snapshots
	BgSettlingPeriod             time.Duration // 5 minutes before first persist

	// Enhanced cleanup policies for schema maintenance
	MaxTrackFeatureAge   time.Duration // 7 days - cleanup old feature vectors
	MaxSystemEventAge    time.Duration // 30 days - cleanup old performance metrics
	ClusterPruneInterval time.Duration // 1 hour - memory cleanup frequency
}

Retention policies optimized for 100 concurrent tracks and schema constraints

type RingBuffer

type RingBuffer[T any] struct {
	Items    []T
	Head     int
	Tail     int
	Size     int
	Capacity int
	// contains filtered or unexported fields
}

Ring buffer implementation for efficient memory management at 100-track scale

func (*RingBuffer[T]) Len

func (rb *RingBuffer[T]) Len() int

func (*RingBuffer[T]) Pop

func (rb *RingBuffer[T]) Pop() (T, bool)

func (*RingBuffer[T]) Push

func (rb *RingBuffer[T]) Push(item T) bool

Ring buffer methods for safe concurrent access

type RunComparison

type RunComparison struct {
	Run1ID          string         `json:"run1_id"`
	Run2ID          string         `json:"run2_id"`
	ParamDiff       map[string]any `json:"param_diff,omitempty"`
	TracksOnlyRun1  []string       `json:"tracks_only_run1,omitempty"`
	TracksOnlyRun2  []string       `json:"tracks_only_run2,omitempty"`
	SplitCandidates []TrackSplit   `json:"split_candidates,omitempty"`
	MergeCandidates []TrackMerge   `json:"merge_candidates,omitempty"`
	MatchedTracks   []TrackMatch   `json:"matched_tracks,omitempty"`
}

RunComparison shows differences between two analysis runs.

type RunParams

type RunParams struct {
	Version        string                     `json:"version"`
	Timestamp      time.Time                  `json:"timestamp"`
	Background     BackgroundParamsExport     `json:"background"`
	Clustering     ClusteringParamsExport     `json:"clustering"`
	Tracking       TrackingParamsExport       `json:"tracking"`
	Classification ClassificationParamsExport `json:"classification,omitempty"`
}

RunParams captures all configurable parameters for reproducibility. This is the structure serialized into AnalysisRun.ParamsJSON.

func DefaultRunParams

func DefaultRunParams() RunParams

DefaultRunParams returns default run parameters.

func ParseRunParams

func ParseRunParams(data json.RawMessage) (*RunParams, error)

ParseRunParams deserializes RunParams from JSON.

func (*RunParams) ToJSON

func (p *RunParams) ToJSON() (json.RawMessage, error)

ToJSON serializes RunParams to JSON.

type RunStatistics

type RunStatistics struct {
	// Track Quality Distribution
	AvgTrackLength    float32 `json:"avg_track_length_meters"`
	MedianTrackLength float32 `json:"median_track_length_meters"`
	AvgTrackDuration  float32 `json:"avg_track_duration_secs"`
	AvgOcclusionCount float32 `json:"avg_occlusion_count"`

	// Classification Distribution
	ClassCounts        map[string]int     `json:"class_counts"`
	ClassConfidenceAvg map[string]float32 `json:"class_confidence_avg"`
	UnknownRatio       float32            `json:"unknown_ratio"`

	// Noise & Coverage
	AvgNoiseRatio      float32 `json:"avg_noise_ratio"`
	AvgSpatialCoverage float32 `json:"avg_spatial_coverage"`

	// Track Lifecycle
	TentativeRatio          float32 `json:"tentative_ratio"`
	ConfirmedRatio          float32 `json:"confirmed_ratio"`
	AvgObservationsPerTrack int     `json:"avg_observations_per_track"`
}

RunStatistics holds aggregate statistics for an analysis run.

func ComputeRunStatistics

func ComputeRunStatistics(tracks []*TrackedObject) *RunStatistics

ComputeRunStatistics calculates aggregate statistics from a set of tracks.

func ParseRunStatistics

func ParseRunStatistics(jsonStr string) (*RunStatistics, error)

ParseRunStatistics deserializes RunStatistics from JSON.

func (*RunStatistics) ToJSON

func (rs *RunStatistics) ToJSON() (string, error)

ToJSON serializes RunStatistics to JSON for database storage.

type RunTrack

type RunTrack struct {
	RunID   string `json:"run_id"`
	TrackID string `json:"track_id"`

	// Track fields (from TrackedObject)
	SensorID             string  `json:"sensor_id"`
	TrackState           string  `json:"track_state"`
	StartUnixNanos       int64   `json:"start_unix_nanos"`
	EndUnixNanos         int64   `json:"end_unix_nanos,omitempty"`
	ObservationCount     int     `json:"observation_count"`
	AvgSpeedMps          float32 `json:"avg_speed_mps"`
	PeakSpeedMps         float32 `json:"peak_speed_mps"`
	P50SpeedMps          float32 `json:"p50_speed_mps,omitempty"`
	P85SpeedMps          float32 `json:"p85_speed_mps,omitempty"`
	P95SpeedMps          float32 `json:"p95_speed_mps,omitempty"`
	BoundingBoxLengthAvg float32 `json:"bounding_box_length_avg"`
	BoundingBoxWidthAvg  float32 `json:"bounding_box_width_avg"`
	BoundingBoxHeightAvg float32 `json:"bounding_box_height_avg"`
	HeightP95Max         float32 `json:"height_p95_max"`
	IntensityMeanAvg     float32 `json:"intensity_mean_avg"`
	ObjectClass          string  `json:"object_class,omitempty"`
	ObjectConfidence     float32 `json:"object_confidence,omitempty"`
	ClassificationModel  string  `json:"classification_model,omitempty"`

	// User labels (for ML training)
	UserLabel       string  `json:"user_label,omitempty"`
	LabelConfidence float32 `json:"label_confidence,omitempty"`
	LabelerID       string  `json:"labeler_id,omitempty"`
	LabeledAt       int64   `json:"labeled_at,omitempty"`

	// Track quality flags
	IsSplitCandidate bool     `json:"is_split_candidate,omitempty"`
	IsMergeCandidate bool     `json:"is_merge_candidate,omitempty"`
	LinkedTrackIDs   []string `json:"linked_track_ids,omitempty"`
}

RunTrack represents a track within a specific analysis run. This extends TrackedObject with run-specific fields like user labels.

func RunTrackFromTrackedObject

func RunTrackFromTrackedObject(runID string, t *TrackedObject) *RunTrack

RunTrackFromTrackedObject creates a RunTrack from a TrackedObject.

type SensorConfig

type SensorConfig struct {
	ModelName     string  // e.g., "Pandar40P"
	Channels      int     // Number of laser channels (40 for Pandar40P)
	MotorSpeedRPM float64 // Motor speed for packet metadata
	UDPPort       int     // Target UDP port (default 2368)
}

SensorConfig holds sensor-specific configuration for packet generation.

func DefaultPandar40PConfig

func DefaultPandar40PConfig() *SensorConfig

DefaultPandar40PConfig returns default configuration for Pandar40P sensor.

type SidecarState

type SidecarState struct {
	Poses              *PoseCache                    // thread-safe pose management
	BackgroundManagers map[string]*BackgroundManager // enhanced with persistence
	Tracks             map[string]*Track             // up to 100 concurrent

	// Ring buffers sized for 100 tracks with thread safety
	RecentClusters   *RingBuffer[*WorldCluster]        // 10,000 capacity
	RecentTrackObs   map[string]*RingBuffer[*TrackObs] // 1000 per track
	RecentFrameStats *RingBuffer[*FrameStats]          // 1000 capacity

	// Performance monitoring for system_events integration
	TrackCount     int64
	DroppedPackets int64
	ActiveTracks   int64 // current number of active tracks
	TotalClusters  int64 // lifetime cluster count
	TotalFrames    int64 // lifetime frame count

	// Configuration
	Config *RetentionConfig

	// Schema integration hooks
	SystemEventCallback func(event *SystemEvent) error    // callback to persist system events
	ClusterCallback     func(cluster *WorldCluster) error // callback to persist clusters
	TrackObsCallback    func(obs *TrackObs) error         // callback to persist track observations
	// contains filtered or unexported fields
}

SidecarState is the main state container optimized for 100 concurrent tracks

func (*SidecarState) AddTrack

func (s *SidecarState) AddTrack(track *Track)

func (*SidecarState) GetActiveTrackCount

func (s *SidecarState) GetActiveTrackCount() int64

Thread-safe methods for SidecarState

func (*SidecarState) GetTrack

func (s *SidecarState) GetTrack(trackID string) (*Track, bool)

func (*SidecarState) RemoveTrack

func (s *SidecarState) RemoveTrack(trackID string)

type SpatialIndex

type SpatialIndex struct {
	CellSize float64
	Grid     map[int64][]int // Cell ID → point indices
}

SpatialIndex provides efficient nearest neighbor queries using a regular grid. Cell size should approximately match the DBSCAN eps parameter.

func NewSpatialIndex

func NewSpatialIndex(cellSize float64) *SpatialIndex

NewSpatialIndex creates a spatial index with the specified cell size.

func (*SpatialIndex) Build

func (si *SpatialIndex) Build(points []WorldPoint)

Build populates the spatial index from a set of world points. Uses 2D (x, y) coordinates for cell assignment.

func (*SpatialIndex) RegionQuery

func (si *SpatialIndex) RegionQuery(points []WorldPoint, idx int, eps float64) []int

RegionQuery returns indices of all points within eps distance of points[idx]. Uses 2D (x, y) Euclidean distance for neighborhood queries.

type SystemEvent

type SystemEvent struct {
	EventID     *int64                 // auto-generated by database
	SensorID    *string                // NULL for system-wide events
	TSUnixNanos int64                  // event timestamp
	EventType   string                 // 'performance', 'track_initiate', etc.
	EventData   map[string]interface{} // JSON data specific to event type
}

SystemEvent represents entries for the schema system_events table

type Track

type Track struct {
	// Core identification matching schema exactly
	TrackID    string  // matches track_id TEXT PRIMARY KEY
	SensorID   string  // matches sensor_id TEXT NOT NULL
	WorldFrame FrameID // matches world_frame TEXT NOT NULL
	PoseID     int64   // matches pose_id INTEGER NOT NULL

	// Lifecycle timestamps matching schema
	FirstUnixNanos int64 // matches start_unix_nanos INTEGER NOT NULL
	LastUnixNanos  int64 // matches end_unix_nanos INTEGER (NULL if active)

	// Current state for real-time tracking
	State TrackState2D

	// Running averages matching schema summary fields
	BoundingBoxLengthAvg, BoundingBoxWidthAvg, BoundingBoxHeightAvg float32 // matches bounding_box_length_avg, bounding_box_width_avg, bounding_box_height_avg REAL

	// Rollups for features/training matching schema fields
	ObservationCount int     // matches observation_count INTEGER
	AvgSpeedMps      float32 // matches avg_speed_mps REAL
	PeakSpeedMps     float32 // matches peak_speed_mps REAL
	HeightP95Max     float32 // matches height_p95_max REAL
	IntensityMeanAvg float32 // matches intensity_mean_avg REAL

	// Classification matching schema
	ClassLabel      string  // matches class_label TEXT
	ClassConfidence float32 // matches class_conf REAL

	// Source tracking matching schema (LiDAR-only implementation)
	SourceMask uint8 // matches source_mask INTEGER (bit0=lidar only for now)

	// Life-cycle management (in-memory only)
	Misses int // consecutive misses for deletion
}

Track enhanced to match schema lidar_tracks table structure

type TrackClassifier

type TrackClassifier struct {
	ModelVersion string
}

TrackClassifier performs rule-based classification of tracked objects. This can be replaced with an ML model in future iterations.

func NewTrackClassifier

func NewTrackClassifier() *TrackClassifier

NewTrackClassifier creates a new track classifier.

func (*TrackClassifier) Classify

func (tc *TrackClassifier) Classify(track *TrackedObject) ClassificationResult

Classify determines the object class for a tracked object. Returns the classification result with class, confidence, and features used.

func (*TrackClassifier) ClassifyAndUpdate

func (tc *TrackClassifier) ClassifyAndUpdate(track *TrackedObject)

ClassifyAndUpdate classifies a track and updates its classification fields. This should be called periodically or when track state changes.

type TrackMatch

type TrackMatch struct {
	Track1ID   string  `json:"track1_id"`
	Track2ID   string  `json:"track2_id"`
	OverlapPct float32 `json:"overlap_pct"`
}

TrackMatch represents a matched track between two runs.

type TrackMerge

type TrackMerge struct {
	MergedTrack  string   `json:"merged_track"`
	SourceTracks []string `json:"source_tracks"`
	MergeX       float32  `json:"merge_x"`
	MergeY       float32  `json:"merge_y"`
	Confidence   float32  `json:"confidence"`
}

TrackMerge represents a suspected track merge between runs.

type TrackObs

type TrackObs struct {
	TrackID    string  // matches track_id TEXT NOT NULL
	UnixNanos  int64   // matches ts_unix_nanos INTEGER NOT NULL
	WorldFrame FrameID // matches world_frame TEXT NOT NULL
	PoseID     int64   // matches pose_id INTEGER NOT NULL

	// Position matching schema
	X, Y, Z float32 // matches x, y, z REAL

	// Velocity matching schema
	VelocityX, VelocityY, VelocityZ float32 // matches velocity_x, velocity_y, velocity_z REAL

	// Derived kinematics matching schema
	SpeedMps   float32 // matches speed_mps REAL
	HeadingRad float32 // matches heading_rad REAL

	// Shape matching schema
	BoundingBoxLength, BoundingBoxWidth, BoundingBoxHeight float32 // matches bounding_box_length, bounding_box_width, bounding_box_height REAL

	// Quality metrics matching schema
	HeightP95     float32 // matches height_p95 REAL
	IntensityMean float32 // matches intensity_mean REAL
}

TrackObs exactly matches schema lidar_track_obs table structure

type TrackObservation

type TrackObservation struct {
	TrackID     string
	TSUnixNanos int64
	WorldFrame  string

	// Position (world frame)
	X, Y, Z float32

	// Velocity (world frame)
	VelocityX, VelocityY float32
	SpeedMps             float32
	HeadingRad           float32

	// Shape
	BoundingBoxLength float32
	BoundingBoxWidth  float32
	BoundingBoxHeight float32
	HeightP95         float32
	IntensityMean     float32
}

TrackObservation represents a single observation of a track at a point in time.

func GetTrackObservations

func GetTrackObservations(db *sql.DB, trackID string, limit int) ([]*TrackObservation, error)

GetTrackObservations retrieves observations for a track.

func GetTrackObservationsInRange

func GetTrackObservationsInRange(db *sql.DB, sensorID string, startNanos, endNanos int64, limit int, trackID string) ([]*TrackObservation, error)

GetTrackObservationsInRange returns observations for a sensor within a time window (inclusive). Joins against tracks to scope by sensor.

type TrackPoint

type TrackPoint struct {
	X         float32
	Y         float32
	Timestamp int64 // Unix nanos
}

TrackPoint represents a single point in a track's history.

type TrackPointCloudExporter

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

TrackPointCloudExporter handles exporting point clouds for individual tracks. Maintains polar frame representation with minimal transformations.

type TrackPointCloudFrame

type TrackPointCloudFrame struct {
	TrackID     string       // Track identifier
	FrameIndex  int          // Frame sequence number within track
	Timestamp   time.Time    // Frame timestamp
	PolarPoints []PointPolar // Points in polar coordinates
}

TrackPointCloudFrame represents a single frame of point cloud data for a track. Points are stored in polar coordinates (sensor frame) for compatibility with parsers.

func ExportTrackPointCloud

func ExportTrackPointCloud(track *TrackedObject, observationHistory []*TrackObservation) ([]*TrackPointCloudFrame, error)

ExportTrackPointCloud extracts point clouds for a specific track from observation history. Returns frames that can be encoded into PCAP format for LidarView inspection.

type TrackPointCloudMetadata

type TrackPointCloudMetadata struct {
	TrackID          string    `json:"track_id"`
	SensorID         string    `json:"sensor_id"`
	StartTime        time.Time `json:"start_time"`
	EndTime          time.Time `json:"end_time"`
	TotalFrames      int       `json:"total_frames"`
	TotalPoints      int       `json:"total_points"`
	ObjectClass      string    `json:"object_class,omitempty"`
	ObjectConfidence float32   `json:"object_confidence,omitempty"`
	// Phase 1 quality metrics
	TrackLength    float32 `json:"track_length_meters"`
	Duration       float32 `json:"duration_secs"`
	OcclusionCount int     `json:"occlusion_count"`
	QualityScore   float32 `json:"quality_score,omitempty"`
}

TrackPointCloudMetadata contains metadata for exported track point clouds.

func ExtractMetadata

func ExtractMetadata(track *TrackedObject, frames []*TrackPointCloudFrame) *TrackPointCloudMetadata

ExtractMetadata generates metadata for an exported track point cloud.

type TrackQualityMetrics

type TrackQualityMetrics struct {
	TrackID            string  `json:"track_id"`
	TrackLengthMeters  float32 `json:"track_length_meters"`
	TrackDurationSecs  float32 `json:"track_duration_secs"`
	OcclusionCount     int     `json:"occlusion_count"`
	MaxOcclusionFrames int     `json:"max_occlusion_frames"`
	SpatialCoverage    float32 `json:"spatial_coverage"`
	NoisePointRatio    float32 `json:"noise_point_ratio"`
	QualityScore       float32 `json:"quality_score"` // Composite quality metric (0-1)
}

TrackQualityMetrics provides per-track quality assessment.

func ComputeTrackQualityMetrics

func ComputeTrackQualityMetrics(track *TrackedObject) *TrackQualityMetrics

ComputeTrackQualityMetrics extracts quality metrics from a TrackedObject.

type TrackSplit

type TrackSplit struct {
	OriginalTrack string   `json:"original_track"`
	SplitTracks   []string `json:"split_tracks"`
	SplitX        float32  `json:"split_x"`
	SplitY        float32  `json:"split_y"`
	Confidence    float32  `json:"confidence"`
}

TrackSplit represents a suspected track split between runs.

type TrackState

type TrackState string

TrackState represents the lifecycle state of a track.

const (
	TrackTentative TrackState = "tentative" // New track, needs confirmation
	TrackConfirmed TrackState = "confirmed" // Stable track with sufficient history
	TrackDeleted   TrackState = "deleted"   // Track marked for removal
)

type TrackState2D

type TrackState2D struct {
	X, Y                 float32     // State vector in world frame: [x y velocity_x velocity_y]
	VelocityX, VelocityY float32     // Velocity components in world frame
	CovarianceMatrix     [16]float32 // Row-major covariance (4x4). float32 saves RAM for 100-track performance.
}

TrackState2D represents the core kinematic state for Kalman filtering

type TrackStore

type TrackStore interface {
	InsertCluster(cluster *WorldCluster) (int64, error)
	InsertTrack(track *TrackedObject, worldFrame string) error
	UpdateTrack(track *TrackedObject, worldFrame string) error
	InsertTrackObservation(obs *TrackObservation) error
	ClearTracks(sensorID string) error
	GetTrack(trackID string) (*TrackedObject, error)
	GetActiveTracks(sensorID string, state string) ([]*TrackedObject, error)
	GetTracksInRange(sensorID string, state string, startNanos, endNanos int64, limit int) ([]*TrackedObject, error)
	GetTrackObservations(trackID string, limit int) ([]*TrackObservation, error)
	GetTrackObservationsInRange(sensorID string, startNanos, endNanos int64, limit int, trackID string) ([]*TrackObservation, error)
	GetRecentClusters(sensorID string, startNanos, endNanos int64, limit int) ([]*WorldCluster, error)
}

TrackStore defines the interface for track persistence operations.

type TrackSummary

type TrackSummary struct {
	TrackID    string  // matches schema track_id TEXT PRIMARY KEY
	SensorID   string  // matches schema sensor_id TEXT NOT NULL
	WorldFrame FrameID // matches schema world_frame TEXT NOT NULL
	UnixNanos  int64   // current observation timestamp

	// Current kinematics (world frame; road-plane oriented)
	X, Y                 float32 // current position
	VelocityX, VelocityY float32 // current velocity
	SpeedMps             float32 // current speed magnitude
	HeadingRad           float32 // current heading

	// Current shape/quality
	BoundingBoxLength float32
	BoundingBoxWidth  float32
	BoundingBoxHeight float32
	PointsCount       int
	HeightP95         float32
	IntensityMean     float32

	// Classification from track summary
	ClassLabel      string  // matches schema class_label TEXT
	ClassConfidence float32 // matches schema class_conf REAL

	// Optional uncertainty (for advanced fusion)
	Covariance4x4 []float32 // flattened 4x4 covariance of [x y velocity_x velocity_y]
}

TrackSummary for HTTP API responses - streamlined view of track state

type TrackTrainingFilter

type TrackTrainingFilter struct {
	MinQualityScore   float32      // Minimum composite quality score (0-1)
	MinDuration       float32      // Minimum track duration (seconds)
	MinLength         float32      // Minimum track length (meters)
	MaxOcclusionRatio float32      // Maximum occlusion ratio (occlusions / observations)
	MinObservations   int          // Minimum observation count
	RequireClass      bool         // Only include tracks with assigned class
	AllowedStates     []TrackState // Allowed track states (e.g., only confirmed)
}

TrackTrainingFilter defines criteria for selecting high-quality tracks for ML training.

func DefaultTrackTrainingFilter

func DefaultTrackTrainingFilter() *TrackTrainingFilter

DefaultTrackTrainingFilter returns sensible defaults for high-quality training tracks.

type TrackedObject

type TrackedObject struct {
	// Identity
	TrackID  string
	SensorID string
	State    TrackState

	// Lifecycle counters
	Hits   int // Consecutive successful associations
	Misses int // Consecutive missed associations

	// Timestamps
	FirstUnixNanos int64
	LastUnixNanos  int64

	// Kalman state (world frame): [x, y, vx, vy]
	X  float32 // Position X
	Y  float32 // Position Y
	VX float32 // Velocity X
	VY float32 // Velocity Y

	// Kalman covariance (4x4, row-major)
	P [16]float32

	// Aggregated features
	ObservationCount     int
	BoundingBoxLengthAvg float32
	BoundingBoxWidthAvg  float32
	BoundingBoxHeightAvg float32
	HeightP95Max         float32
	IntensityMeanAvg     float32
	AvgSpeedMps          float32
	PeakSpeedMps         float32

	// History of positions
	History []TrackPoint

	// Classification (Phase 3.4)
	ObjectClass         string  // Classification result: "pedestrian", "car", "bird", "other"
	ObjectConfidence    float32 // Classification confidence [0, 1]
	ClassificationModel string  // Model version used for classification

	// Phase 1: Track Quality Metrics
	TrackLengthMeters  float32 // Total distance traveled (meters)
	TrackDurationSecs  float32 // Total lifetime (seconds)
	OcclusionCount     int     // Number of missed frames (gaps)
	MaxOcclusionFrames int     // Longest gap in observations
	SpatialCoverage    float32 // % of bounding box covered by observations
	NoisePointRatio    float32 // Ratio of noise points to cluster points
	// contains filtered or unexported fields
}

TrackedObject represents a single tracked object in the tracker.

func FilterTracksForTraining

func FilterTracksForTraining(tracks []*TrackedObject, filter *TrackTrainingFilter) []*TrackedObject

FilterTracksForTraining selects tracks that meet training data quality criteria.

func GetActiveTracks

func GetActiveTracks(db *sql.DB, sensorID string, state string) ([]*TrackedObject, error)

GetActiveTracks retrieves active tracks from the database. If state is empty, returns all non-deleted tracks.

func GetTracksInRange

func GetTracksInRange(db *sql.DB, sensorID string, state string, startNanos, endNanos int64, limit int) ([]*TrackedObject, error)

GetTracksInRange retrieves tracks whose lifespan overlaps the given time window (nanoseconds). A track is included if its start is on/before endNanos and its end (or start when end is NULL) is on/after startNanos. Deleted tracks are excluded by default unless state explicitly requests them.

func (*TrackedObject) ComputeQualityMetrics

func (track *TrackedObject) ComputeQualityMetrics()

ComputeQualityMetrics calculates track quality metrics for Phase 1. This should be called when a track is finalized (state changes to deleted or when exporting).

func (*TrackedObject) Heading

func (track *TrackedObject) Heading() float32

Heading returns the current heading in radians for a track.

func (*TrackedObject) Speed

func (track *TrackedObject) Speed() float32

Speed returns the current speed magnitude for a track.

func (*TrackedObject) SpeedHistory

func (track *TrackedObject) SpeedHistory() []float32

SpeedHistory returns a copy of the track's speed history for percentile computation.

type Tracker

type Tracker struct {
	Tracks      map[string]*TrackedObject
	NextTrackID int64
	Config      TrackerConfig

	// Last update timestamp for dt computation
	LastUpdateNanos int64
	// contains filtered or unexported fields
}

Tracker manages multi-object tracking with explicit lifecycle states.

func NewTracker

func NewTracker(config TrackerConfig) *Tracker

NewTracker creates a new tracker with the specified configuration.

func (*Tracker) GetActiveTracks

func (t *Tracker) GetActiveTracks() []*TrackedObject

GetActiveTracks returns a slice of currently active (non-deleted) tracks.

func (*Tracker) GetAllTracks

func (t *Tracker) GetAllTracks() []*TrackedObject

GetAllTracks returns a slice of all tracks including deleted ones. This is useful for analysis and reporting after processing is complete.

func (*Tracker) GetConfirmedTracks

func (t *Tracker) GetConfirmedTracks() []*TrackedObject

GetConfirmedTracks returns only confirmed tracks.

func (*Tracker) GetTrack

func (t *Tracker) GetTrack(trackID string) *TrackedObject

GetTrack returns a track by ID, or nil if not found.

func (*Tracker) GetTrackCount

func (t *Tracker) GetTrackCount() (total, tentative, confirmed, deleted int)

GetTrackCount returns counts of tracks by state.

func (*Tracker) Update

func (t *Tracker) Update(clusters []WorldCluster, timestamp time.Time)

Update processes a new frame of clusters and updates tracks. This is the main entry point for the tracking pipeline.

type TrackerConfig

type TrackerConfig struct {
	MaxTracks               int           // Maximum number of concurrent tracks
	MaxMisses               int           // Consecutive misses before deletion
	HitsToConfirm           int           // Consecutive hits needed for confirmation
	GatingDistanceSquared   float32       // Squared gating distance for association (meters²)
	ProcessNoisePos         float32       // Process noise for position (σ²)
	ProcessNoiseVel         float32       // Process noise for velocity (σ²)
	MeasurementNoise        float32       // Measurement noise (σ²)
	DeletedTrackGracePeriod time.Duration // How long to keep deleted tracks before cleanup
}

TrackerConfig holds configuration parameters for the tracker.

func DefaultTrackerConfig

func DefaultTrackerConfig() TrackerConfig

DefaultTrackerConfig returns default tracker configuration.

type TrackingParamsExport

type TrackingParamsExport struct {
	MaxTracks               int           `json:"max_tracks"`
	MaxMisses               int           `json:"max_misses"`
	HitsToConfirm           int           `json:"hits_to_confirm"`
	GatingDistanceSquared   float32       `json:"gating_distance_squared"`
	ProcessNoisePos         float32       `json:"process_noise_pos"`
	ProcessNoiseVel         float32       `json:"process_noise_vel"`
	MeasurementNoise        float32       `json:"measurement_noise"`
	DeletedTrackGracePeriod time.Duration `json:"deleted_track_grace_period_nanos"`
}

TrackingParamsExport is the JSON-serializable tracking params.

func FromTrackerConfig

func FromTrackerConfig(c TrackerConfig) TrackingParamsExport

FromTrackerConfig creates export params from TrackerConfig.

type TrackingPipelineConfig

type TrackingPipelineConfig struct {
	BackgroundManager  *BackgroundManager
	FgForwarder        ForegroundForwarder // Use interface to avoid import cycle
	Tracker            *Tracker
	Classifier         *TrackClassifier
	DB                 *sql.DB // Use standard sql.DB to avoid import cycle with db package
	SensorID           string
	DebugMode          bool
	AnalysisRunManager *AnalysisRunManager // Optional: for recording analysis runs
}

TrackingPipelineConfig holds dependencies for the tracking pipeline callback.

func (*TrackingPipelineConfig) NewFrameCallback

func (cfg *TrackingPipelineConfig) NewFrameCallback() func(*LiDARFrame)

NewFrameCallback creates a FrameBuilder callback that processes frames through the full tracking pipeline: foreground extraction, clustering, tracking, and persistence.

type TrainingDataFilter

type TrainingDataFilter struct {
	SensorID       string // Filter by sensor (empty = all)
	SequenceID     string // Filter by sequence (empty = all)
	MinForeground  int    // Minimum foreground points per frame
	AnnotationOnly bool   // Only return annotated frames
}

TrainingDataFilter defines criteria for filtering training frames.

func DefaultTrainingDataFilter

func DefaultTrainingDataFilter() TrainingDataFilter

DefaultTrainingDataFilter returns a filter suitable for high-quality training data.

type TrainingDatasetSummary

type TrainingDatasetSummary struct {
	TotalTracks       int            `json:"total_tracks"`
	TotalFrames       int            `json:"total_frames"`
	TotalPoints       int            `json:"total_points"`
	ClassDistribution map[string]int `json:"class_distribution"`
	AvgQualityScore   float32        `json:"avg_quality_score"`
	AvgDuration       float32        `json:"avg_duration_secs"`
	AvgLength         float32        `json:"avg_length_meters"`
}

TrainingDatasetSummary provides statistics about a curated training dataset.

func SummarizeTrainingDataset

func SummarizeTrainingDataset(tracks []*TrackedObject) *TrainingDatasetSummary

SummarizeTrainingDataset generates statistics for a curated training dataset.

type TrainingFrameMetadata

type TrainingFrameMetadata struct {
	FrameID          int64
	SensorID         string
	TSUnixNanos      int64
	SequenceID       string
	TotalPoints      int
	ForegroundPoints int
	BackgroundPoints int
	AnnotationStatus string // "unlabeled", "in_progress", "labeled"
}

TrainingFrameMetadata contains metadata for a training frame without the point data. Useful for querying/filtering frames before loading full point clouds.

type WorldCluster

type WorldCluster struct {
	ClusterID         int64   // matches lidar_cluster_id INTEGER PRIMARY KEY
	SensorID          string  // matches sensor_id TEXT NOT NULL
	WorldFrame        FrameID // matches world_frame TEXT NOT NULL
	TSUnixNanos       int64   // matches ts_unix_nanos INTEGER NOT NULL
	CentroidX         float32 // matches centroid_x REAL
	CentroidY         float32 // matches centroid_y REAL
	CentroidZ         float32 // matches centroid_z REAL
	BoundingBoxLength float32 // matches bounding_box_length REAL
	BoundingBoxWidth  float32 // matches bounding_box_width REAL
	BoundingBoxHeight float32 // matches bounding_box_height REAL
	PointsCount       int     // matches points_count INTEGER
	HeightP95         float32 // matches height_p95 REAL
	IntensityMean     float32 // matches intensity_mean REAL

	// Debug hints matching schema optional fields
	SensorRingHint  *int     // matches sensor_ring_hint INTEGER
	SensorAzDegHint *float32 // matches sensor_azimuth_deg_hint REAL

	// Optional in-memory only fields (not persisted to schema)
	SamplePoints [][3]float32 // for debugging/thumbnails
}

WorldCluster matches schema lidar_clusters table structure exactly

func DBSCAN

func DBSCAN(points []WorldPoint, params DBSCANParams) []WorldCluster

DBSCAN performs density-based clustering on world points. Uses 2D (x, y) Euclidean distance. Z is used only for cluster features. Returns a slice of WorldCluster objects representing detected clusters.

func GetRecentClusters

func GetRecentClusters(db *sql.DB, sensorID string, startNanos, endNanos int64, limit int) ([]*WorldCluster, error)

GetRecentClusters retrieves recent clusters from the database.

type WorldPoint

type WorldPoint struct {
	X, Y, Z   float64   // World frame position (meters)
	Intensity uint8     // Laser return intensity
	Timestamp time.Time // Acquisition time
	SensorID  string    // Source sensor
}

WorldPoint represents a point in Cartesian world coordinates (site frame). This is the output of the polar → world transformation (Phase 3.0).

func TransformPointsToWorld

func TransformPointsToWorld(points []Point, pose *Pose) []WorldPoint

TransformPointsToWorld is a convenience function that uses Point (Cartesian sensor frame) instead of PointPolar. This is useful when points already have X,Y,Z computed.

func TransformToWorld

func TransformToWorld(polarPoints []PointPolar, pose *Pose, sensorID string) []WorldPoint

TransformToWorld converts foreground polar points to world frame coordinates. This is Phase 3.0 of the foreground tracking pipeline.

Steps: 1. Polar → Sensor Cartesian using spherical to Cartesian conversion 2. Sensor Cartesian → World Cartesian using pose transform

If pose is nil, an identity transform is used (sensor frame = world frame).

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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