1use pamoja_mavlink::dialect::{
16 Message, MissionItemInt, SetPositionTargetGlobalInt, SetPositionTargetLocalNed,
17};
18use pamoja_mavlink::protocol::{
19 AckOutcome, CommandProtocol, MissionReceiver, MissionSender, ReceiverAction, ReceiverStep,
20 SenderStep, TypeMask, MAX_RETRIES,
21};
22use pamoja_mavlink::{dialect, Frame, Header};
23
24use crate::mavlink::{status_of, PamojaMavlinkFrame, PamojaMavlinkHeader};
25use crate::mavlink_schema::PamojaMavlinkMessage;
26use crate::{read_bytes, set_last_error, PamojaStatus};
27
28pub const PAMOJA_MAVLINK_MAX_RETRIES: u8 = 5;
31
32const _: () = assert!(PAMOJA_MAVLINK_MAX_RETRIES == MAX_RETRIES);
35
36pub const PAMOJA_MAVLINK_STEP_IGNORED: u32 = 0;
38
39pub const PAMOJA_MAVLINK_RECEIVER_REQUEST: u32 = 1;
41pub const PAMOJA_MAVLINK_RECEIVER_ACK: u32 = 2;
43
44pub const PAMOJA_MAVLINK_SENDER_REPLY: u32 = 1;
46pub const PAMOJA_MAVLINK_SENDER_FINISHED: u32 = 2;
48
49pub const PAMOJA_MAVLINK_ACK_UNRELATED: u32 = 1;
51pub const PAMOJA_MAVLINK_ACK_IN_PROGRESS: u32 = 2;
54pub const PAMOJA_MAVLINK_ACK_FINAL: u32 = 3;
56
57pub const PAMOJA_MAVLINK_TYPEMASK_POSITION: u32 = 1 << 0;
59pub const PAMOJA_MAVLINK_TYPEMASK_VELOCITY: u32 = 1 << 1;
61pub const PAMOJA_MAVLINK_TYPEMASK_ACCELERATION: u32 = 1 << 2;
63pub const PAMOJA_MAVLINK_TYPEMASK_YAW: u32 = 1 << 3;
65pub const PAMOJA_MAVLINK_TYPEMASK_YAW_RATE: u32 = 1 << 4;
67pub const PAMOJA_MAVLINK_TYPEMASK_FORCE: u32 = 1 << 5;
69
70unsafe fn emit(frame: Frame, out_frame: *mut *mut PamojaMavlinkFrame) -> PamojaStatus {
72 if out_frame.is_null() {
73 set_last_error("out_frame must not be null".to_owned());
74 return PamojaStatus::InvalidArgument;
75 }
76 *out_frame = PamojaMavlinkFrame::into_handle(frame);
77 PamojaStatus::Ok
78}
79
80pub struct PamojaMavlinkMissionReceiver {
82 inner: MissionReceiver,
83}
84
85#[no_mangle]
97pub extern "C" fn pamoja_mavlink_mission_receiver_new(
98 target_system: u8,
99 target_component: u8,
100 mission_type: u8,
101) -> *mut PamojaMavlinkMissionReceiver {
102 Box::into_raw(Box::new(PamojaMavlinkMissionReceiver {
103 inner: MissionReceiver::new(target_system, target_component, mission_type),
104 }))
105}
106
107#[no_mangle]
129pub unsafe extern "C" fn pamoja_mavlink_mission_receiver_request_list(
130 receiver: *const PamojaMavlinkMissionReceiver,
131 header: PamojaMavlinkHeader,
132 out_frame: *mut *mut PamojaMavlinkFrame,
133) -> PamojaStatus {
134 let Some(receiver) = receiver.as_ref() else {
135 set_last_error("receiver must not be null".to_owned());
136 return PamojaStatus::InvalidArgument;
137 };
138 match receiver.inner.request_list_frame(Header::from(header)) {
139 Ok(frame) => emit(frame, out_frame),
140 Err(error) => status_of(error),
141 }
142}
143
144#[no_mangle]
175pub unsafe extern "C" fn pamoja_mavlink_mission_receiver_on_frame(
176 receiver: *mut PamojaMavlinkMissionReceiver,
177 frame: *const PamojaMavlinkFrame,
178 header: PamojaMavlinkHeader,
179 out_kind: *mut u32,
180 out_accepted: *mut *mut PamojaMavlinkMessage,
181 out_reply: *mut *mut PamojaMavlinkFrame,
182) -> PamojaStatus {
183 let Some(receiver) = receiver.as_mut() else {
184 set_last_error("receiver must not be null".to_owned());
185 return PamojaStatus::InvalidArgument;
186 };
187 let Some(frame) = frame.as_ref() else {
188 set_last_error("frame must not be null".to_owned());
189 return PamojaStatus::InvalidArgument;
190 };
191 if out_kind.is_null() || out_accepted.is_null() || out_reply.is_null() {
192 set_last_error("the output pointers must not be null".to_owned());
193 return PamojaStatus::InvalidArgument;
194 }
195 *out_kind = PAMOJA_MAVLINK_STEP_IGNORED;
196 *out_accepted = std::ptr::null_mut();
197 *out_reply = std::ptr::null_mut();
198
199 let step = match receiver.inner.on_frame(frame.frame(), Header::from(header)) {
200 Ok(Some(step)) => step,
201 Ok(None) => return PamojaStatus::Ok,
202 Err(error) => return status_of(error),
203 };
204 let ReceiverStep {
205 accepted,
206 action,
207 reply,
208 } = step;
209 *out_kind = match action {
210 ReceiverAction::Request(_) => PAMOJA_MAVLINK_RECEIVER_REQUEST,
211 ReceiverAction::Ack(_) => PAMOJA_MAVLINK_RECEIVER_ACK,
212 };
213 if let Some(item) = accepted {
214 let mut payload = [0u8; pamoja_mavlink::MAX_PAYLOAD];
215 let len = item.encode(&mut payload);
216 *out_accepted =
217 PamojaMavlinkMessage::from_typed(MissionItemInt::DESCRIPTOR, payload[..len].to_vec());
218 }
219 *out_reply = PamojaMavlinkFrame::into_handle(reply);
220 PamojaStatus::Ok
221}
222
223#[no_mangle]
238pub unsafe extern "C" fn pamoja_mavlink_mission_receiver_is_complete(
239 receiver: *const PamojaMavlinkMissionReceiver,
240) -> u8 {
241 receiver
242 .as_ref()
243 .map_or(0, |receiver| u8::from(receiver.inner.is_complete()))
244}
245
246#[no_mangle]
260pub unsafe extern "C" fn pamoja_mavlink_mission_receiver_expected(
261 receiver: *const PamojaMavlinkMissionReceiver,
262) -> u16 {
263 receiver
264 .as_ref()
265 .map_or(0, |receiver| receiver.inner.expected())
266}
267
268#[no_mangle]
279pub unsafe extern "C" fn pamoja_mavlink_mission_receiver_free(
280 receiver: *mut PamojaMavlinkMissionReceiver,
281) {
282 if !receiver.is_null() {
283 drop(Box::from_raw(receiver));
284 }
285}
286
287pub struct PamojaMavlinkMissionSender {
289 items: Vec<MissionItemInt>,
290 target_system: u8,
291 target_component: u8,
292 mission_type: u8,
293}
294
295impl PamojaMavlinkMissionSender {
296 fn with<R>(&self, query: impl FnOnce(&MissionSender<'_>) -> R) -> R {
298 query(&MissionSender::new(
299 &self.items,
300 self.target_system,
301 self.target_component,
302 self.mission_type,
303 ))
304 }
305}
306
307#[no_mangle]
319pub extern "C" fn pamoja_mavlink_mission_sender_new(
320 target_system: u8,
321 target_component: u8,
322 mission_type: u8,
323) -> *mut PamojaMavlinkMissionSender {
324 Box::into_raw(Box::new(PamojaMavlinkMissionSender {
325 items: Vec::new(),
326 target_system,
327 target_component,
328 mission_type,
329 }))
330}
331
332#[no_mangle]
359pub unsafe extern "C" fn pamoja_mavlink_mission_sender_add_item(
360 sender: *mut PamojaMavlinkMissionSender,
361 payload: *const u8,
362 payload_len: usize,
363) -> PamojaStatus {
364 let Some(sender) = sender.as_mut() else {
365 set_last_error("sender must not be null".to_owned());
366 return PamojaStatus::InvalidArgument;
367 };
368 let payload = match read_bytes(payload, payload_len) {
369 Ok(payload) => payload,
370 Err(status) => return status,
371 };
372 match MissionItemInt::decode(&payload) {
373 Ok(item) => {
374 sender.items.push(item);
375 PamojaStatus::Ok
376 }
377 Err(error) => status_of(error),
378 }
379}
380
381#[no_mangle]
395pub unsafe extern "C" fn pamoja_mavlink_mission_sender_len(
396 sender: *const PamojaMavlinkMissionSender,
397) -> u16 {
398 sender
399 .as_ref()
400 .map_or(0, |sender| sender.with(|plan| plan.len()))
401}
402
403#[no_mangle]
425pub unsafe extern "C" fn pamoja_mavlink_mission_sender_count(
426 sender: *const PamojaMavlinkMissionSender,
427 header: PamojaMavlinkHeader,
428 out_frame: *mut *mut PamojaMavlinkFrame,
429) -> PamojaStatus {
430 let Some(sender) = sender.as_ref() else {
431 set_last_error("sender must not be null".to_owned());
432 return PamojaStatus::InvalidArgument;
433 };
434 match sender.with(|plan| plan.count_frame(Header::from(header))) {
435 Ok(frame) => emit(frame, out_frame),
436 Err(error) => status_of(error),
437 }
438}
439
440#[no_mangle]
471pub unsafe extern "C" fn pamoja_mavlink_mission_sender_on_frame(
472 sender: *const PamojaMavlinkMissionSender,
473 frame: *const PamojaMavlinkFrame,
474 header: PamojaMavlinkHeader,
475 out_kind: *mut u32,
476 out_result: *mut u8,
477 out_reply: *mut *mut PamojaMavlinkFrame,
478) -> PamojaStatus {
479 let Some(sender) = sender.as_ref() else {
480 set_last_error("sender must not be null".to_owned());
481 return PamojaStatus::InvalidArgument;
482 };
483 let Some(frame) = frame.as_ref() else {
484 set_last_error("frame must not be null".to_owned());
485 return PamojaStatus::InvalidArgument;
486 };
487 if out_kind.is_null() || out_result.is_null() || out_reply.is_null() {
488 set_last_error("the output pointers must not be null".to_owned());
489 return PamojaStatus::InvalidArgument;
490 }
491 *out_kind = PAMOJA_MAVLINK_STEP_IGNORED;
492 *out_result = 0;
493 *out_reply = std::ptr::null_mut();
494
495 match sender.with(|plan| plan.on_frame(frame.frame(), Header::from(header))) {
496 Ok(Some(SenderStep::Reply(reply))) => {
497 *out_kind = PAMOJA_MAVLINK_SENDER_REPLY;
498 *out_reply = PamojaMavlinkFrame::into_handle(reply);
499 PamojaStatus::Ok
500 }
501 Ok(Some(SenderStep::Finished(result))) => {
502 *out_kind = PAMOJA_MAVLINK_SENDER_FINISHED;
503 *out_result = result;
504 PamojaStatus::Ok
505 }
506 Ok(None) => PamojaStatus::Ok,
507 Err(error) => status_of(error),
508 }
509}
510
511#[no_mangle]
522pub unsafe extern "C" fn pamoja_mavlink_mission_sender_free(
523 sender: *mut PamojaMavlinkMissionSender,
524) {
525 if !sender.is_null() {
526 drop(Box::from_raw(sender));
527 }
528}
529
530pub struct PamojaMavlinkCommand {
532 inner: CommandProtocol,
533}
534
535#[no_mangle]
547pub extern "C" fn pamoja_mavlink_command_new(
548 command: u16,
549 max_retries: u8,
550) -> *mut PamojaMavlinkCommand {
551 Box::into_raw(Box::new(PamojaMavlinkCommand {
552 inner: CommandProtocol::new(command, max_retries),
553 }))
554}
555
556#[no_mangle]
570pub unsafe extern "C" fn pamoja_mavlink_command_id(command: *const PamojaMavlinkCommand) -> u16 {
571 command
572 .as_ref()
573 .map_or(0, |command| command.inner.command())
574}
575
576#[no_mangle]
593pub unsafe extern "C" fn pamoja_mavlink_command_confirmation(
594 command: *const PamojaMavlinkCommand,
595) -> u8 {
596 command
597 .as_ref()
598 .map_or(0, |command| command.inner.confirmation())
599}
600
601#[no_mangle]
626pub unsafe extern "C" fn pamoja_mavlink_command_on_frame(
627 command: *const PamojaMavlinkCommand,
628 frame: *const PamojaMavlinkFrame,
629 out_kind: *mut u32,
630 out_value: *mut u8,
631) -> PamojaStatus {
632 let Some(command) = command.as_ref() else {
633 set_last_error("command must not be null".to_owned());
634 return PamojaStatus::InvalidArgument;
635 };
636 let Some(frame) = frame.as_ref() else {
637 set_last_error("frame must not be null".to_owned());
638 return PamojaStatus::InvalidArgument;
639 };
640 if out_kind.is_null() || out_value.is_null() {
641 set_last_error("the output pointers must not be null".to_owned());
642 return PamojaStatus::InvalidArgument;
643 }
644 *out_kind = PAMOJA_MAVLINK_STEP_IGNORED;
645 *out_value = 0;
646 match command.inner.on_frame(frame.frame()) {
647 Ok(Some(AckOutcome::Unrelated)) => *out_kind = PAMOJA_MAVLINK_ACK_UNRELATED,
648 Ok(Some(AckOutcome::InProgress(progress))) => {
649 *out_kind = PAMOJA_MAVLINK_ACK_IN_PROGRESS;
650 *out_value = progress;
651 }
652 Ok(Some(AckOutcome::Final(result))) => {
653 *out_kind = PAMOJA_MAVLINK_ACK_FINAL;
654 *out_value = result;
655 }
656 Ok(None) => {}
657 Err(error) => return status_of(error),
658 }
659 PamojaStatus::Ok
660}
661
662#[no_mangle]
682pub unsafe extern "C" fn pamoja_mavlink_command_on_timeout(
683 command: *mut PamojaMavlinkCommand,
684 out_confirmation: *mut u8,
685) -> u8 {
686 let Some(command) = command.as_mut() else {
687 return 0;
688 };
689 if out_confirmation.is_null() {
690 return 0;
691 }
692 match command.inner.on_timeout() {
693 Some(confirmation) => {
694 *out_confirmation = confirmation;
695 1
696 }
697 None => 0,
698 }
699}
700
701#[no_mangle]
712pub unsafe extern "C" fn pamoja_mavlink_command_free(command: *mut PamojaMavlinkCommand) {
713 if !command.is_null() {
714 drop(Box::from_raw(command));
715 }
716}
717
718#[no_mangle]
731pub extern "C" fn pamoja_mavlink_offboard_type_mask(flags: u32) -> u16 {
732 let mut mask = TypeMask::ignore_all();
733 if flags & PAMOJA_MAVLINK_TYPEMASK_POSITION != 0 {
734 mask = mask.use_position();
735 }
736 if flags & PAMOJA_MAVLINK_TYPEMASK_VELOCITY != 0 {
737 mask = mask.use_velocity();
738 }
739 if flags & PAMOJA_MAVLINK_TYPEMASK_ACCELERATION != 0 {
740 mask = mask.use_acceleration();
741 }
742 if flags & PAMOJA_MAVLINK_TYPEMASK_YAW != 0 {
743 mask = mask.use_yaw();
744 }
745 if flags & PAMOJA_MAVLINK_TYPEMASK_YAW_RATE != 0 {
746 mask = mask.use_yaw_rate();
747 }
748 if flags & PAMOJA_MAVLINK_TYPEMASK_FORCE != 0 {
749 mask = mask.force();
750 }
751 mask.bits()
752}
753
754#[no_mangle]
779#[allow(clippy::too_many_arguments)]
780pub unsafe extern "C" fn pamoja_mavlink_offboard_local_position(
781 header: PamojaMavlinkHeader,
782 time_boot_ms: u32,
783 coordinate_frame: u8,
784 target_system: u8,
785 target_component: u8,
786 x: f32,
787 y: f32,
788 z: f32,
789 out_frame: *mut *mut PamojaMavlinkFrame,
790) -> PamojaStatus {
791 let setpoint = SetPositionTargetLocalNed::position(
792 time_boot_ms,
793 coordinate_frame,
794 target_system,
795 target_component,
796 x,
797 y,
798 z,
799 );
800 match dialect::encode_message(Header::from(header), &setpoint) {
801 Ok(frame) => emit(frame, out_frame),
802 Err(error) => status_of(error),
803 }
804}
805
806#[no_mangle]
831#[allow(clippy::too_many_arguments)]
832pub unsafe extern "C" fn pamoja_mavlink_offboard_local_velocity(
833 header: PamojaMavlinkHeader,
834 time_boot_ms: u32,
835 coordinate_frame: u8,
836 target_system: u8,
837 target_component: u8,
838 vx: f32,
839 vy: f32,
840 vz: f32,
841 out_frame: *mut *mut PamojaMavlinkFrame,
842) -> PamojaStatus {
843 let setpoint = SetPositionTargetLocalNed::velocity(
844 time_boot_ms,
845 coordinate_frame,
846 target_system,
847 target_component,
848 vx,
849 vy,
850 vz,
851 );
852 match dialect::encode_message(Header::from(header), &setpoint) {
853 Ok(frame) => emit(frame, out_frame),
854 Err(error) => status_of(error),
855 }
856}
857
858#[no_mangle]
884#[allow(clippy::too_many_arguments)]
885pub unsafe extern "C" fn pamoja_mavlink_offboard_global_position(
886 header: PamojaMavlinkHeader,
887 time_boot_ms: u32,
888 coordinate_frame: u8,
889 target_system: u8,
890 target_component: u8,
891 lat_int: i32,
892 lon_int: i32,
893 alt: f32,
894 out_frame: *mut *mut PamojaMavlinkFrame,
895) -> PamojaStatus {
896 let setpoint = SetPositionTargetGlobalInt::position(
897 time_boot_ms,
898 coordinate_frame,
899 target_system,
900 target_component,
901 lat_int,
902 lon_int,
903 alt,
904 );
905 match dialect::encode_message(Header::from(header), &setpoint) {
906 Ok(frame) => emit(frame, out_frame),
907 Err(error) => status_of(error),
908 }
909}
910
911#[cfg(test)]
912mod tests {
913 use super::*;
914 use crate::mavlink::{pamoja_mavlink_frame_free, pamoja_mavlink_frame_message_id};
915 use crate::mavlink_schema::pamoja_mavlink_message_free;
916 use pamoja_mavlink::dialect::{mav_cmd, mav_mission_result, mav_result, CommandAck};
917
918 const VEHICLE: PamojaMavlinkHeader = PamojaMavlinkHeader {
919 system_id: 1,
920 component_id: 1,
921 sequence: 0,
922 };
923 const STATION: PamojaMavlinkHeader = PamojaMavlinkHeader {
924 system_id: 255,
925 component_id: 190,
926 sequence: 0,
927 };
928
929 fn item_payload(command: u16, z: f32) -> Vec<u8> {
930 let item = MissionItemInt {
931 command,
932 z,
933 ..MissionItemInt::zeroed()
934 };
935 let mut payload = [0u8; pamoja_mavlink::MAX_PAYLOAD];
936 let len = item.encode(&mut payload);
937 payload[..len].to_vec()
938 }
939
940 #[test]
941 fn a_whole_upload_runs_across_the_boundary() {
942 unsafe {
943 let sender = pamoja_mavlink_mission_sender_new(1, 1, 0);
944 for (command, z) in [(mav_cmd::NAV_TAKEOFF, 20.0), (mav_cmd::NAV_WAYPOINT, 50.0)] {
945 let payload = item_payload(command, z);
946 assert_eq!(
947 pamoja_mavlink_mission_sender_add_item(sender, payload.as_ptr(), payload.len()),
948 PamojaStatus::Ok
949 );
950 }
951 assert_eq!(pamoja_mavlink_mission_sender_len(sender), 2);
952
953 let receiver = pamoja_mavlink_mission_receiver_new(255, 190, 0);
954 let mut opened = std::ptr::null_mut();
955 assert_eq!(
956 pamoja_mavlink_mission_receiver_request_list(receiver, STATION, &mut opened),
957 PamojaStatus::Ok
958 );
959
960 let mut kind = 0;
961 let mut result = 0;
962 let mut from_vehicle = std::ptr::null_mut();
963 assert_eq!(
964 pamoja_mavlink_mission_sender_on_frame(
965 sender,
966 opened,
967 VEHICLE,
968 &mut kind,
969 &mut result,
970 &mut from_vehicle
971 ),
972 PamojaStatus::Ok
973 );
974 pamoja_mavlink_frame_free(opened);
975 assert_eq!(kind, PAMOJA_MAVLINK_SENDER_REPLY);
976 assert_eq!(pamoja_mavlink_frame_message_id(from_vehicle), 44);
977
978 let mut accepted_items = 0;
979 loop {
980 let mut accepted = std::ptr::null_mut();
981 let mut reply = std::ptr::null_mut();
982 assert_eq!(
983 pamoja_mavlink_mission_receiver_on_frame(
984 receiver,
985 from_vehicle,
986 STATION,
987 &mut kind,
988 &mut accepted,
989 &mut reply
990 ),
991 PamojaStatus::Ok
992 );
993 pamoja_mavlink_frame_free(from_vehicle);
994 assert_ne!(kind, PAMOJA_MAVLINK_STEP_IGNORED);
995 if !accepted.is_null() {
996 accepted_items += 1;
997 pamoja_mavlink_message_free(accepted);
998 }
999
1000 let mut next = std::ptr::null_mut();
1001 assert_eq!(
1002 pamoja_mavlink_mission_sender_on_frame(
1003 sender,
1004 reply,
1005 VEHICLE,
1006 &mut kind,
1007 &mut result,
1008 &mut next
1009 ),
1010 PamojaStatus::Ok
1011 );
1012 pamoja_mavlink_frame_free(reply);
1013 if kind == PAMOJA_MAVLINK_SENDER_FINISHED {
1014 assert_eq!(result, mav_mission_result::ACCEPTED);
1015 break;
1016 }
1017 assert_eq!(kind, PAMOJA_MAVLINK_SENDER_REPLY);
1018 from_vehicle = next;
1019 }
1020 assert_eq!(accepted_items, 2);
1021 assert_eq!(pamoja_mavlink_mission_receiver_is_complete(receiver), 1);
1022
1023 pamoja_mavlink_mission_receiver_free(receiver);
1024 pamoja_mavlink_mission_sender_free(sender);
1025 }
1026 }
1027
1028 #[test]
1029 fn a_command_is_matched_to_its_acknowledgment_and_retried() {
1030 unsafe {
1031 let arm = pamoja_mavlink_command_new(mav_cmd::COMPONENT_ARM_DISARM, 2);
1032 assert_eq!(
1033 pamoja_mavlink_command_id(arm),
1034 mav_cmd::COMPONENT_ARM_DISARM
1035 );
1036 assert_eq!(pamoja_mavlink_command_confirmation(arm), 0);
1037
1038 let ack = CommandAck {
1039 command: mav_cmd::COMPONENT_ARM_DISARM,
1040 result: mav_result::ACCEPTED,
1041 ..CommandAck::zeroed()
1042 };
1043 let frame = PamojaMavlinkFrame::into_handle(
1044 dialect::encode_message(Header::from(VEHICLE), &ack).unwrap(),
1045 );
1046 let mut kind = 0;
1047 let mut value = 0;
1048 assert_eq!(
1049 pamoja_mavlink_command_on_frame(arm, frame, &mut kind, &mut value),
1050 PamojaStatus::Ok
1051 );
1052 assert_eq!(kind, PAMOJA_MAVLINK_ACK_FINAL);
1053 assert_eq!(value, mav_result::ACCEPTED);
1054 pamoja_mavlink_frame_free(frame);
1055
1056 let mut confirmation = 0;
1058 assert_eq!(pamoja_mavlink_command_on_timeout(arm, &mut confirmation), 1);
1059 assert_eq!(confirmation, 1);
1060 assert_eq!(pamoja_mavlink_command_on_timeout(arm, &mut confirmation), 1);
1061 assert_eq!(confirmation, 2);
1062 assert_eq!(pamoja_mavlink_command_on_timeout(arm, &mut confirmation), 0);
1063 pamoja_mavlink_command_free(arm);
1064 }
1065 }
1066
1067 #[test]
1068 fn a_setpoint_goes_out_as_the_right_message() {
1069 unsafe {
1070 let mut frame = std::ptr::null_mut();
1071 assert_eq!(
1072 pamoja_mavlink_offboard_local_velocity(
1073 STATION, 1_000, 1, 1, 1, 0.5, 0.0, 0.0, &mut frame
1074 ),
1075 PamojaStatus::Ok
1076 );
1077 assert_eq!(pamoja_mavlink_frame_message_id(frame), 84);
1078 pamoja_mavlink_frame_free(frame);
1079
1080 assert_eq!(
1081 pamoja_mavlink_offboard_global_position(
1082 STATION,
1083 1_000,
1084 6,
1085 1,
1086 1,
1087 -338_567_800,
1088 1_512_153_000,
1089 50.0,
1090 &mut frame
1091 ),
1092 PamojaStatus::Ok
1093 );
1094 assert_eq!(pamoja_mavlink_frame_message_id(frame), 86);
1095 pamoja_mavlink_frame_free(frame);
1096
1097 let mask = pamoja_mavlink_offboard_type_mask(
1098 PAMOJA_MAVLINK_TYPEMASK_VELOCITY | PAMOJA_MAVLINK_TYPEMASK_YAW_RATE,
1099 );
1100 assert_eq!(
1101 mask,
1102 TypeMask::ignore_all().use_velocity().use_yaw_rate().bits()
1103 );
1104 }
1105 }
1106}