Documentation
¶
Index ¶
- Constants
- func ConfigureTimestampMode(parser *Pandar40PParser)
- func ElevationsFromConfig(cfg *Pandar40PConfig) []float64
- type AngleCorrection
- type ChannelData
- type DataBlock
- type FiretimeCorrection
- type PacketHeader
- type PacketTail
- type Pandar40PConfig
- type Pandar40PParser
- func (p *Pandar40PParser) GetLastMotorSpeed() uint16
- func (p *Pandar40PParser) ParsePacket(data []byte) ([]lidar.PointPolar, error)
- func (p *Pandar40PParser) SetDebug(enabled bool)
- func (p *Pandar40PParser) SetDebugPackets(count int)
- func (p *Pandar40PParser) SetPacketTime(ts time.Time)
- func (p *Pandar40PParser) SetTimestampMode(mode TimestampMode)
- type TimestampMode
Constants ¶
const ( PACKET_SIZE_STANDARD = 1262 // Standard UDP packet size in bytes (without UDP sequence) PACKET_SIZE_SEQUENCE = 1266 // UDP packet size with 4-byte sequence number BLOCKS_PER_PACKET = 10 // Number of data blocks per packet (each contains measurements from all 40 channels) CHANNELS_PER_BLOCK = 40 // Number of laser channels per data block (Pandar40P has 40 channels total) BYTES_PER_CHANNEL = 3 // Channel data size: 2 bytes distance + 1 byte reflectivity HEADER_SIZE = 6 // Legacy constant - not used (blocks start at offset 0) TAIL_START = 1240 // Fixed tail start offset after 10 × 124-byte blocks TAIL_SIZE = 22 // Actual LiDAR data tail size in bytes SEQUENCE_SIZE = 4 // UDP sequence number size (when enabled) BLOCK_PREAMBLE_SIZE = 2 // Block preamble size (0xFFEE marker) AZIMUTH_SIZE = 2 // Azimuth field size in each data block (2 bytes, little-endian) BLOCK_SIZE = BLOCK_PREAMBLE_SIZE + AZIMUTH_SIZE + (CHANNELS_PER_BLOCK * BYTES_PER_CHANNEL) // 124 bytes total with preamble RANGING_DATA_SIZE = BLOCKS_PER_PACKET * BLOCK_SIZE // 1240 bytes total for all blocks // Physical measurement conversion constants DISTANCE_RESOLUTION = 0.004 // Distance unit: 4mm per LSB (converts raw values to meters) AZIMUTH_RESOLUTION = 0.01 // Azimuth unit: 0.01 degrees per LSB (converts raw values to degrees) ROTATION_MAX_UNITS = 36000 // Maximum azimuth value representing 360.00 degrees // Static timestamp detection threshold for PTP/GPS mode fallback STATIC_TIMESTAMP_THRESHOLD = 10 // Number of consecutive static timestamps before fallback to system time // Debug logging control constants for development and troubleshooting DEBUG_LOG_INTERVAL = 100 // Debug log every Nth packet after initial packets (reduces log volume) )
Pandar40P LiDAR packet structure constants These define the fixed format of UDP packets sent by Hesai Pandar40P sensors
Variables ¶
This section is empty.
Functions ¶
func ConfigureTimestampMode ¶
func ConfigureTimestampMode(parser *Pandar40PParser)
ConfigureTimestampMode configures the parser's timestamp mode based on environment variable LIDAR_TIMESTAMP_MODE. Valid values are: "system", "gps", "internal". If not set or invalid, defaults to "system" mode.
func ElevationsFromConfig ¶
func ElevationsFromConfig(cfg *Pandar40PConfig) []float64
ElevationsFromConfig extracts per-channel elevation angles (degrees) from a Pandar40PConfig. Returns nil if config is nil. Length equals CHANNELS_PER_BLOCK.
Types ¶
type AngleCorrection ¶
type AngleCorrection struct {
Channel int // Laser channel number (1-40) for identification
Elevation float64 // Vertical angle correction in degrees (relative to horizontal plane, typically ±15°)
Azimuth float64 // Horizontal angle correction in degrees (relative to sensor front, small corrections ~±1°)
}
AngleCorrection contains the angular calibration parameters for each laser channel These corrections account for mechanical tolerances in the sensor assembly and ensure that each laser channel's measurements are properly aligned in 3D space
type ChannelData ¶
type ChannelData struct {
Distance uint16 // Raw distance measurement in 4mm units (0 = no return, max ~262m)
Reflectivity uint8 // Laser return intensity/reflectivity value (0-255, higher = more reflective surface)
}
ChannelData represents the raw measurement from a single laser channel Contains the fundamental distance and intensity measurements that form the basis of the 3D point cloud data after calibration and coordinate transformation
type DataBlock ¶
type DataBlock struct {
Azimuth uint16 // Raw azimuth angle in 0.01-degree units (0-35999 = 0-359.99°)
Channels [CHANNELS_PER_BLOCK]ChannelData // Measurement data for all 40 channels at this azimuth
}
DataBlock represents one of 10 data blocks within a packet Each block contains measurements from all 40 channels at a specific azimuth angle. The Pandar40P captures data in discrete angular steps as the sensor rotates, with each block representing approximately 3.6° of rotation (360° / 100 blocks typical). Block structure: 2-byte preamble (0xFFEE) + 2-byte azimuth + 40 × 3-byte channel data
type FiretimeCorrection ¶
type FiretimeCorrection struct {
Channel int // Laser channel number (1-40) for identification
FireTime float64 // Time offset in microseconds when this channel fires relative to block start (can be negative)
}
FiretimeCorrection contains timing calibration for each laser channel Different channels fire at slightly different times to avoid interference and ensure proper laser pulse separation. This timing affects the precise azimuth calculation.
type PacketHeader ¶
type PacketHeader struct {
SOB uint16 // Start of Block identifier (packet format marker) - UNUSED
ChLaserNum uint8 // Channel and laser configuration number - UNUSED
ChBlockNum uint8 // Channel and block configuration number - UNUSED
FirstBlockReturn uint8 // Return mode for first block (single/dual return) - UNUSED
DisUnit uint8 // Distance measurement unit identifier - UNUSED
}
PacketHeader represents the theoretical 6-byte header at the start of UDP packets Contains metadata about the packet format and sensor configuration NOTE: This structure is DEPRECATED - analysis shows data blocks start at offset 0 Kept for reference only; actual parsing begins immediately with block preambles
type PacketTail ¶
type PacketTail struct {
// Reserved fields for future protocol extensions
Reserved1 [5]uint8 // Reserved (bytes 0-4) - available for future features
Reserved2 [2]uint8 // Reserved (bytes 6-7) - available for future features
// Sensor thermal management
HighTempFlag uint8 // High temperature shutdown flag: 0x01=High temp warning, 0x00=Normal operation
// Motor control and speed monitoring - CRITICAL for frame timing
MotorSpeed uint16 // Current motor speed in RPM (typically 600-1200, used for time-based frame detection)
// Timing information for precise timestamp calculation
Timestamp uint32 // Microsecond part of UTC timestamp, Range: 0 to 999,999 μs
// Return mode configuration for dual-return LiDAR operation
ReturnMode uint8 // Return mode: 0x37=Strongest, 0x38=Last, 0x39=Last+Strongest
// Sensor identification and configuration
FactoryInfo uint8 // Factory configuration identifier (typically 0x42 or 0x43)
// Accurate UTC date and time information
DateTime [6]uint8 // Whole second UTC timestamp: [year-2000, month, day, hour, minute, second]
// Computed high-precision timestamp combining DateTime + Timestamp
CombinedTimestamp time.Time // Accurate UTC timestamp (DateTime + Timestamp) for precise timing
// Optional packet sequencing for completeness tracking
UDPSequence uint32 // Sequence number of this data packet (when UDP sequencing enabled)
}
PacketTail represents the 22-byte data tail at the end of each LiDAR UDP packet Structure based on verified Hesai Pandar40P documentation and packet analysis: Reserved(5) + HighTempFlag(1) + Reserved(2) + MotorSpeed(2) + Timestamp(4) + ReturnMode(1) + FactoryInfo(1) + DateTime(6) + [UDPSequence(4) when enabled] This tail contains critical sensor state and timing information used for accurate 3D point generation, frame timing, and sensor health monitoring.
type Pandar40PConfig ¶
type Pandar40PConfig struct {
AngleCorrections [CHANNELS_PER_BLOCK]AngleCorrection // Per-channel angular calibration data (elevation & azimuth offsets)
FiretimeCorrections [CHANNELS_PER_BLOCK]FiretimeCorrection // Per-channel timing calibration data (microsecond firing delays)
}
Pandar40P configuration containing calibration data embedded in the binary This configuration is essential for accurate point cloud generation as it contains sensor-specific calibration parameters that correct for manufacturing tolerances. Each sensor has unique calibration values that must be applied to achieve millimeter precision.
func DefaultPandar40PConfig ¶
func DefaultPandar40PConfig() *Pandar40PConfig
DefaultPandar40PConfig returns a default configuration using embedded CSV files
func LoadEmbeddedPandar40PConfig ¶
func LoadEmbeddedPandar40PConfig() (*Pandar40PConfig, error)
LoadEmbeddedPandar40PConfig loads configuration from embedded CSV files only
func LoadPandar40PConfig ¶
func LoadPandar40PConfig() (*Pandar40PConfig, error)
LoadPandar40PConfig loads configuration from embedded CSV files
func (*Pandar40PConfig) Validate ¶
func (config *Pandar40PConfig) Validate() error
ValidateConfig validates that the configuration is complete
type Pandar40PParser ¶
type Pandar40PParser struct {
// contains filtered or unexported fields
}
The parser uses calibration data to convert raw measurements into accurate 3D coordinates and provides multiple timestamp modes for different synchronization requirements. Motor speed tracking enables real-time frame timing adaptation for variable RPM operation.
func NewPandar40PParser ¶
func NewPandar40PParser(config Pandar40PConfig) *Pandar40PParser
NewPandar40PParser creates a new parser instance with the provided calibration configuration The configuration must contain valid angle and firetime corrections for all 40 channels. Parser initializes with system time mode for reliability and configurable debug packet count.
func (*Pandar40PParser) GetLastMotorSpeed ¶
func (p *Pandar40PParser) GetLastMotorSpeed() uint16
GetLastMotorSpeed returns the motor speed from the last parsed packet Used by frame builder for real-time motor speed-based frame timing calculations
func (*Pandar40PParser) ParsePacket ¶
func (p *Pandar40PParser) ParsePacket(data []byte) ([]lidar.PointPolar, error)
ParsePacket parses a complete UDP packet from Pandar40P sensor into a slice of 3D points Supports both standard 1262-byte packets and sequence-enabled 1266-byte packets. The packet must contain valid data blocks and timestamp information. Returns up to 400 points (10 blocks × 40 channels, excluding invalid measurements). Motor speed from packet tail is cached for frame builder time-based detection.
func (*Pandar40PParser) SetDebug ¶
func (p *Pandar40PParser) SetDebug(enabled bool)
SetDebug enables or disables debug logging for development and troubleshooting
func (*Pandar40PParser) SetDebugPackets ¶
func (p *Pandar40PParser) SetDebugPackets(count int)
SetDebugPackets sets the number of initial packets to debug log (prevents log spam) Only affects logging when debug mode is enabled
func (*Pandar40PParser) SetPacketTime ¶
func (p *Pandar40PParser) SetPacketTime(ts time.Time)
SetPacketTime overrides the packet timestamp (used for PCAP replay capture times)
func (*Pandar40PParser) SetTimestampMode ¶
func (p *Pandar40PParser) SetTimestampMode(mode TimestampMode)
SetTimestampMode configures how the parser interprets LiDAR timestamps This affects frame timing accuracy and synchronization with external systems
type TimestampMode ¶
type TimestampMode int
Pandar40PParser handles parsing of Pandar40P LiDAR UDP packets into 3D point clouds TimestampMode defines how LiDAR timestamps should be interpreted for accurate timing
const ( TimestampModeSystemTime TimestampMode = iota // Use system reception time (reliable, default for street analytics) TimestampModePTP // PTP synchronized microseconds with static detection and fallback TimestampModeGPS // GPS synchronized microseconds with static detection and fallback TimestampModeInternal // Microseconds since device boot with bootTime offset alignment TimestampModeLiDAR // Use LiDAR's native DateTime + Timestamp fields (most accurate) )