use crate::core::scalar::ControlScalar;
pub type HallState = u8;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PhaseState {
High,
Low,
Off,
}
const COMMUTATION_TABLE: [[PhaseState; 3]; 8] = {
use PhaseState::*;
[
[Off, Off, Off], [High, Low, Off], [Off, High, Low], [High, Off, Low], [Low, Off, High], [Low, High, Off], [Off, Low, High], [Off, Off, Off], ]
};
const COMMUTATION_TABLE_REV: [[PhaseState; 3]; 8] = {
use PhaseState::*;
[
[Off, Off, Off], [Off, Low, High], [High, Off, Low], [Off, High, Low], [High, Low, Off], [Low, Off, High], [Low, High, Off], [Off, Off, Off], ]
};
pub struct SixStepCommutator<S: ControlScalar> {
duty: S,
forward: bool,
}
impl<S: ControlScalar> SixStepCommutator<S> {
pub fn new() -> Self {
Self {
duty: S::ZERO,
forward: true,
}
}
pub fn set_duty(&mut self, duty: S) {
self.duty = duty.clamp_val(S::ZERO, S::ONE);
}
pub fn set_direction(&mut self, forward: bool) {
self.forward = forward;
}
pub fn commutate(&self, hall: HallState) -> ([PhaseState; 3], S) {
let idx = (hall & 0x07) as usize;
let table = if self.forward {
&COMMUTATION_TABLE
} else {
&COMMUTATION_TABLE_REV
};
(table[idx], self.duty)
}
pub fn phase_duties(&self, hall: HallState) -> [S; 3] {
let (states, duty) = self.commutate(hall);
core::array::from_fn(|i| match states[i] {
PhaseState::High => duty,
PhaseState::Low => S::ZERO,
PhaseState::Off => S::ZERO,
})
}
}
impl<S: ControlScalar> Default for SixStepCommutator<S> {
fn default() -> Self {
Self::new()
}
}
pub fn hall_to_sector(hall: HallState) -> Option<u8> {
match hall & 0x07 {
1 => Some(1),
2 => Some(2),
3 => Some(3),
4 => Some(4),
5 => Some(5),
6 => Some(6),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_hall_states_produce_output() {
let mut comm = SixStepCommutator::<f64>::new();
comm.set_duty(0.8);
for hall in 1..=6_u8 {
let (states, duty) = comm.commutate(hall);
assert_eq!(duty, 0.8);
let highs = states.iter().filter(|&&s| s == PhaseState::High).count();
let lows = states.iter().filter(|&&s| s == PhaseState::Low).count();
assert_eq!(highs, 1, "hall={}: should have 1 High", hall);
assert_eq!(lows, 1, "hall={}: should have 1 Low", hall);
}
}
#[test]
fn invalid_hall_gives_all_off() {
let comm = SixStepCommutator::<f64>::new();
let (states, _) = comm.commutate(0);
assert!(states.iter().all(|&s| s == PhaseState::Off));
let (states7, _) = comm.commutate(7);
assert!(states7.iter().all(|&s| s == PhaseState::Off));
}
#[test]
fn reverse_differs_from_forward() {
let mut fwd = SixStepCommutator::<f64>::new();
fwd.set_duty(1.0);
let mut rev = SixStepCommutator::<f64>::new();
rev.set_direction(false);
rev.set_duty(1.0);
let (fwd_states, _) = fwd.commutate(1);
let (rev_states, _) = rev.commutate(1);
assert_ne!(fwd_states, rev_states);
}
#[test]
fn duty_clamped_to_zero_one() {
let mut comm = SixStepCommutator::<f64>::new();
comm.set_duty(2.0);
let (_, d) = comm.commutate(1);
assert_eq!(d, 1.0);
comm.set_duty(-1.0);
let (_, d) = comm.commutate(1);
assert_eq!(d, 0.0);
}
#[test]
fn hall_to_sector_mapping() {
for h in 1..=6_u8 {
assert!(hall_to_sector(h).is_some());
}
assert!(hall_to_sector(0).is_none());
assert!(hall_to_sector(7).is_none());
}
}