Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: define IMU & Radio serial string structs #1

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ authors = ["AeroRust <[email protected]>"]

[dependencies]

parse-display = "0.9"
113 changes: 113 additions & 0 deletions src/flight_controller/imu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//! IMU
use std::primitive::str;

use parse_display::{Display, FromStr};

pub type AccX = Acceleration<X, f32>;
pub type AccY = Acceleration<Y, f32>;
pub type AccZ = Acceleration<Z, f32>;

pub type GyroX = Gyroscope<X, f32>;
pub type GyroY = Gyroscope<Y, f32>;
pub type GyroZ = Gyroscope<Z, f32>;

#[derive(Display, FromStr, Debug, PartialEq, Eq, Clone)]
#[display("Acc{axis}:{value}")]
pub struct Acceleration<AXIS: Axis, V> {
value: V,
axis: AXIS,
}

#[derive(Display, FromStr, Debug, PartialEq, Eq, Clone)]
#[display("Gyro{axis}:{value}")]
pub struct Gyroscope<AXIS: Axis, V> {
#[from_str(default)]
value: V,
axis: AXIS,
}

pub trait Axis: core::fmt::Display {}

#[derive(Display, FromStr, Debug, PartialEq, Eq, Clone, Copy)]
#[display("X")]
pub struct X;
impl Axis for X {}

#[derive(Display, FromStr, Debug, PartialEq, Eq, Clone, Copy)]
#[display("Y")]
pub struct Y;
impl Axis for Y {}

#[derive(Display, FromStr, Debug, PartialEq, Eq, Clone, Copy)]
#[display("Z")]
pub struct Z;
impl Axis for Z {}

/// Acceleration
/// > Prints filtered accelerometer data direct from IMU (expected: ~ -2 to 2; x,y 0 when level, z 1 when level)
/// ```cpp
/// float AccX, AccY, AccZ;
/// float AccX_prev, AccY_prev, AccZ_prev;
/// ````
///
/// <https://www.arduino.cc/reference/en/language/variables/data-types/float>
#[derive(Display, FromStr, Debug, PartialEq, Clone)]
#[display("{x} {y} {z}")]
pub struct Acc {
x: AccX,
y: AccY,
z: AccZ,
}

/// > Prints filtered gyro data direct from IMU (expected: ~ -250 to 250, 0 at rest)
#[derive(Display, FromStr, Debug, PartialEq, Clone)]
#[display("{x} {y} {z}")]
pub struct Gyro {
x: GyroX,
y: GyroY,
z: GyroZ,
}

// TODO: WTF is `F()`
#[cfg(test)]
pub mod tests {
pub const GYRO_SERIAL_MSG: &str = "GyroX:4.22222 GyroY:230.0000 GyroZ:-100.123456789";
pub const ACC_SERIAL_MSG: &str = "AccX:0.1111 AccY:1.999999 AccZ:1";

use super::*;

/// <https://github.com/nickrehm/dRehmFlight/blob/423806a7058e81e410d1ec1f7284a49555859112/Versions/dRehmFlight_Teensy_BETA_1.3/dRehmFlight_Teensy_BETA_1.3.ino#L1605-L1615>
///
///
/// ```cpp
/// Serial.print(F("GyroX:"));
/// Serial.print(GyroX);
/// Serial.print(F(" GyroY:"));
/// Serial.print(GyroY);
/// Serial.print(F(" GyroZ:"));
/// Serial.println(GyroZ);
/// ```
#[test]
fn test_gyroscope_parse() {
let gyro = GYRO_SERIAL_MSG.parse::<Gyro>().expect("Should parse value");

dbg!(gyro);
}

/// <https://github.com/nickrehm/dRehmFlight/blob/423806a7058e81e410d1ec1f7284a49555859112/Versions/dRehmFlight_Teensy_BETA_1.3/dRehmFlight_Teensy_BETA_1.3.ino#L1617-1627>
///
/// ```cpp
/// Serial.print(F("AccX:"));
/// Serial.print(AccX);
/// Serial.print(F(" AccY:"));
/// Serial.print(AccY);
/// Serial.print(F(" AccZ:"));
/// Serial.println(AccZ);
/// ```
#[test]
fn test_acceleration_parse() {
let acceleration = ACC_SERIAL_MSG.parse::<Acc>().expect("Should parse value");

dbg!(acceleration);
}
}
61 changes: 61 additions & 0 deletions src/flight_controller/radio.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//! unsigned long channel_1_pwm, channel_2_pwm, channel_3_pwm, channel_4_pwm, channel_5_pwm, channel_6_pwm;
//! unsigned long channel_1_pwm_prev, channel_2_pwm_prev, channel_3_pwm_prev, channel_4_pwm_prev;

