use std::collections::VecDeque;
use std::ops::Range;
use crate::time::VirtualNanos;
pub(super) const FIFO_BYTES: usize = 512;
pub(super) const FIFO_R_W: usize = 0x74;
pub(super) const SAMPLE_BYTES: usize = 14;
const TEMP_FIFO_EN: u8 = 0x80;
const GYRO_FIFO_EN: [u8; 3] = [0x40, 0x20, 0x10];
const ACCEL_FIFO_EN: u8 = 0x08;
const ACCEL_LSB_PER_G: i64 = 16_384;
const GYRO_LSB_NUMERATOR: i64 = 1 << 16;
const GYRO_DPS_DENOMINATOR: i64 = 500;
#[derive(Debug, Clone, Copy)]
pub(super) struct Signals {
pub(super) rate: [i32; 3],
pub(super) acceleration: [i32; 3],
pub(super) centi_celsius: i32,
}
impl Signals {
pub(super) fn still(centi_celsius: i32) -> Self {
Self {
rate: [0; 3],
acceleration: [0, 0, 1_000_000],
centi_celsius,
}
}
pub(super) fn accel_counts(&self, axis: usize, step: u32, offset: i64) -> i64 {
let counts = round(
i64::from(self.acceleration[axis]) * ACCEL_LSB_PER_G,
1_000_000 << step,
);
counts - ((offset * 16) >> step)
}
pub(super) fn gyro_counts(&self, axis: usize, step: u32) -> i64 {
round(
i64::from(self.rate[axis]) * GYRO_LSB_NUMERATOR,
(GYRO_DPS_DENOMINATOR * 1_000) << step,
)
}
}
pub(super) fn offset15(high: u8, low: u8) -> i64 {
let fifteen = (u16::from(high) << 7) | u16::from(low >> 1);
i64::from((fifteen << 1).cast_signed() >> 1)
}
pub(super) fn round(numerator: i64, denominator: i64) -> i64 {
let half = denominator / 2;
if numerator >= 0 {
(numerator + half) / denominator
} else {
(numerator - half) / denominator
}
}
pub(super) fn word(counts: i64) -> [u8; 2] {
i16::try_from(counts.clamp(i64::from(i16::MIN), i64::from(i16::MAX)))
.expect("held to sixteen bits")
.to_be_bytes()
}
pub(super) fn pack(accel: [i64; 3], temperature: i64, gyro: [i64; 3]) -> [u8; SAMPLE_BYTES] {
let mut bytes = [0; SAMPLE_BYTES];
for axis in 0..3 {
bytes[2 * axis..2 * axis + 2].copy_from_slice(&word(accel[axis]));
bytes[8 + 2 * axis..10 + 2 * axis].copy_from_slice(&word(gyro[axis]));
}
bytes[6..8].copy_from_slice(&word(temperature));
bytes
}
pub(super) fn fifo_bytes(sample: &[u8; SAMPLE_BYTES], enabled: u8) -> Vec<u8> {
let mut bytes = Vec::with_capacity(SAMPLE_BYTES);
if enabled & ACCEL_FIFO_EN != 0 {
bytes.extend_from_slice(&sample[0..6]);
}
if enabled & TEMP_FIFO_EN != 0 {
bytes.extend_from_slice(&sample[6..8]);
}
for axis in 0..3 {
if enabled & GYRO_FIFO_EN[axis] != 0 {
bytes.extend_from_slice(&sample[8 + 2 * axis..10 + 2 * axis]);
}
}
bytes
}
#[derive(Debug, Clone, Copy)]
pub(super) struct SampleClock {
epoch_at: VirtualNanos,
epoch_sample: u64,
generated: u64,
}
impl SampleClock {
pub(super) fn new() -> Self {
Self {
epoch_at: VirtualNanos(0),
epoch_sample: 0,
generated: 0,
}
}
pub(super) fn restart(&mut self, now: VirtualNanos) {
*self = Self {
epoch_at: now,
epoch_sample: 0,
generated: 0,
};
}
pub(super) fn due(&self, now: VirtualNanos, hz: u64) -> u64 {
let since = u128::from(now.0.saturating_sub(self.epoch_at.0));
let taken = since * u128::from(hz) / 1_000_000_000;
self.epoch_sample + u64::try_from(taken).expect("a run outlasts no sample count")
}
pub(super) fn rebase(&mut self, now: VirtualNanos, hz: u64) {
self.epoch_sample = self.due(now, hz);
self.epoch_at = now;
}
pub(super) fn catch_up(&mut self, now: VirtualNanos, hz: u64) -> Range<u64> {
let due = self.due(now, hz).max(self.generated);
let taken = self.generated..due;
self.generated = due;
taken
}
}
#[derive(Debug)]
pub(super) struct Fifo {
bytes: VecDeque<u8>,
latched: u16,
}
impl Fifo {
pub(super) fn new() -> Self {
Self {
bytes: VecDeque::with_capacity(FIFO_BYTES),
latched: 0,
}
}
pub(super) fn clear(&mut self) {
self.bytes.clear();
}
pub(super) fn push(&mut self, sample: &[u8], count: u64, keep_oldest: bool) -> bool {
if sample.is_empty() {
return false;
}
let room = (FIFO_BYTES - self.bytes.len()) / sample.len();
let count = usize::try_from(count).unwrap_or(usize::MAX);
if keep_oldest {
for _ in 0..count.min(room) {
self.bytes.extend(sample);
}
} else {
let whole = FIFO_BYTES / sample.len();
for _ in 0..count.min(whole) {
while self.bytes.len() + sample.len() > FIFO_BYTES {
self.bytes.drain(..sample.len());
}
self.bytes.extend(sample);
}
}
count > room
}
pub(super) fn latch(&mut self) -> u8 {
self.latched = u16::try_from(self.bytes.len()).expect("the FIFO is 512 bytes");
(self.latched >> 8) as u8
}
pub(super) fn latched_low(&self) -> u8 {
(self.latched & 0xFF) as u8
}
pub(super) fn pop(&mut self) -> Option<u8> {
self.bytes.pop_front()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum Access {
Read(usize),
Write(usize, u8),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Phase {
Address,
Read(usize),
Write(usize),
}
#[derive(Debug)]
pub(super) struct Serial {
phase: Phase,
selected: bool,
}
impl Serial {
pub(super) fn new() -> Self {
Self {
phase: Phase::Address,
selected: false,
}
}
pub(super) fn select(&mut self) {
self.selected = true;
self.phase = Phase::Address;
}
pub(super) fn deselect(&mut self) {
self.selected = false;
self.phase = Phase::Address;
}
pub(super) fn step(&mut self, byte: u8) -> Option<Access> {
if !self.selected {
return None;
}
match self.phase {
Phase::Address => {
let register = usize::from(byte & 0x7F);
self.phase = if byte & 0x80 != 0 {
Phase::Read(register)
} else {
Phase::Write(register)
};
None
}
Phase::Read(register) => {
self.phase = Phase::Read(if register == FIFO_R_W {
FIFO_R_W
} else {
(register + 1) & 0x7F
});
Some(Access::Read(register))
}
Phase::Write(register) => {
self.phase = Phase::Write((register + 1) & 0x7F);
Some(Access::Write(register, byte))
}
}
}
}