use super::en13201::En13201Class;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum DesignSpeed {
VeryHigh,
High,
Moderate,
Low,
}
impl DesignSpeed {
fn vws_weight_c(self) -> i32 {
match self {
Self::VeryHigh => 2,
Self::High => 1,
Self::Moderate => 0,
Self::Low => -1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TrafficVolume {
VeryHigh,
High,
Moderate,
Low,
VeryLow,
}
impl TrafficVolume {
fn vws_weight_c(self) -> i32 {
match self {
Self::VeryHigh => 1,
Self::High => 1,
Self::Moderate => 0,
Self::Low => 0,
Self::VeryLow => -1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum TrafficComposition {
Mixed,
MixedMotorDominant,
MotorOnly,
}
impl TrafficComposition {
fn vws_weight_c(self) -> i32 {
match self {
Self::Mixed => 2,
Self::MixedMotorDominant => 1,
Self::MotorOnly => 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Separation {
Separated,
NotSeparated,
}
impl Separation {
fn vws_weight_c(self) -> i32 {
match self {
Self::Separated => 0,
Self::NotSeparated => 1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum JunctionDensity {
High,
Moderate,
}
impl JunctionDensity {
fn vws_weight_c(self) -> i32 {
match self {
Self::High => 1,
Self::Moderate => 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum ParkedVehicles {
Present,
NotPresent,
}
impl ParkedVehicles {
fn vws_weight_c(self) -> i32 {
match self {
Self::Present => 1,
Self::NotPresent => 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum AmbientLuminance {
High,
Moderate,
Low,
}
impl AmbientLuminance {
fn vws_weight_c(self) -> i32 {
match self {
Self::High => 1,
Self::Moderate => 0,
Self::Low => -1,
}
}
fn vws_weight_p(self) -> i32 {
match self {
Self::High => 1,
Self::Moderate => 0,
Self::Low => -1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum NavigationalTask {
VeryDifficult,
Difficult,
Easy,
}
impl NavigationalTask {
fn vws_weight_c(self) -> i32 {
match self {
Self::VeryDifficult => 2,
Self::Difficult => 1,
Self::Easy => 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct CParamsEn13201 {
pub design_speed: DesignSpeed,
pub traffic_volume: TrafficVolume,
pub traffic_composition: TrafficComposition,
pub separation: Separation,
pub junction_density: JunctionDensity,
pub parked_vehicles: ParkedVehicles,
pub ambient_luminance: AmbientLuminance,
pub navigational_task: NavigationalTask,
}
impl Default for CParamsEn13201 {
fn default() -> Self {
Self {
design_speed: DesignSpeed::High,
traffic_volume: TrafficVolume::Moderate,
traffic_composition: TrafficComposition::MixedMotorDominant,
separation: Separation::NotSeparated,
junction_density: JunctionDensity::Moderate,
parked_vehicles: ParkedVehicles::NotPresent,
ambient_luminance: AmbientLuminance::Moderate,
navigational_task: NavigationalTask::Easy,
}
}
}
impl CParamsEn13201 {
pub fn vws(&self) -> i32 {
self.design_speed.vws_weight_c()
+ self.traffic_volume.vws_weight_c()
+ self.traffic_composition.vws_weight_c()
+ self.separation.vws_weight_c()
+ self.junction_density.vws_weight_c()
+ self.parked_vehicles.vws_weight_c()
+ self.ambient_luminance.vws_weight_c()
+ self.navigational_task.vws_weight_c()
}
pub fn recommended_class(&self) -> En13201Class {
match self.vws() {
i if i <= 0 => En13201Class::C5,
1 => En13201Class::C4,
2 => En13201Class::C3,
3 => En13201Class::C2,
4 => En13201Class::C1,
_ => En13201Class::C0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum PedestrianSpeed {
WalkingOnly,
SlowMixed,
LowSpeedMotor,
}
impl PedestrianSpeed {
fn vws_weight_p(self) -> i32 {
match self {
Self::WalkingOnly => -1,
Self::SlowMixed => 0,
Self::LowSpeedMotor => 1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum UserDensity {
High,
Moderate,
Low,
}
impl UserDensity {
fn vws_weight_p(self) -> i32 {
match self {
Self::High => 1,
Self::Moderate => 0,
Self::Low => -1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum FacialRecognition {
Necessary,
Useful,
NotNeeded,
}
impl FacialRecognition {
fn vws_weight_p(self) -> i32 {
match self {
Self::Necessary => 1,
Self::Useful => 0,
Self::NotNeeded => -1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct PParamsEn13201 {
pub pedestrian_speed: PedestrianSpeed,
pub user_density: UserDensity,
pub ambient_luminance: AmbientLuminance,
pub facial_recognition: FacialRecognition,
}
impl Default for PParamsEn13201 {
fn default() -> Self {
Self {
pedestrian_speed: PedestrianSpeed::SlowMixed,
user_density: UserDensity::Moderate,
ambient_luminance: AmbientLuminance::Moderate,
facial_recognition: FacialRecognition::Useful,
}
}
}
impl PParamsEn13201 {
pub fn vws(&self) -> i32 {
self.pedestrian_speed.vws_weight_p()
+ self.user_density.vws_weight_p()
+ self.ambient_luminance.vws_weight_p()
+ self.facial_recognition.vws_weight_p()
}
pub fn recommended_class(&self) -> En13201Class {
match self.vws() {
i if i >= 2 => En13201Class::P1,
1 => En13201Class::P2,
0 => En13201Class::P3,
-1 => En13201Class::P4,
-2 => En13201Class::P5,
_ => En13201Class::P6,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn c_params_defaults_give_mid_class() {
let p = CParamsEn13201::default();
assert_eq!(p.vws(), 3);
assert_eq!(p.recommended_class(), En13201Class::C2);
}
#[test]
fn c_very_high_everything_tops_out_at_c0() {
let p = CParamsEn13201 {
design_speed: DesignSpeed::VeryHigh,
traffic_volume: TrafficVolume::VeryHigh,
traffic_composition: TrafficComposition::Mixed,
separation: Separation::NotSeparated,
junction_density: JunctionDensity::High,
parked_vehicles: ParkedVehicles::Present,
ambient_luminance: AmbientLuminance::High,
navigational_task: NavigationalTask::VeryDifficult,
};
assert!(p.vws() >= 5);
assert_eq!(p.recommended_class(), En13201Class::C0);
}
#[test]
fn c_minimum_everything_bottoms_out_at_c5() {
let p = CParamsEn13201 {
design_speed: DesignSpeed::Low,
traffic_volume: TrafficVolume::VeryLow,
traffic_composition: TrafficComposition::MotorOnly,
separation: Separation::Separated,
junction_density: JunctionDensity::Moderate,
parked_vehicles: ParkedVehicles::NotPresent,
ambient_luminance: AmbientLuminance::Low,
navigational_task: NavigationalTask::Easy,
};
assert!(p.vws() <= 0);
assert_eq!(p.recommended_class(), En13201Class::C5);
}
#[test]
fn p_defaults_produce_mid_class() {
let p = PParamsEn13201::default();
assert_eq!(p.vws(), 0);
assert_eq!(p.recommended_class(), En13201Class::P3);
}
#[test]
fn p_high_everything_tops_out_at_p1() {
let p = PParamsEn13201 {
pedestrian_speed: PedestrianSpeed::LowSpeedMotor,
user_density: UserDensity::High,
ambient_luminance: AmbientLuminance::High,
facial_recognition: FacialRecognition::Necessary,
};
assert!(p.vws() >= 2);
assert_eq!(p.recommended_class(), En13201Class::P1);
}
#[test]
fn p_minimum_everything_bottoms_at_p6() {
let p = PParamsEn13201 {
pedestrian_speed: PedestrianSpeed::WalkingOnly,
user_density: UserDensity::Low,
ambient_luminance: AmbientLuminance::Low,
facial_recognition: FacialRecognition::NotNeeded,
};
assert!(p.vws() <= -3);
assert_eq!(p.recommended_class(), En13201Class::P6);
}
#[test]
fn every_c_class_is_reachable() {
use En13201Class::*;
let base = CParamsEn13201 {
design_speed: DesignSpeed::Moderate,
traffic_volume: TrafficVolume::Moderate,
traffic_composition: TrafficComposition::MotorOnly,
separation: Separation::Separated,
junction_density: JunctionDensity::Moderate,
parked_vehicles: ParkedVehicles::NotPresent,
ambient_luminance: AmbientLuminance::Moderate,
navigational_task: NavigationalTask::Easy,
};
assert_eq!(base.recommended_class(), C5); let mut p = base;
p.traffic_volume = TrafficVolume::High;
assert_eq!(p.recommended_class(), C4); p.ambient_luminance = AmbientLuminance::High;
assert_eq!(p.recommended_class(), C3); p.navigational_task = NavigationalTask::Difficult;
assert_eq!(p.recommended_class(), C2); p.traffic_composition = TrafficComposition::MixedMotorDominant;
assert_eq!(p.recommended_class(), C1); p.traffic_composition = TrafficComposition::Mixed;
assert_eq!(p.recommended_class(), C0); }
}