#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Direction {
Forward,
Backward,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Drive {
Wave,
FullStep,
HalfStep,
}
const WAVE: [u8; 4] = [0b1000, 0b0100, 0b0010, 0b0001];
const FULL_STEP: [u8; 4] = [0b1100, 0b0110, 0b0011, 0b1001];
const HALF_STEP: [u8; 8] = [
0b1000, 0b1100, 0b0100, 0b0110, 0b0010, 0b0011, 0b0001, 0b1001,
];
impl Drive {
pub fn pattern(self) -> &'static [u8] {
match self {
Drive::Wave => &WAVE,
Drive::FullStep => &FULL_STEP,
Drive::HalfStep => &HALF_STEP,
}
}
pub fn step_count(self) -> usize {
self.pattern().len()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Sequencer {
drive: Drive,
index: usize,
}
impl Sequencer {
pub fn new(drive: Drive) -> Sequencer {
Sequencer { drive, index: 0 }
}
pub fn coils(&self) -> u8 {
self.drive.pattern()[self.index]
}
pub fn step(&mut self, direction: Direction) -> u8 {
let count = self.drive.step_count();
self.index = match direction {
Direction::Forward => (self.index + 1) % count,
Direction::Backward => (self.index + count - 1) % count,
};
self.coils()
}
pub fn drive(&self) -> Drive {
self.drive
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Position {
steps: i32,
}
impl Position {
pub fn new() -> Position {
Position { steps: 0 }
}
pub fn step(&mut self, direction: Direction) -> i32 {
self.steps += match direction {
Direction::Forward => 1,
Direction::Backward => -1,
};
self.steps
}
pub fn steps(self) -> i32 {
self.steps
}
}
pub fn steps_for_degrees(degrees: f32, steps_per_revolution: u32) -> i32 {
let scaled = degrees / 360.0 * steps_per_revolution as f32;
let half = if scaled >= 0.0 { 0.5 } else { -0.5 };
(scaled + half) as i32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn drive_patterns_match_the_standard_sequences() {
assert_eq!(Drive::Wave.pattern(), &[0b1000, 0b0100, 0b0010, 0b0001]);
assert_eq!(Drive::FullStep.pattern(), &[0b1100, 0b0110, 0b0011, 0b1001]);
assert_eq!(
Drive::HalfStep.pattern(),
&[0b1000, 0b1100, 0b0100, 0b0110, 0b0010, 0b0011, 0b0001, 0b1001]
);
}
#[test]
fn half_step_interleaves_wave_and_full_step() {
let half = Drive::HalfStep.pattern();
for i in 0..4 {
assert_eq!(half[2 * i], Drive::Wave.pattern()[i]);
assert_eq!(half[2 * i + 1], Drive::FullStep.pattern()[i]);
}
}
#[test]
fn a_full_cycle_returns_to_the_start() {
for drive in [Drive::Wave, Drive::FullStep, Drive::HalfStep] {
let mut sequencer = Sequencer::new(drive);
let start = sequencer.coils();
for _ in 0..drive.step_count() {
sequencer.step(Direction::Forward);
}
assert_eq!(sequencer.coils(), start);
}
}
#[test]
fn stepping_back_undoes_a_step_forward() {
let mut sequencer = Sequencer::new(Drive::FullStep);
let start = sequencer.coils();
sequencer.step(Direction::Forward);
assert_eq!(sequencer.step(Direction::Backward), start);
}
#[test]
fn backward_from_the_start_wraps_to_the_last_pattern() {
let mut sequencer = Sequencer::new(Drive::Wave);
assert_eq!(sequencer.step(Direction::Backward), 0b0001);
}
#[test]
fn position_counts_signed_steps() {
let mut position = Position::new();
assert_eq!(position.step(Direction::Forward), 1);
assert_eq!(position.step(Direction::Forward), 2);
assert_eq!(position.step(Direction::Backward), 1);
assert_eq!(position.steps(), 1);
}
#[test]
fn degrees_convert_to_steps() {
assert_eq!(steps_for_degrees(360.0, 200), 200);
assert_eq!(steps_for_degrees(90.0, 200), 50);
assert_eq!(steps_for_degrees(-90.0, 200), -50);
assert_eq!(steps_for_degrees(1.8, 200), 1);
}
}