libs/kaptan-mavlink: add heartbeat and RAW_IMU sender
This commit is contained in:
1 parent
2491cc6fb3
commit
567eebe1f1
1 file changed
+123
-1
@@ -1,7 +1,79 @@
|
|||||||
//! MAVLink support for Kaptan.
|
//! MAVLink protocol adapter for Kaptan.
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
use kaptan_sensor::ImuSnapshot;
|
||||||
|
use mavlink::MessageData;
|
||||||
|
|
||||||
|
/// An owned MAVLink 2 frame supplied by rust-mavlink.
|
||||||
|
pub type MavlinkFrame = message::MAVLinkV2MessageRaw;
|
||||||
|
|
||||||
|
/// Owns MAVLink sender identity and packet sequence state.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub struct MavlinkSender {
|
||||||
|
system_id: u8,
|
||||||
|
component_id: u8,
|
||||||
|
next_sequence: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MavlinkSender {
|
||||||
|
/// Creates a sender beginning with MAVLink packet sequence zero.
|
||||||
|
pub const fn new(system_id: u8, component_id: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
system_id,
|
||||||
|
component_id,
|
||||||
|
next_sequence: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serializes a Kaptan heartbeat with the supplied system-health state.
|
||||||
|
pub fn heartbeat(&mut self, system_status: message::MavState) -> MavlinkFrame {
|
||||||
|
let heartbeat = message::HEARTBEAT_DATA {
|
||||||
|
custom_mode: 0,
|
||||||
|
mavtype: message::MavType::MAV_TYPE_QUADROTOR,
|
||||||
|
autopilot: message::MavAutopilot::MAV_AUTOPILOT_GENERIC,
|
||||||
|
base_mode: message::MavModeFlag::empty(),
|
||||||
|
system_status,
|
||||||
|
mavlink_version: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.serialize(&heartbeat)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Serializes a standard `RAW_IMU` frame from a flight-data snapshot.
|
||||||
|
pub fn raw_imu_snapshot(&mut self, snapshot: &ImuSnapshot) -> MavlinkFrame {
|
||||||
|
let raw_imu = message::RAW_IMU_DATA {
|
||||||
|
time_usec: snapshot.timestamp.as_microseconds(),
|
||||||
|
xacc: snapshot.acceleration_milligravity[0],
|
||||||
|
yacc: snapshot.acceleration_milligravity[1],
|
||||||
|
zacc: snapshot.acceleration_milligravity[2],
|
||||||
|
xgyro: snapshot.angular_rate_milliradians_per_second[0],
|
||||||
|
ygyro: snapshot.angular_rate_milliradians_per_second[1],
|
||||||
|
zgyro: snapshot.angular_rate_milliradians_per_second[2],
|
||||||
|
xmag: 0,
|
||||||
|
ymag: 0,
|
||||||
|
zmag: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.serialize(&raw_imu)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize<M: MessageData>(&mut self, message: &M) -> MavlinkFrame {
|
||||||
|
let mut raw = message::MAVLinkV2MessageRaw::new();
|
||||||
|
raw.serialize_message_data(
|
||||||
|
message::MavHeader {
|
||||||
|
sequence: self.next_sequence,
|
||||||
|
system_id: self.system_id,
|
||||||
|
component_id: self.component_id,
|
||||||
|
},
|
||||||
|
message,
|
||||||
|
);
|
||||||
|
self.next_sequence = self.next_sequence.wrapping_add(1);
|
||||||
|
|
||||||
|
raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Supported MAVLink message types for Kaptan.
|
/// Supported MAVLink message types for Kaptan.
|
||||||
pub mod message {
|
pub mod message {
|
||||||
pub use mavlink::MAVLinkV2MessageRaw;
|
pub use mavlink::MAVLinkV2MessageRaw;
|
||||||
@@ -11,3 +83,53 @@ pub mod message {
|
|||||||
#[cfg(kaptan_has_messages)]
|
#[cfg(kaptan_has_messages)]
|
||||||
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
include!(concat!(env!("OUT_DIR"), "/mod.rs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::MavlinkSender;
|
||||||
|
use kaptan_sensor::ImuSnapshot;
|
||||||
|
use kaptan_time::Timestamp;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn heartbeat_has_the_configured_sender_and_sequence() {
|
||||||
|
let mut sender = MavlinkSender::new(42, 7);
|
||||||
|
let frame = sender.heartbeat(crate::message::MavState::MAV_STATE_STANDBY);
|
||||||
|
let bytes = frame.raw_bytes();
|
||||||
|
|
||||||
|
assert_eq!(bytes[0], 0xFD);
|
||||||
|
assert_eq!(bytes[2], 0);
|
||||||
|
assert_eq!(bytes[3], 0);
|
||||||
|
assert_eq!(bytes[4], 0);
|
||||||
|
assert_eq!(bytes[5], 42);
|
||||||
|
assert_eq!(bytes[6], 7);
|
||||||
|
assert_eq!(&bytes[7..10], &[0, 0, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn heartbeat_preserves_the_requested_health_state() {
|
||||||
|
let mut sender = MavlinkSender::new(1, 1);
|
||||||
|
let frame = sender.heartbeat(crate::message::MavState::MAV_STATE_CRITICAL);
|
||||||
|
|
||||||
|
// MAVLink 2 header is 10 octets; HEARTBEAT.system_status is payload
|
||||||
|
// octet seven according to the common dialect wire layout.
|
||||||
|
assert_eq!(
|
||||||
|
frame.raw_bytes()[17],
|
||||||
|
crate::message::MavState::MAV_STATE_CRITICAL as u8
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn snapshot_adapter_uses_flight_data_timestamp_and_units() {
|
||||||
|
let mut sender = MavlinkSender::new(1, 1);
|
||||||
|
let snapshot = ImuSnapshot::new(
|
||||||
|
Timestamp::from_microseconds(123),
|
||||||
|
[1, 2, 3],
|
||||||
|
[4, 5, 6],
|
||||||
|
2_500,
|
||||||
|
);
|
||||||
|
|
||||||
|
let frame = sender.raw_imu_snapshot(&snapshot);
|
||||||
|
|
||||||
|
assert_eq!(&frame.raw_bytes()[7..10], &[27, 0, 0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user