l2frames

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Mar 25, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package l2frames owns Layer 2 (Frames) of the LiDAR data model.

Responsibilities: assembling raw points into complete rotation frames, coordinate geometry (polar ↔ Cartesian), and frame-level export. Key types: Point, FrameID, Pose.

Dependency rule: L2 may depend on L1, but never on L3+.

See docs/lidar/architecture/lidar-data-layer-model.md for the full layer model.

Index

Constants

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

Variables

This section is empty.

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 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 RegisterFrameBuilder

func RegisterFrameBuilder(sensorID string, fb *FrameBuilder)

RegisterFrameBuilder registers a FrameBuilder for a sensor ID.

func SetLogWriters

func SetLogWriters(ops, diag, trace io.Writer)

SetLogWriters configures the three logging streams for the l2frames package. Pass nil for any writer to disable that stream.

func SphericalToCartesian

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

SphericalToCartesian converts spherical coordinates to Cartesian. Uses the sensor coordinate system (X=forward, Y=right, Z=up).

func UnregisterFrameBuilder

func UnregisterFrameBuilder(sensorID string)

UnregisterFrameBuilder removes a FrameBuilder from the global registry.

Types

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 NewFrameBuilderDI

func NewFrameBuilderDI(config FrameBuilderConfig) *FrameBuilder

NewFrameBuilderDI creates a FrameBuilder without registering it in the global registry. Prefer this constructor when wiring dependencies explicitly via pipeline.SensorRuntime.

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. Both polar and Cartesian representations are stored on the frame. This is used by network listeners that parse into polar form.

func (*FrameBuilder) Close

func (fb *FrameBuilder) Close()

Close shuts down the frame callback worker and waits for it to drain. Must be called when the FrameBuilder is no longer needed to avoid goroutine leaks. Close is idempotent — subsequent calls are no-ops.

func (*FrameBuilder) DroppedFrames

func (fb *FrameBuilder) DroppedFrames() uint64

DroppedFrames returns the number of frames dropped due to a full callback channel. Useful for post-run diagnostics.

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) SetBlockOnFrameChannel

func (fb *FrameBuilder) SetBlockOnFrameChannel(block bool)

SetBlockOnFrameChannel enables or disables blocking mode for the frame callback channel. When true, finalizeFrame blocks until the pipeline accepts the frame (true back-pressure). When false (default), frames are dropped if the channel is full. Enable for analysis mode to ensure every frame is processed; disable for live mode where dropping is acceptable to maintain real-time throughput.

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

	// FrameChCapacity sets the buffered channel capacity for the frame
	// callback worker. Default 8 is adequate for live sensor input;
	// PCAP replay benefits from a larger buffer (e.g. 32) to absorb
	// short processing stalls without dropping frames.
	FrameChCapacity int
}

FrameBuilderConfig contains configuration for the FrameBuilder

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
	PolarPoints    []PointPolar // sensor-polar view: all points in polar coordinates
	Points         []Point      // sensor-Cartesian view: all points in Cartesian coordinates
	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 Point

type Point struct {
	// 3D Cartesian coordinates (computed from spherical measurements)
	X float64 // X coordinate in meters (forward direction from sensor)
	Y float64 // Y coordinate in meters (right direction from sensor)
	Z float64 // Z coordinate in meters (upward direction from sensor)

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

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

Point represents a point in sensor Cartesian coordinates.

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).

Jump to

Keyboard shortcuts

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