use parse_display::{FromStr, Display};

#[derive(Display, FromStr, PartialEq, Debug)]
#[display(" {ch_1} {ch_2} {ch_3} {ch_4} {ch_5} {ch_6}")]
pub struct RadioData {
ch_1: Channel1Pwm,
ch_2: Channel2Pwm,
ch_3: Channel3Pwm,
ch_4: Channel4Pwm,
ch_5: Channel5Pwm,
ch_6: Channel6Pwm,
}

#[derive(Display, FromStr, PartialEq, Debug)]
#[display("CH{channel}:{value}")]
pub struct Channel<const CHANNEL: u8, PWM> {
value: PWM,
channel: u8,
}

impl<const CHANNEL: u8, PWM> Channel<CHANNEL, PWM> {
pub fn new(pwm_value: PWM) -> Self {
Self {
value: pwm_value,
channel: CHANNEL.into(),
}
}
}

pub type ChannelPwm<const CHANNEL: u8> = Channel<CHANNEL, u32>;

pub type Channel1Pwm = Channel<1, u32>;
pub type Channel2Pwm = Channel<2, u32>;
pub type Channel3Pwm = Channel<3, u32>;
pub type Channel4Pwm = Channel<4, u32>;
pub type Channel5Pwm = Channel<5, u32>;
pub type Channel6Pwm = Channel<6, u32>;

pub type Channel1PwmPrev = Channel<1, u32>;
pub type Channel2PwmPrev = Channel<2, u32>;
pub type Channel3PwmPrev = Channel<3, u32>;
pub type Channel4PwmPrev = Channel<4, u32>;

#[cfg(test)]
pub(crate) mod tests {
use super::*;

// > Radio pwm values (expected: 1000 to 2000)
pub const RADIO_DATA_SERIAL_MSG: &str = " CH1:1000 CH2:1200 CH3:1300 CH4:1400 CH5:1500 CH6:1600";

#[test]
fn test_radio_channels_data_parse() {
let radio_data = RADIO_DATA_SERIAL_MSG.parse::<RadioData>().expect("Should parse radio channels data");

dbg!(radio_data);
}

}
57 changes: 57 additions & 0 deletions src/flight_controller/serial.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use parse_display::{Display, FromStr};

use super::{
imu::{Acc, Gyro},
radio::RadioData,
};

/// Default:
/// `Serial.begin(500000); // USB serial`
///
/// <https://github.com/nickrehm/dRehmFlight/blob/423806a7058e81e410d1ec1f7284a49555859112/Versions/dRehmFlight_Teensy_BETA_1.3/dRehmFlight_Teensy_BETA_1.3.ino#L309>
///
pub const SERIAL_BAUD_RATE: u32 = 500_000;

#[derive(Display, FromStr, PartialEq, Debug)]
pub enum SerialData {
#[display("{0}")]
ImuAcceleration(Acc),
#[display("{0}")]
ImuGyroscope(Gyro),
#[display("{0}")]
RadioData(RadioData),
}

#[cfg(test)]
pub mod test {
use core::fmt::Write;

use crate::flight_controller::{
imu::tests::{ACC_SERIAL_MSG, GYRO_SERIAL_MSG},
radio::{tests::RADIO_DATA_SERIAL_MSG, RadioData},
};

use super::*;

#[test]
fn test_all_known_serial_sentences() {
let all = vec![GYRO_SERIAL_MSG, ACC_SERIAL_MSG, RADIO_DATA_SERIAL_MSG];

let sentences = all
.iter()
.try_fold(String::new(), |mut string, item| {
writeln!(&mut string, "{item}")?;

Ok::<String, std::fmt::Error>(string)
})
.expect("Should concatenate on different lines");

let sentences_parsed = sentences
.lines()
.map(|line| line.parse::<SerialData>())
.collect::<Result<Vec<SerialData>, parse_display::ParseError>>()
.expect("Should not fail parsing sentences");

dbg!(sentences_parsed);
}
}
Loading