Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions python/examples/pose_relay.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from fusion_engine_client.messages.measurements import (
ExternalPoseInput, SystemTimeSource,
)
from fusion_engine_client.messages.defs import SolutionType, Timestamp
from fusion_engine_client.messages.defs import SolutionType, SourceIdentifier, Timestamp
from fusion_engine_client.parsers import FusionEngineDecoder, FusionEngineEncoder
from fusion_engine_client.utils import trace as logging
from fusion_engine_client.utils.argument_parser import ArgumentParser
Expand Down Expand Up @@ -391,7 +391,9 @@ def main():
source_pose_msg, transformed_pose)

# Encode and send.
encoded_data = encoder.encode_message(ext_pose)
encoded_data = encoder.encode_message(
ext_pose,
source_identifier=SourceIdentifier.OUTPUT_LEVER_ARM)
logger.debug(bytes_to_hex(encoded_data, bytes_per_row=16, bytes_per_col=2))

target_transport.send(encoded_data)
Expand Down
2 changes: 1 addition & 1 deletion python/fusion_engine_client/analysis/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
SolutionType.RTKFloat: SolutionTypeInfo(name='RTK Float', style={'color': 'green'}),
SolutionType.RTKFixed: SolutionTypeInfo(name='RTK Fixed', style={'color': 'orange'}),
SolutionType.PPP: SolutionTypeInfo(name='PPP', style={'color': 'pink'}),
SolutionType.Visual: SolutionTypeInfo(name='Vision', style={'color': 'purple'}),
SolutionType.External: SolutionTypeInfo(name='External', style={'color': 'purple'}),
}


Expand Down
12 changes: 6 additions & 6 deletions python/fusion_engine_client/messages/defs.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ class SolutionType(IntEnum):
RTKFloat = 5
# Integrated position using dead reckoning.
Integrate = 6
# Using vision measurements.
Visual = 9
# Using external (vision, lidar) measurements.
External = 9
# GNSS precise point positioning (PPP) pseudorange/carrier phase solution.
PPP = 10

Expand Down Expand Up @@ -239,11 +239,11 @@ class SourceIdentifier(IntEnum):


class MessageHeader:
INVALID_SOURCE_ID = 0xFFFFFFFF

SYNC0 = 0x2E # '.'
SYNC1 = 0x31 # '1'

INVALID_SOURCE_ID = SourceIdentifier.INVALID

SYNC = bytes((SYNC0, SYNC1))

_FORMAT = '<BBHIBBHIII'
Expand All @@ -259,7 +259,7 @@ def __init__(self, message_type: MessageType = MessageType.INVALID):
self.message_version: int = 0
self.message_type: MessageType = message_type
self.payload_size_bytes: int = 0
self.source_identifier: int = MessageHeader.INVALID_SOURCE_ID
self.source_identifier: int = SourceIdentifier.INVALID

def get_type_string(self):
return MessageType.get_type_string(self.message_type)
Expand Down Expand Up @@ -321,7 +321,7 @@ def pack(self, buffer: bytes = None, offset: int = 0, payload: bytes = None, ret

args = (MessageHeader.SYNC0, MessageHeader.SYNC1, self.reserved, self.crc, self.protocol_version,
self.message_version, int(self.message_type), self.sequence_number, self.payload_size_bytes,
self.source_identifier)
int(self.source_identifier))
if buffer is None:
buffer = struct.pack(MessageHeader._FORMAT, *args)
if payload is not None:
Expand Down
5 changes: 3 additions & 2 deletions python/fusion_engine_client/parsers/encoder.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from ..messages import MessageHeader, MessagePayload
from ..messages import MessageHeader, MessagePayload, SourceIdentifier


class FusionEngineEncoder:
Expand All @@ -15,7 +15,8 @@ def __init__(self):
"""
self.sequence_number = 0

def encode_message(self, message: MessagePayload, source_identifier: int = 0) -> (bytes):
def encode_message(self, message: MessagePayload,
source_identifier: SourceIdentifier = SourceIdentifier.OUTPUT_LEVER_ARM) -> (bytes):
"""!
@brief Serialize a message with valid header and payload.

