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 = MAX_RETRIES;
31
32pub const PAMOJA_MAVLINK_STEP_IGNORED: u32 = 0;
34
35pub const PAMOJA_MAVLINK_RECEIVER_REQUEST: u32 = 1;
37pub const PAMOJA_MAVLINK_RECEIVER_ACK: u32 = 2;
39
40pub const PAMOJA_MAVLINK_SENDER_REPLY: u32 = 1;
42pub const PAMOJA_MAVLINK_SENDER_FINISHED: u32 = 2;
44
45pub const PAMOJA_MAVLINK_ACK_UNRELATED: u32 = 1;
47pub const PAMOJA_MAVLINK_ACK_IN_PROGRESS: u32 = 2;
50pub const PAMOJA_MAVLINK_ACK_FINAL: u32 = 3;
52
53pub const PAMOJA_MAVLINK_TYPEMASK_POSITION: u32 = 1 << 0;
55pub const PAMOJA_MAVLINK_TYPEMASK_VELOCITY: u32 = 1 << 1;
57pub const PAMOJA_MAVLINK_TYPEMASK_ACCELERATION: u32 = 1 << 2;
59pub const PAMOJA_MAVLINK_TYPEMASK_YAW: u32 = 1 << 3;
61pub const PAMOJA_MAVLINK_TYPEMASK_YAW_RATE: u32 = 1 << 4;
63pub const PAMOJA_MAVLINK_TYPEMASK_FORCE: u32 = 1 << 5;
65
66unsafe fn emit(frame: Frame, out_frame: *mut *mut PamojaMavlinkFrame) -> PamojaStatus {
68 if out_frame.is_null() {
69 set_last_error("out_frame must not be null".to_owned());
70 return PamojaStatus::InvalidArgument;
71 }
72 *out_frame = PamojaMavlinkFrame::into_handle(frame);
73 PamojaStatus::Ok
74}
75
76pub struct PamojaMavlinkMissionReceiver {
78 inner: MissionReceiver,
79}
80
81#[no_mangle]
93pub extern "C" fn pamoja_mavlink_mission_receiver_new(
94 target_system: u8,
95 target_component: u8,
96 mission_type: u8,
97) -> *mut PamojaMavlinkMissionReceiver {
98 Box::into_raw(Box::new(PamojaMavlinkMissionReceiver {
99 inner: MissionReceiver::new(target_system, target_component, mission_type),
100 }))
101}
102
103#[no_mangle]
125pub unsafe extern "C" fn pamoja_mavlink_mission_receiver_request_list(
126 receiver: *const PamojaMavlinkMissionReceiver,
127 header: PamojaMavlinkHeader,
128 out_frame: *mut *mut PamojaMavlinkFrame,
129) -> PamojaStatus {
130 let Some(receiver) = receiver.as_ref() else {
131 set_last_error("receiver must not be null".to_owned());
132 return PamojaStatus::InvalidArgument;
133 };
134 match receiver.inner.request_list_frame(Header::from(header)) {
135 Ok(frame) => emit(frame, out_frame),
136 Err(error) => status_of(error),
137 }
138}
139
140#[no_mangle]
171pub unsafe extern "C" fn pamoja_mavlink_mission_receiver_on_frame(
172 receiver: *mut PamojaMavlinkMissionReceiver,
173 frame: *const PamojaMavlinkFrame,
174 header: PamojaMavlinkHeader,
175 out_kind: *mut u32,
176 out_accepted: *mut *mut PamojaMavlinkMessage,
177 out_reply: *mut *mut PamojaMavlinkFrame,
178) -> PamojaStatus {
179 let Some(receiver) = receiver.as_mut() else {
180 set_last_error("receiver must not be null".to_owned());
181 return PamojaStatus::InvalidArgument;
182 };
183 let Some(frame) = frame.as_ref() else {
184 set_last_error("frame must not be null".to_owned());
185 return PamojaStatus::InvalidArgument;
186 };
187 if out_kind.is_null() || out_accepted.is_null() || out_reply.is_null() {
188 set_last_error("the output pointers must not be null".to_owned());
189 return PamojaStatus::InvalidArgument;
190 }
191 *out_kind = PAMOJA_MAVLINK_STEP_IGNORED;
192 *out_accepted = std::ptr::null_mut();
193 *out_reply = std::ptr::null_mut();
194
195 let step = match receiver.inner.on_frame(frame.frame(), Header::from(header)) {
196 Ok(Some(step)) => step,
197 Ok(None) => return PamojaStatus::Ok,
198 Err(error) => return status_of(error),
199 };
200 let ReceiverStep {
201 accepted,
202 action,
203 reply,
204 } = step;
205 *out_kind = match action {
206 ReceiverAction::Request(_) => PAMOJA_MAVLINK_RECEIVER_REQUEST,
207 ReceiverAction::Ack(_) => PAMOJA_MAVLINK_RECEIVER_ACK,
208 };
209 if let Some(item) = accepted {
210 let mut payload = [0u8; pamoja_mavlink::MAX_PAYLOAD];
211 let len = item.encode(&mut payload);
212 *out_accepted =
213 PamojaMavlinkMessage::from_typed(MissionItemInt::DESCRIPTOR, payload[..len].to_vec());
214 }
215 *out_reply = PamojaMavlinkFrame::into_handle(reply);
216 PamojaStatus::Ok
217}
218
219#[no_mangle]
234pub unsafe extern "C" fn pamoja_mavlink_mission_receiver_is_complete(
235 receiver: *const PamojaMavlinkMissionReceiver,
236) -> u8 {
237 receiver
238 .as_ref()
239 .map_or(0, |receiver| u8::from(receiver.inner.is_complete()))
240}
241
242#[no_mangle]
256pub unsafe extern "C" fn pamoja_mavlink_mission_receiver_expected(
257 receiver: *const PamojaMavlinkMissionReceiver,
258) -> u16 {
259 receiver
260 .as_ref()
261 .map_or(0, |receiver| receiver.inner.expected())
262}
263
264#[no_mangle]
275pub unsafe extern "C" fn pamoja_mavlink_mission_receiver_free(
276 receiver: *mut PamojaMavlinkMissionReceiver,
277) {
278 if !receiver.is_null() {
279 drop(Box::from_raw(receiver));
280 }
281}
282
283pub struct PamojaMavlinkMissionSender {
285 items: Vec<MissionItemInt>,
286 target_system: u8,
287 target_component: u8,
288 mission_type: u8,
289}
290
291impl PamojaMavlinkMissionSender {
292 fn with<R>(&self, query: impl FnOnce(&MissionSender<'_>) -> R) -> R {
294 query(&MissionSender::new(
295 &self.items,
296 self.target_system,
297 self.target_component,
298 self.mission_type,
299 ))
300 }
301}
302
303#[no_mangle]
315pub extern "C" fn pamoja_mavlink_mission_sender_new(
316 target_system: u8,
317 target_component: u8,
318 mission_type: u8,
319) -> *mut PamojaMavlinkMissionSender {
320 Box::into_raw(Box::new(PamojaMavlinkMissionSender {
321 items: Vec::new(),
322 target_system,
323 target_component,
324 mission_type,
325 }))
326}
327
328#[no_mangle]
355pub unsafe extern "C" fn pamoja_mavlink_mission_sender_add_item(
356 sender: *mut PamojaMavlinkMissionSender,
357 payload: *const u8,
358 payload_len: usize,
359) -> PamojaStatus {
360 let Some(sender) = sender.as_mut() else {
361 set_last_error("sender must not be null".to_owned());
362 return PamojaStatus::InvalidArgument;
363 };
364 let payload = match read_bytes(payload, payload_len) {
365 Ok(payload) => payload,
366 Err(status) => return status,
367 };
368 match MissionItemInt::decode(&payload) {
369 Ok(item) => {
370 sender.items.push(item);
371 PamojaStatus::Ok
372 }
373 Err(error) => status_of(error),
374 }
375}
376
377#[no_mangle]
391pub unsafe extern "C" fn pamoja_mavlink_mission_sender_len(
392 sender: *const PamojaMavlinkMissionSender,
393) -> u16 {
394 sender
395 .as_ref()
396 .map_or(0, |sender| sender.with(|plan| plan.len()))
397}
398
399#[no_mangle]
421pub unsafe extern "C" fn pamoja_mavlink_mission_sender_count(
422 sender: *const PamojaMavlinkMissionSender,
423 header: PamojaMavlinkHeader,
424 out_frame: *mut *mut PamojaMavlinkFrame,
425) -> PamojaStatus {
426 let Some(sender) = sender.as_ref() else {
427 set_last_error("sender must not be null".to_owned());
428 return PamojaStatus::InvalidArgument;
429 };
430 match sender.with(|plan| plan.count_frame(Header::from(header))) {
431 Ok(frame) => emit(frame, out_frame),
432 Err(error) => status_of(error),
433 }
434}
435
436#[no_mangle]
467pub unsafe extern "C" fn pamoja_mavlink_mission_sender_on_frame(
468 sender: *const PamojaMavlinkMissionSender,
469 frame: *const PamojaMavlinkFrame,
470 header: PamojaMavlinkHeader,
471 out_kind: *mut u32,
472 out_result: *mut u8,
473 out_reply: *mut *mut PamojaMavlinkFrame,
474) -> PamojaStatus {
475 let Some(sender) = sender.as_ref() else {
476 set_last_error("sender must not be null".to_owned());
477 return PamojaStatus::InvalidArgument;
478 };
479 let Some(frame) = frame.as_ref() else {
480 set_last_error("frame must not be null".to_owned());
481 return PamojaStatus::InvalidArgument;
482 };
483 if out_kind.is_null() || out_result.is_null() || out_reply.is_null() {
484 set_last_error("the output pointers must not be null".to_owned());
485 return PamojaStatus::InvalidArgument;
486 }
487 *out_kind = PAMOJA_MAVLINK_STEP_IGNORED;
488 *out_result = 0;
489 *out_reply = std::ptr::null_mut();
490
491 match sender.with(|plan| plan.on_frame(frame.frame(), Header::from(header))) {
492 Ok(Some(SenderStep::Reply(reply))) => {
493 *out_kind = PAMOJA_MAVLINK_SENDER_REPLY;
494 *out_reply = PamojaMavlinkFrame::into_handle(reply);
495 PamojaStatus::Ok
496 }
497 Ok(Some(SenderStep::Finished(result))) => {
498 *out_kind = PAMOJA_MAVLINK_SENDER_FINISHED;
499 *out_result = result;
500 PamojaStatus::Ok
501 }
502 Ok(None) => PamojaStatus::Ok,
503 Err(error) => status_of(error),
504 }
505}
506
507#[no_mangle]
518pub unsafe extern "C" fn pamoja_mavlink_mission_sender_free(
519 sender: *mut PamojaMavlinkMissionSender,
520) {
521 if !sender.is_null() {
522 drop(Box::from_raw(sender));
523 }
524}
525
526pub struct PamojaMavlinkCommand {
528 inner: CommandProtocol,
529}
530
531#[no_mangle]
543pub extern "C" fn pamoja_mavlink_command_new(
544 command: u16,
545 max_retries: u8,
546) -> *mut PamojaMavlinkCommand {
547 Box::into_raw(Box::new(PamojaMavlinkCommand {
548 inner: CommandProtocol::new(command, max_retries),
549 }))
550}
551
552#[no_mangle]
566pub unsafe extern "C" fn pamoja_mavlink_command_id(command: *const PamojaMavlinkCommand) -> u16 {
567 command
568 .as_ref()
569 .map_or(0, |command| command.inner.command())
570}
571
572#[no_mangle]
589pub unsafe extern "C" fn pamoja_mavlink_command_confirmation(
590 command: *const PamojaMavlinkCommand,
591) -> u8 {
592 command
593 .as_ref()
594 .map_or(0, |command| command.inner.confirmation())
595}
596
597#[no_mangle]
622pub unsafe extern "C" fn pamoja_mavlink_command_on_frame(
623 command: *const PamojaMavlinkCommand,
624 frame: *const PamojaMavlinkFrame,
625 out_kind: *mut u32,
626 out_value: *mut u8,
627) -> PamojaStatus {
628 let Some(command) = command.as_ref() else {
629 set_last_error("command must not be null".to_owned());
630 return PamojaStatus::InvalidArgument;
631 };
632 let Some(frame) = frame.as_ref() else {
633 set_last_error("frame must not be null".to_owned());
634 return PamojaStatus::InvalidArgument;
635 };
636 if out_kind.is_null() || out_value.is_null() {
637 set_last_error("the output pointers must not be null".to_owned());
638 return PamojaStatus::InvalidArgument;
639 }
640 *out_kind = PAMOJA_MAVLINK_STEP_IGNORED;
641 *out_value = 0;
642 match command.inner.on_frame(frame.frame()) {
643 Ok(Some(AckOutcome::Unrelated)) => *out_kind = PAMOJA_MAVLINK_ACK_UNRELATED,
644 Ok(Some(AckOutcome::InProgress(progress))) => {
645 *out_kind = PAMOJA_MAVLINK_ACK_IN_PROGRESS;
646 *out_value = progress;
647 }
648 Ok(Some(AckOutcome::Final(result))) => {
649 *out_kind = PAMOJA_MAVLINK_ACK_FINAL;
650 *out_value = result;
651 }
652 Ok(None) => {}
653 Err(error) => return status_of(error),
654 }
655 PamojaStatus::Ok
656}
657
658#[no_mangle]
678pub unsafe extern "C" fn pamoja_mavlink_command_on_timeout(
679 command: *mut PamojaMavlinkCommand,
680 out_confirmation: *mut u8,
681) -> u8 {
682 let Some(command) = command.as_mut() else {
683 return 0;
684 };
685 if out_confirmation.is_null() {
686 return 0;
687 }
688 match command.inner.on_timeout() {
689 Some(confirmation) => {
690 *out_confirmation = confirmation;
691 1
692 }
693 None => 0,
694 }
695}
696
697#[no_mangle]
708pub unsafe extern "C" fn pamoja_mavlink_command_free(command: *mut PamojaMavlinkCommand) {
709 if !command.is_null() {
710 drop(Box::from_raw(command));
711 }
712}
713
714#[no_mangle]
727pub extern "C" fn pamoja_mavlink_offboard_type_mask(flags: u32) -> u16 {
728 let mut mask = TypeMask::ignore_all();
729 if flags & PAMOJA_MAVLINK_TYPEMASK_POSITION != 0 {
730 mask = mask.use_position();
731 }
732 if flags & PAMOJA_MAVLINK_TYPEMASK_VELOCITY != 0 {
733 mask = mask.use_velocity();
734 }
735 if flags & PAMOJA_MAVLINK_TYPEMASK_ACCELERATION != 0 {
736 mask = mask.use_acceleration();
737 }
738 if flags & PAMOJA_MAVLINK_TYPEMASK_YAW != 0 {
739 mask = mask.use_yaw();
740 }
741 if flags & PAMOJA_MAVLINK_TYPEMASK_YAW_RATE != 0 {
742 mask = mask.use_yaw_rate();
743 }
744 if flags & PAMOJA_MAVLINK_TYPEMASK_FORCE != 0 {
745 mask = mask.force();
746 }
747 mask.bits()
748}
749
750#[no_mangle]
775#[allow(clippy::too_many_arguments)]
776pub unsafe extern "C" fn pamoja_mavlink_offboard_local_position(
777 header: PamojaMavlinkHeader,
778 time_boot_ms: u32,
779 coordinate_frame: u8,
780 target_system: u8,
781 target_component: u8,
782 x: f32,
783 y: f32,
784 z: f32,
785 out_frame: *mut *mut PamojaMavlinkFrame,
786) -> PamojaStatus {
787 let setpoint = SetPositionTargetLocalNed::position(
788 time_boot_ms,
789 coordinate_frame,
790 target_system,
791 target_component,
792 x,
793 y,
794 z,
795 );
796 match dialect::encode_message(Header::from(header), &setpoint) {
797 Ok(frame) => emit(frame, out_frame),
798 Err(error) => status_of(error),
799 }
800}
801
802#[no_mangle]
827#[allow(clippy::too_many_arguments)]
828pub unsafe extern "C" fn pamoja_mavlink_offboard_local_velocity(
829 header: PamojaMavlinkHeader,
830 time_boot_ms: u32,
831 coordinate_frame: u8,
832 target_system: u8,
833 target_component: u8,
834 vx: f32,
835 vy: f32,
836 vz: f32,
837 out_frame: *mut *mut PamojaMavlinkFrame,
838) -> PamojaStatus {
839 let setpoint = SetPositionTargetLocalNed::velocity(
840 time_boot_ms,
841 coordinate_frame,
842 target_system,
843 target_component,
844 vx,
845 vy,
846 vz,
847 );
848 match dialect::encode_message(Header::from(header), &setpoint) {
849 Ok(frame) => emit(frame, out_frame),
850 Err(error) => status_of(error),
851 }
852}
853
854#[no_mangle]
880#[allow(clippy::too_many_arguments)]
881pub unsafe extern "C" fn pamoja_mavlink_offboard_global_position(
882 header: PamojaMavlinkHeader,
883 time_boot_ms: u32,
884 coordinate_frame: u8,
885 target_system: u8,
886 target_component: u8,
887 lat_int: i32,
888 lon_int: i32,
889 alt: f32,
890 out_frame: *mut *mut PamojaMavlinkFrame,
891) -> PamojaStatus {
892 let setpoint = SetPositionTargetGlobalInt::position(
893 time_boot_ms,
894 coordinate_frame,
895 target_system,
896 target_component,
897 lat_int,
898 lon_int,
899 alt,
900 );
901 match dialect::encode_message(Header::from(header), &setpoint) {
902 Ok(frame) => emit(frame, out_frame),
903 Err(error) => status_of(error),
904 }
905}
906
907#[cfg(test)]
908mod tests {
909 use super::*;
910 use crate::mavlink::{pamoja_mavlink_frame_free, pamoja_mavlink_frame_message_id};
911 use crate::mavlink_schema::pamoja_mavlink_message_free;
912 use pamoja_mavlink::dialect::{mav_cmd, mav_mission_result, mav_result, CommandAck};
913
914 const VEHICLE: PamojaMavlinkHeader = PamojaMavlinkHeader {
915 system_id: 1,
916 component_id: 1,
917 sequence: 0,
918 };
919 const STATION: PamojaMavlinkHeader = PamojaMavlinkHeader {
920 system_id: 255,
921 component_id: 190,
922 sequence: 0,
923 };
924
925 fn item_payload(command: u16, z: f32) -> Vec<u8> {
926 let item = MissionItemInt {
927 command,
928 z,
929 ..MissionItemInt::zeroed()
930 };
931 let mut payload = [0u8; pamoja_mavlink::MAX_PAYLOAD];
932 let len = item.encode(&mut payload);
933 payload[..len].to_vec()
934 }
935
936 #[test]
937 fn a_whole_upload_runs_across_the_boundary() {
938 unsafe {
939 let sender = pamoja_mavlink_mission_sender_new(1, 1, 0);
940 for (command, z) in [(mav_cmd::NAV_TAKEOFF, 20.0), (mav_cmd::NAV_WAYPOINT, 50.0)] {
941 let payload = item_payload(command, z);
942 assert_eq!(
943 pamoja_mavlink_mission_sender_add_item(sender, payload.as_ptr(), payload.len()),
944 PamojaStatus::Ok
945 );
946 }
947 assert_eq!(pamoja_mavlink_mission_sender_len(sender), 2);
948
949 let receiver = pamoja_mavlink_mission_receiver_new(255, 190, 0);
950 let mut opened = std::ptr::null_mut();
951 assert_eq!(
952 pamoja_mavlink_mission_receiver_request_list(receiver, STATION, &mut opened),
953 PamojaStatus::Ok
954 );
955
956 let mut kind = 0;
957 let mut result = 0;
958 let mut from_vehicle = std::ptr::null_mut();
959 assert_eq!(
960 pamoja_mavlink_mission_sender_on_frame(
961 sender,
962 opened,
963 VEHICLE,
964 &mut kind,
965 &mut result,
966 &mut from_vehicle
967 ),
968 PamojaStatus::Ok
969 );
970 pamoja_mavlink_frame_free(opened);
971 assert_eq!(kind, PAMOJA_MAVLINK_SENDER_REPLY);
972 assert_eq!(pamoja_mavlink_frame_message_id(from_vehicle), 44);
973
974 let mut accepted_items = 0;
975 loop {
976 let mut accepted = std::ptr::null_mut();
977 let mut reply = std::ptr::null_mut();
978 assert_eq!(
979 pamoja_mavlink_mission_receiver_on_frame(
980 receiver,
981 from_vehicle,
982 STATION,
983 &mut kind,
984 &mut accepted,
985 &mut reply
986 ),
987 PamojaStatus::Ok
988 );
989 pamoja_mavlink_frame_free(from_vehicle);
990 assert_ne!(kind, PAMOJA_MAVLINK_STEP_IGNORED);
991 if !accepted.is_null() {
992 accepted_items += 1;
993 pamoja_mavlink_message_free(accepted);
994 }
995
996 let mut next = std::ptr::null_mut();
997 assert_eq!(
998 pamoja_mavlink_mission_sender_on_frame(
999 sender,
1000 reply,
1001 VEHICLE,
1002 &mut kind,
1003 &mut result,
1004 &mut next
1005 ),
1006 PamojaStatus::Ok
1007 );
1008 pamoja_mavlink_frame_free(reply);
1009 if kind == PAMOJA_MAVLINK_SENDER_FINISHED {
1010 assert_eq!(result, mav_mission_result::ACCEPTED);
1011 break;
1012 }
1013 assert_eq!(kind, PAMOJA_MAVLINK_SENDER_REPLY);
1014 from_vehicle = next;
1015 }
1016 assert_eq!(accepted_items, 2);
1017 assert_eq!(pamoja_mavlink_mission_receiver_is_complete(receiver), 1);
1018
1019 pamoja_mavlink_mission_receiver_free(receiver);
1020 pamoja_mavlink_mission_sender_free(sender);
1021 }
1022 }
1023
1024 #[test]
1025 fn a_command_is_matched_to_its_acknowledgement_and_retried() {
1026 unsafe {
1027 let arm = pamoja_mavlink_command_new(mav_cmd::COMPONENT_ARM_DISARM, 2);
1028 assert_eq!(
1029 pamoja_mavlink_command_id(arm),
1030 mav_cmd::COMPONENT_ARM_DISARM
1031 );
1032 assert_eq!(pamoja_mavlink_command_confirmation(arm), 0);
1033
1034 let ack = CommandAck {
1035 command: mav_cmd::COMPONENT_ARM_DISARM,
1036 result: mav_result::ACCEPTED,
1037 ..CommandAck::zeroed()
1038 };
1039 let frame = PamojaMavlinkFrame::into_handle(
1040 dialect::encode_message(Header::from(VEHICLE), &ack).unwrap(),
1041 );
1042 let mut kind = 0;
1043 let mut value = 0;
1044 assert_eq!(
1045 pamoja_mavlink_command_on_frame(arm, frame, &mut kind, &mut value),
1046 PamojaStatus::Ok
1047 );
1048 assert_eq!(kind, PAMOJA_MAVLINK_ACK_FINAL);
1049 assert_eq!(value, mav_result::ACCEPTED);
1050 pamoja_mavlink_frame_free(frame);
1051
1052 let mut confirmation = 0;
1054 assert_eq!(pamoja_mavlink_command_on_timeout(arm, &mut confirmation), 1);
1055 assert_eq!(confirmation, 1);
1056 assert_eq!(pamoja_mavlink_command_on_timeout(arm, &mut confirmation), 1);
1057 assert_eq!(confirmation, 2);
1058 assert_eq!(pamoja_mavlink_command_on_timeout(arm, &mut confirmation), 0);
1059 pamoja_mavlink_command_free(arm);
1060 }
1061 }
1062
1063 #[test]
1064 fn a_setpoint_goes_out_as_the_right_message() {
1065 unsafe {
1066 let mut frame = std::ptr::null_mut();
1067 assert_eq!(
1068 pamoja_mavlink_offboard_local_velocity(
1069 STATION, 1_000, 1, 1, 1, 0.5, 0.0, 0.0, &mut frame
1070 ),
1071 PamojaStatus::Ok
1072 );
1073 assert_eq!(pamoja_mavlink_frame_message_id(frame), 84);
1074 pamoja_mavlink_frame_free(frame);
1075
1076 assert_eq!(
1077 pamoja_mavlink_offboard_global_position(
1078 STATION,
1079 1_000,
1080 6,
1081 1,
1082 1,
1083 -338_567_800,
1084 1_512_153_000,
1085 50.0,
1086 &mut frame
1087 ),
1088 PamojaStatus::Ok
1089 );
1090 assert_eq!(pamoja_mavlink_frame_message_id(frame), 86);
1091 pamoja_mavlink_frame_free(frame);
1092
1093 let mask = pamoja_mavlink_offboard_type_mask(
1094 PAMOJA_MAVLINK_TYPEMASK_VELOCITY | PAMOJA_MAVLINK_TYPEMASK_YAW_RATE,
1095 );
1096 assert_eq!(
1097 mask,
1098 TypeMask::ignore_all().use_velocity().use_yaw_rate().bits()
1099 );
1100 }
1101 }
1102}