#![cfg(feature = "autopilot")]
use embassy_sync::{
blocking_mutex::raw::CriticalSectionRawMutex,
watch::{Receiver, Sender, Watch},
};
use crate::{
autopilot::pilot::Autopilot,
flight::RxMessage,
tasks::{gyro_pid_task::GyroPidReceiver, rx_task::RxReceiver},
};
#[cfg(any(feature = "barometer", feature = "gps", feature = "optical_flow", feature = "rangefinder"))]
use {crate::flight::RcControls, radio_controllers::RcMode, vqm::Vector3f32};
#[cfg(feature = "barometer")]
use crate::tasks::barometer_task::BarometerSubscriber;
#[cfg(feature = "gps")]
use crate::{gps::GpsMessage, tasks::gps_task::GpsSubscriber};
#[cfg(feature = "optical_flow")]
use crate::tasks::optical_flow_task::OpticalFlowSubscriber;
#[cfg(feature = "rangefinder")]
use crate::tasks::rangefinder_task::RangefinderSubscriber;
const AUTOPILOT_WATCH_COUNT: usize = 1;
static AUTOPILOT_WATCH: Watch<CriticalSectionRawMutex, RxMessage, AUTOPILOT_WATCH_COUNT> = Watch::new();
type AutopilotSender = Sender<'static, CriticalSectionRawMutex, RxMessage, AUTOPILOT_WATCH_COUNT>;
pub fn autopilot_sender() -> AutopilotSender {
AUTOPILOT_WATCH.sender()
}
pub type AutopilotReceiver = Receiver<'static, CriticalSectionRawMutex, RxMessage, AUTOPILOT_WATCH_COUNT>;
#[allow(clippy::expect_used)]
pub fn autopilot_receiver() -> AutopilotReceiver {
AUTOPILOT_WATCH.receiver().expect("autopilot_receiver failed")
}
#[allow(unused)]
pub struct AutopilotContext {
pub gyro_pid_receiver: GyroPidReceiver,
pub rx_receiver: RxReceiver,
pub autopilot_sender: AutopilotSender,
pub autopilot: Autopilot,
#[cfg(feature = "barometer")]
pub barometer_subscriber: BarometerSubscriber,
#[cfg(feature = "gps")]
pub gps_subscriber: GpsSubscriber,
#[allow(unused)]
#[cfg(feature = "optical_flow")]
pub optical_flow_subscriber: OpticalFlowSubscriber,
#[cfg(feature = "rangefinder")]
pub rangefinder_subscriber: RangefinderSubscriber,
}
impl AutopilotContext {
#[rustfmt::skip]
pub const fn new(
gyro_pid_receiver: GyroPidReceiver,
rx_receiver: RxReceiver,
autopilot_sender: AutopilotSender,
#[cfg(feature = "barometer")] barometer_subscriber: BarometerSubscriber,
#[cfg(feature = "gps")] gps_subscriber: GpsSubscriber,
#[cfg(feature = "optical_flow")] optical_flow_subscriber: OpticalFlowSubscriber,
#[cfg(feature = "rangefinder")] rangefinder_subscriber: RangefinderSubscriber,
) -> Self {
Self {
gyro_pid_receiver,
rx_receiver,
autopilot_sender,
autopilot: Autopilot::new(),
#[cfg(feature = "barometer")] barometer_subscriber,
#[cfg(feature = "gps")] gps_subscriber,
#[cfg(feature = "optical_flow")] optical_flow_subscriber,
#[cfg(feature = "rangefinder")] rangefinder_subscriber,
}
}
}
#[allow(unused)]
#[embassy_executor::task]
pub async fn autopilot_task(ctx: &'static mut AutopilotContext) {
let mut ticker = embassy_time::Ticker::every(embassy_time::Duration::from_millis(1));
let delta_t = 0.001;
let mut loop_count: u32 = 0;
#[allow(unused)]
let mut altitude_hold = false;
#[cfg(feature = "gps")]
let mut position_hold = false;
log::info!(" AUTOPILOT: task started");
loop {
ticker.next().await;
#[cfg(any(feature = "barometer", feature = "gps", feature = "optical_flow", feature = "rangefinder"))]
{
if let Some(gyro_pid_message) = ctx.gyro_pid_receiver.try_get() {
let vertical_acceleration = gyro_pid_message.acc.z;
let Vector3f32 { x: estimated_vertical_speed, y: estimated_altitude, z: _estimated_bias } =
ctx.autopilot.altitude_kalman_filter.predict(vertical_acceleration, delta_t);
if let Some(rx_message) = ctx.rx_receiver.try_changed() {
let rc_modes = rx_message.rc_modes;
altitude_hold = rc_modes.test(RcMode::ALTITUDE_HOLD);
#[cfg(feature = "gps")]
{
position_hold = rc_modes.test(RcMode::POSITION_HOLD)
| rc_modes.test(RcMode::GPS_RESCUE)
| rc_modes.test(RcMode::AUTOPILOT);
}
}
if altitude_hold {
let throttle_stick = ctx.autopilot.altitude_controller.update(
estimated_altitude,
estimated_vertical_speed,
gyro_pid_message.orientation,
delta_t,
);
let rx_message = RxMessage {
controls: RcControls { throttle_stick, ..Default::default() },
..Default::default()
};
ctx.autopilot_sender.send(rx_message);
}
}
}
#[cfg(feature = "barometer")]
if let Some(wait_result) = ctx.barometer_subscriber.try_next_message()
&& let embassy_sync::pubsub::WaitResult::Message(barometer_message) = wait_result
{
ctx.autopilot.altitude_kalman_filter.correct_altitude_using_barometer(barometer_message.altitude_m);
#[cfg(feature = "gps")]
ctx.autopilot.position_kalman_filter.correct_altitude_using_barometer(barometer_message.altitude_m);
}
#[cfg(feature = "rangefinder")]
if let Some(wait_result) = ctx.rangefinder_subscriber.try_next_message()
&& let embassy_sync::pubsub::WaitResult::Message(rangefinder_message) = wait_result
{
let rangefinder_base_altitude_m = 0.0_f32;
let altitude = rangefinder_base_altitude_m + rangefinder_message.distance_m;
ctx.autopilot.altitude_kalman_filter.correct_altitude_using_rangefinder(altitude);
#[cfg(feature = "gps")]
ctx.autopilot.position_kalman_filter.correct_altitude_using_rangefinder(altitude);
}
#[cfg(feature = "gps")]
if let Some(wait_result) = ctx.gps_subscriber.try_next_message()
&& let embassy_sync::pubsub::WaitResult::Message(event) = wait_result
{
if let GpsMessage::GpsPositionMeters(gps_position) = event {
if altitude_hold {
ctx.autopilot.altitude_kalman_filter.correct_altitude_using_gps(gps_position.position.z);
} else if position_hold {
ctx.autopilot.position_kalman_filter.correct_position_using_gps(gps_position.position);
}
} else {
}
}
if loop_count.is_multiple_of(200) {
log::info!(" AUTOPILOT:loop {loop_count}");
}
loop_count = loop_count.wrapping_add(1); }
}