use std::fmt;
use std::str::FromStr;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::error::Error;
use crate::measurements::{Angle, Speed, SpeedUnit};
#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[repr(C)]
pub struct Wind {
pub direction: Angle,
pub speed: Speed,
}
impl Wind {
pub fn headwind(&self, direction: &Angle) -> Speed {
let relative_wind = self.direction - *direction;
self.speed * relative_wind.to_si().cos()
}
pub fn crosswind(&self, direction: &Angle) -> Speed {
let relative_wind = self.direction - *direction;
self.speed * relative_wind.to_si().sin()
}
}
impl FromStr for Wind {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let direction: Option<f32> = s.get(0..3).and_then(|s| s.parse().ok());
let speed: Option<f32> = s.get(3..5).and_then(|s| s.parse().ok());
let unit: &str = s.get(5..s.len()).unwrap_or_default();
match (direction, speed, unit) {
(Some(direction), Some(speed), "KT") => Ok(Wind {
direction: Angle::t(direction),
speed: Speed::kt(speed),
}),
(Some(direction), Some(speed), "MPS") => Ok(Wind {
direction: Angle::t(direction),
speed: Speed::mps(speed),
}),
_ => Err(Error::UnexpectedString),
}
}
}
impl fmt::Display for Wind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{0}/{1}",
self.direction,
self.speed.convert_to(SpeedUnit::Knots),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_str() {
assert_eq!(
"33008KT".parse::<Wind>(),
Ok(Wind {
direction: Angle::t(330.0),
speed: Speed::kt(8.0),
}),
);
assert_eq!(
"33004MPS".parse::<Wind>(),
Ok(Wind {
direction: Angle::t(330.0),
speed: Speed::mps(4.0),
}),
);
assert_eq!("330".parse::<Wind>(), Err(Error::UnexpectedString));
}
#[test]
fn full_headwind() {
let wind = Wind {
direction: Angle::t(0.0),
speed: Speed::kt(10.0),
};
assert_eq!(wind.headwind(&Angle::t(0.0)), Speed::kt(10.0));
}
#[test]
fn full_tailwind() {
let wind = Wind {
direction: Angle::t(0.0),
speed: Speed::kt(10.0),
};
assert_eq!(wind.headwind(&Angle::t(180.0)), Speed::kt(-10.0));
}
#[test]
fn full_crosswind_left() {
let wind = Wind {
direction: Angle::t(0.0),
speed: Speed::kt(10.0),
};
assert_eq!(wind.crosswind(&Angle::t(90.0)), Speed::kt(-10.0));
}
#[test]
fn full_crosswind_right() {
let wind = Wind {
direction: Angle::t(0.0),
speed: Speed::kt(10.0),
};
assert_eq!(wind.crosswind(&Angle::t(270.0)), Speed::kt(10.0));
}
}