use crazyflie_link::Packet;
use flume::Sender;
use crate::{Error, Result};
use crate::crazyflie::COMMANDER_PORT;
use crate::crazyflie::GENERIC_SETPOINT_PORT;
const RPYT_CHANNEL: u8 = 0;
const GENERIC_SETPOINT_CHANNEL: u8 = 0;
const GENERIC_CMD_CHANNEL: u8 = 1;
const TYPE_FULL_STATE: u8 = 6;
const TYPE_POSITION: u8 = 7;
const TYPE_VELOCITY_WORLD: u8 = 8;
const TYPE_ZDISTANCE: u8 = 9;
const TYPE_HOVER: u8 = 10;
const TYPE_MANUAL: u8 = 11;
const TYPE_STOP: u8 = 0;
const TYPE_META_COMMAND_NOTIFY_SETPOINT_STOP: u8 = 0;
#[derive(Debug)]
pub struct Commander {
uplink: Sender<Packet>,
}
impl Commander {
pub(crate) fn new(uplink: Sender<Packet>) -> Self {
Self { uplink }
}
}
impl Commander {
pub async fn setpoint_rpyt(&self, roll: f32, pitch: f32, yawrate: f32, thrust: u16) -> Result<()> {
let mut payload = Vec::new();
payload.append(&mut roll.to_le_bytes().to_vec());
payload.append(&mut (-pitch).to_le_bytes().to_vec());
payload.append(&mut yawrate.to_le_bytes().to_vec());
payload.append(&mut thrust.to_le_bytes().to_vec());
let pk = Packet::new(COMMANDER_PORT, RPYT_CHANNEL, payload);
self.uplink
.send_async(pk)
.await
.map_err(|_| Error::Disconnected)?;
Ok(())
}
}
impl Commander {
pub async fn setpoint_full_state(
&self,
position: [f32; 3],
velocity: [f32; 3],
acceleration: [f32; 3],
orientation: [f32; 4],
angular_velocity: [f32; 3],
) -> Result<()> {
let payload = encode_full_state_setpoint(
position,
velocity,
acceleration,
orientation,
angular_velocity,
)?;
let pk = Packet::new(GENERIC_SETPOINT_PORT, GENERIC_SETPOINT_CHANNEL, payload);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn setpoint_position(&self, x: f32, y: f32, z: f32, yaw: f32) -> Result<()> {
let mut payload = Vec::with_capacity(1 + 4 * 4);
payload.push(TYPE_POSITION);
payload.extend_from_slice(&x.to_le_bytes());
payload.extend_from_slice(&y.to_le_bytes());
payload.extend_from_slice(&z.to_le_bytes());
payload.extend_from_slice(&yaw.to_le_bytes());
let pk = Packet::new(GENERIC_SETPOINT_PORT, GENERIC_SETPOINT_CHANNEL, payload);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn setpoint_velocity_world(&self, vx: f32, vy: f32, vz: f32, yawrate: f32) -> Result<()> {
let mut payload = Vec::with_capacity(1 + 4 * 4);
payload.push(TYPE_VELOCITY_WORLD);
payload.extend_from_slice(&vx.to_le_bytes());
payload.extend_from_slice(&vy.to_le_bytes());
payload.extend_from_slice(&vz.to_le_bytes());
payload.extend_from_slice(&yawrate.to_le_bytes());
let pk = Packet::new(GENERIC_SETPOINT_PORT, GENERIC_SETPOINT_CHANNEL, payload);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn setpoint_zdistance(&self, roll: f32, pitch: f32, yawrate: f32, zdistance: f32) -> Result<()> {
let mut payload = Vec::with_capacity(1 + 4 * 4);
payload.push(TYPE_ZDISTANCE);
payload.extend_from_slice(&roll.to_le_bytes());
payload.extend_from_slice(&pitch.to_le_bytes());
payload.extend_from_slice(&yawrate.to_le_bytes());
payload.extend_from_slice(&zdistance.to_le_bytes());
let pk = Packet::new(GENERIC_SETPOINT_PORT, GENERIC_SETPOINT_CHANNEL, payload);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn setpoint_hover(&self, vx: f32, vy: f32, yawrate: f32, zdistance: f32) -> Result<()> {
let mut payload = Vec::with_capacity(1 + 4 * 4);
payload.push(TYPE_HOVER);
payload.extend_from_slice(&vx.to_le_bytes());
payload.extend_from_slice(&vy.to_le_bytes());
payload.extend_from_slice(&yawrate.to_le_bytes());
payload.extend_from_slice(&zdistance.to_le_bytes());
let pk = Packet::new(GENERIC_SETPOINT_PORT, GENERIC_SETPOINT_CHANNEL, payload);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn setpoint_manual(&self, roll: f32, pitch: f32, yawrate: f32, thrust_percentage: f32, rate: bool) -> Result<()> {
let thrust = 10001.0 + 0.01 * thrust_percentage * (60000.0 - 10001.0);
let thrust_16 = thrust as u16;
let mut payload = Vec::with_capacity(1 + 4 * 3 + 2 + 1);
payload.push(TYPE_MANUAL);
payload.extend_from_slice(&roll.to_le_bytes());
payload.extend_from_slice(&pitch.to_le_bytes());
payload.extend_from_slice(&yawrate.to_le_bytes());
payload.extend_from_slice(&thrust_16.to_le_bytes());
payload.push(rate as u8);
let pk = Packet::new(GENERIC_SETPOINT_PORT, GENERIC_SETPOINT_CHANNEL, payload);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn setpoint_stop(&self) -> Result<()> {
let payload = vec![TYPE_STOP];
let pk = Packet::new(GENERIC_SETPOINT_PORT, GENERIC_SETPOINT_CHANNEL, payload);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn notify_setpoint_stop(&self, remain_valid_milliseconds: u32) -> Result<()> {
let mut payload = Vec::with_capacity(1 + 4);
payload.push(TYPE_META_COMMAND_NOTIFY_SETPOINT_STOP);
payload.extend_from_slice(&remain_valid_milliseconds.to_le_bytes());
let pk = Packet::new(GENERIC_SETPOINT_PORT, GENERIC_CMD_CHANNEL, payload);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
Ok(())
}
}
fn encode_full_state_setpoint(
position: [f32; 3],
velocity: [f32; 3],
acceleration: [f32; 3],
orientation: [f32; 4],
angular_velocity: [f32; 3],
) -> Result<Vec<u8>> {
let mut payload = Vec::with_capacity(29);
payload.push(TYPE_FULL_STATE);
for value in position.into_iter().chain(velocity).chain(acceleration) {
payload.extend_from_slice(&scale_to_i16(value)?.to_le_bytes());
}
payload.extend_from_slice(&compress_quaternion(orientation)?.to_le_bytes());
for value in angular_velocity {
payload.extend_from_slice(&scale_to_i16(value)?.to_le_bytes());
}
Ok(payload)
}
fn scale_to_i16(value: f32) -> Result<i16> {
let scaled = value * 1000.0;
if !scaled.is_finite() || scaled < i16::MIN as f32 || scaled > i16::MAX as f32 {
return Err(Error::InvalidArgument(
"Full-state values must be finite and fit in a signed 16-bit value after scaling by 1000"
.to_owned(),
));
}
Ok(scaled as i16)
}
fn compress_quaternion(quaternion: [f32; 4]) -> Result<u32> {
let norm_squared: f64 = quaternion
.iter()
.map(|component| f64::from(*component).powi(2))
.sum();
if !norm_squared.is_finite() || norm_squared == 0.0 {
return Err(Error::InvalidArgument(
"Orientation must be a finite, non-zero quaternion".to_owned(),
));
}
let norm = norm_squared.sqrt();
let normalized = quaternion.map(|component| f64::from(component) / norm);
let mut largest_index = 0;
for index in 1..normalized.len() {
if normalized[index].abs() > normalized[largest_index].abs() {
largest_index = index;
}
}
let negate = normalized[largest_index] < 0.0;
let mut compressed = largest_index as u32;
for (index, component) in normalized.into_iter().enumerate() {
if index != largest_index {
let negative_bit = (component < 0.0) ^ negate;
let magnitude = (511.0 * component.abs() / std::f64::consts::FRAC_1_SQRT_2 + 0.5)
as u32;
compressed = (compressed << 10) | ((negative_bit as u32) << 9) | magnitude;
}
}
Ok(compressed)
}