Expand Down
50 changes: 44 additions & 6 deletions src/point_one/fusion_engine/messages/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -530,8 +530,8 @@ enum class SolutionType : uint8_t {
RTKFloat = 5,
/** Integrated position using dead reckoning. */
Integrate = 6,
/** Using vision measurements. */
Visual = 9,
/** Using external (vision, lidar) measurements. */
External = 9,
/**
* GNSS precise point positioning (PPP) pseudorange/carrier phase solution.
*/
Expand Down Expand Up @@ -567,8 +567,8 @@ P1_CONSTEXPR_FUNC const char* to_string(SolutionType type) {
case SolutionType::Integrate:
return "Dead Reckoning";

case SolutionType::Visual:
return "Visual Navigation";
case SolutionType::External:
return "External Navigation";

case SolutionType::PPP:
return "PPP GNSS";
Expand All @@ -585,6 +585,43 @@ inline p1_ostream& operator<<(p1_ostream& stream, SolutionType type) {
return stream;
}

/**
* @brief Output solution/measurement source identifiers.
*/
enum class SourceIdentifier : uint32_t {
// 0 - 99 is reserved for pose solutions.
/**
* The location on the vehicle defined by the device's output lever arm
* setting.
*/
OUTPUT_LEVER_ARM = 0,

// 100 - 199 is reserved for IMUs.

// 300 - 399 is reserved for GNSS receivers/antennas.
/** Primary GNSS antenna. */
PRIMARY_GNSS_ANTENNA = 300,
/**
* Secondary/auxiliary GNSS antenna (heading + pitch/roll for dual-antenna
* systems).
*/
SECONDARY_GNSS_ANTENNA = 301,

// 500 - 599 is reserved for external pose sources, such as an external SLAM
// or VIO/LIO.
/** Invalid source identifier. */
INVALID = 0xFFFFFFFF,
};

/**
* @brief @ref SourceIdentifier stream operator.
* @ingroup enum_definitions
*/
inline p1_ostream& operator<<(p1_ostream& stream, SourceIdentifier id) {
stream << static_cast<uint32_t>(id);
return stream;
}

/** @} */

/**
Expand All @@ -598,7 +635,8 @@ struct P1_ALIGNAS(4) MessageHeader {
static constexpr uint8_t SYNC0 = 0x2E; // '.'
static constexpr uint8_t SYNC1 = 0x31; // '1'

static constexpr uint32_t INVALID_SOURCE_ID = 0xFFFFFFFF;
static constexpr SourceIdentifier INVALID_SOURCE_ID =
SourceIdentifier::INVALID;

/**
* The maximum expected message size (in bytes), used for sanity checking.
Expand Down Expand Up @@ -638,7 +676,7 @@ struct P1_ALIGNAS(4) MessageHeader {
uint32_t payload_size_bytes = 0;

/** Identifies the source of the serialized data. */
uint32_t source_identifier = INVALID_SOURCE_ID;
SourceIdentifier source_identifier = SourceIdentifier::INVALID;
};

/**
Expand Down
28 changes: 18 additions & 10 deletions src/point_one/fusion_engine/messages/measurements.h
Original file line number Diff line number Diff line change
Expand Up @@ -1278,14 +1278,15 @@ struct P1_ALIGNAS(4) RawGNSSAttitudeOutput : public MessagePayload {
* device or a vision system).
*
* Position is expressed in the ECEF frame, and velocity is expressed in the
* local ENU frame. @ref position_ecef_m should correspond to the output lever
* arm point configured on the receiving device (see @ref
* ConfigType::OUTPUT_LEVER_ARM), so that the position matches the point the
* device will report in its own @ref PoseMessage after initialization.
* local ENU frame. @ref position_ecef_m should correspond to the reference
* point selected by @ref MessageHeader::source_identifier - either the external
* source's own point (for example, a lidar/camera sensor origin), or the
* receiving device output point when the source identifier is set to @ref
* SourceIdentifier::OUTPUT_LEVER_ARM.
*
* Orientation is specified as yaw, pitch, roll (YPR) angles in the local ENU
* frame, following the same intrinsic Euler-321 convention as @ref
* PoseMessage::ypr_deg.
* PoseMessage::ypr_deg, but body-fixed to the external source frame.
*
* Any elements that are not available should be set to `NAN`. Standard
* deviation fields are specified in the same units as the corresponding
Expand All @@ -1310,8 +1311,12 @@ struct P1_ALIGNAS(4) ExternalPoseInput : public MessagePayload {
uint32_t flags = 0;

/**
* An estimate of the device's output position (in meters), resolved in the
* ECEF frame.
* An estimate of the external source's position (in meters), resolved in the
* ECEF frame. This should correspond to the reference point selected by
* @ref MessageHeader::source_identifier - either the source's own point (e.g.
* the lidar/camera sensor origin), for which the receiving device applies the
* configured lever arm, or the device output point when the source identifier
* is set to @ref SourceIdentifier::OUTPUT_LEVER_ARM.
*/
double position_ecef_m[3] = {NAN, NAN, NAN};

Expand All @@ -1321,9 +1326,12 @@ struct P1_ALIGNAS(4) ExternalPoseInput : public MessagePayload {
*/
float position_std_ecef_m[3] = {NAN, NAN, NAN};

/** An estimate of the device's output orientation (in degrees), resolved in
* the local ENU tangent plane. See @ref PoseMessage::ypr_deg for a complete
* rotation definition.
/**
* An estimate of the external source's orientation (in degrees), resolved
* in the local ENU tangent plane. This describes the frame selected by
* @ref MessageHeader::source_identifier (e.g. the lidar/camera sensor frame);
* the receiving device applies the configured mounting rotation. See @ref
* PoseMessage::ypr_deg for a complete rotation definition.
*/
float ypr_deg[3] = {NAN, NAN, NAN};

Expand Down
Loading