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
- func ApplyPose(x, y, z float64, T [16]float64) (wx, wy, wz float64)
- func ExportPointsToASC(points []PointASC, extraHeader string) (string, error)
- func RegisterFrameBuilder(sensorID string, fb *FrameBuilder)
- func SetLogWriters(ops, diag, trace io.Writer)
- func SphericalToCartesian(distance, azimuthDeg, elevationDeg float64) (x, y, z float64)
- func UnregisterFrameBuilder(sensorID string)
- type FrameBuilder
- func GetFrameBuilder(sensorID string) *FrameBuilder
- func NewFrameBuilder(config FrameBuilderConfig) *FrameBuilder
- func NewFrameBuilderDI(config FrameBuilderConfig) *FrameBuilder
- func NewFrameBuilderWithDebugLogging(sensorID string, debug bool) *FrameBuilder
- func NewFrameBuilderWithDebugLoggingAndInterval(sensorID string, debug bool, logInterval time.Duration) *FrameBuilder
- func NewFrameBuilderWithLogging(sensorID string) *FrameBuilder
- func (fb *FrameBuilder) AddPointsPolar(polar []PointPolar)
- func (fb *FrameBuilder) Close()
- func (fb *FrameBuilder) DroppedFrames() uint64
- func (fb *FrameBuilder) EnableTimeBased(enable bool)
- func (fb *FrameBuilder) GetCurrentFrameStats() (frameCount int, oldestAge time.Duration, newestAge time.Duration)
- func (fb *FrameBuilder) RequestExportFrameBatchASC(count int)
- func (fb *FrameBuilder) RequestExportNextFrameASC()
- func (fb *FrameBuilder) Reset()
- func (fb *FrameBuilder) SetBlockOnFrameChannel(block bool)
- func (fb *FrameBuilder) SetMotorSpeed(rpm uint16)
- type FrameBuilderConfig
- type LiDARFrame
- type Point
- type PointASC
- type PointPolar
Constants ¶
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 ¶
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 ¶
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 ¶
SetLogWriters configures the three logging streams for the l2frames package. Pass nil for any writer to disable that stream.
func SphericalToCartesian ¶
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 ¶
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).