mod address;
mod eeprom;
pub use eeprom::Mlx90640Calibration;
use crate::common::{Address, MelexisCamera, PixelAddressRange};
use crate::register::{AccessPattern, Subpage};
pub use address::RamAddress;
pub(crate) const HEIGHT: usize = 24;
pub(crate) const WIDTH: usize = 32;
pub(crate) const NUM_PIXELS: usize = HEIGHT * WIDTH;
#[derive(Clone, Debug, PartialEq)]
pub struct Mlx90640();
impl MelexisCamera for Mlx90640 {
type PixelRangeIterator = core::array::IntoIter<PixelAddressRange, 1>;
type PixelsInSubpageIterator = Mlx90640PixelSubpage;
fn pixel_ranges(_subpage: Subpage, _access_pattern: AccessPattern) -> Self::PixelRangeIterator {
core::array::IntoIter::new([PixelAddressRange {
start_address: RamAddress::Base.into(),
length: NUM_PIXELS * 2,
}])
}
fn pixels_in_subpage(
subpage: Subpage,
access_pattern: AccessPattern,
) -> Self::PixelsInSubpageIterator {
Mlx90640PixelSubpage::new(access_pattern, subpage)
}
fn t_a_v_be() -> Address {
RamAddress::AmbientTemperatureVoltageBe.into()
}
fn t_a_ptat() -> Address {
RamAddress::AmbientTemperatureVoltage.into()
}
fn compensation_pixel(subpage: Subpage) -> Address {
match subpage {
Subpage::Zero => RamAddress::CompensationPixelZero.into(),
Subpage::One => RamAddress::CompensationPixelOne.into(),
}
}
fn gain() -> Address {
RamAddress::Gain.into()
}
fn v_dd_pixel() -> Address {
RamAddress::PixelSupplyVoltage.into()
}
fn resolution_correction(calibrated_resolution: u8, current_resolution: u8) -> f32 {
let resolution_exp: i8 = calibrated_resolution as i8 - current_resolution as i8;
f32::from(resolution_exp).exp2()
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Mlx90640PixelSubpage {
index: usize,
access_pattern: AccessPattern,
subpage_num: usize,
}
impl Mlx90640PixelSubpage {
fn new(access_pattern: AccessPattern, subpage: Subpage) -> Self {
Self {
index: 0,
access_pattern,
subpage_num: subpage as usize,
}
}
}
impl Iterator for Mlx90640PixelSubpage {
type Item = bool;
fn next(&mut self) -> Option<Self::Item> {
if self.index < NUM_PIXELS {
let row = self.index / WIDTH;
let column = self.index % WIDTH;
self.index += 1;
Some(match self.access_pattern {
AccessPattern::Chess => {
if self.subpage_num == 0 {
row % 2 == column % 2
} else {
row % 2 != column % 2
}
}
AccessPattern::Interleave => row % 2 == self.subpage_num,
})
} else {
None
}
}
}
#[cfg(test)]
mod test {
use core::iter::repeat;
use crate::{AccessPattern, Subpage};
use super::{Mlx90640PixelSubpage, NUM_PIXELS, WIDTH};
#[test]
fn pixel_subpage_interleaved() {
let seq0 = Mlx90640PixelSubpage::new(AccessPattern::Interleave, Subpage::Zero);
let pattern0 = repeat(true)
.take(WIDTH)
.chain(repeat(false).take(WIDTH))
.cycle();
seq0.zip(pattern0)
.enumerate()
.for_each(|(index, (seq, expected))| {
assert_eq!(seq, expected, "{} is incorrect (pixel {})", seq, index)
});
let seq1 = Mlx90640PixelSubpage::new(AccessPattern::Interleave, Subpage::One);
let pattern1 = repeat(false)
.take(WIDTH)
.chain(repeat(true).take(WIDTH))
.cycle();
seq1.zip(pattern1)
.enumerate()
.for_each(|(index, (seq, expected))| {
assert_eq!(seq, expected, "{} is incorrect (pixel {})", seq, index)
});
}
#[test]
fn pixel_subpage_chess() {
let subpages = [Subpage::Zero, Subpage::One];
let zero_first = subpages.iter().copied().cycle().take(WIDTH);
let one_first = subpages.iter().copied().cycle().skip(1).take(WIDTH);
let chessboard = zero_first.chain(one_first).cycle().take(NUM_PIXELS);
let seq0 = Mlx90640PixelSubpage::new(AccessPattern::Chess, Subpage::Zero);
let seq1 = Mlx90640PixelSubpage::new(AccessPattern::Chess, Subpage::One);
let all = chessboard.zip(seq0.zip(seq1));
for (subpage, (seq0, seq1)) in all {
assert_eq!(subpage == Subpage::Zero, seq0);
assert_eq!(subpage == Subpage::One, seq1);
}
}
}