#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum SstvMode {
Pd120,
Pd180,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub struct ModeSpec {
pub mode: SstvMode,
pub vis_code: u8,
pub line_pixels: u32,
pub image_lines: u32,
pub line_seconds: f64,
pub sync_seconds: f64,
pub porch_seconds: f64,
pub pixel_seconds: f64,
pub septr_seconds: f64,
pub channel_layout: ChannelLayout,
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ChannelLayout {
PdYcbcr,
}
#[must_use]
pub fn lookup(vis_code: u8) -> Option<ModeSpec> {
match vis_code {
0x5F => Some(PD120),
0x60 => Some(PD180),
_ => None,
}
}
#[must_use]
pub fn for_mode(mode: SstvMode) -> ModeSpec {
match mode {
SstvMode::Pd120 => PD120,
SstvMode::Pd180 => PD180,
}
}
const PD120: ModeSpec = ModeSpec {
mode: SstvMode::Pd120,
vis_code: 0x5F,
line_pixels: 640,
image_lines: 496,
line_seconds: 0.508_48,
sync_seconds: 0.020,
porch_seconds: 0.002_08,
pixel_seconds: 0.000_19,
septr_seconds: 0.0, channel_layout: ChannelLayout::PdYcbcr,
};
const PD180: ModeSpec = ModeSpec {
mode: SstvMode::Pd180,
vis_code: 0x60,
line_pixels: 640,
image_lines: 496,
line_seconds: 0.754_24,
sync_seconds: 0.020,
porch_seconds: 0.002_08,
pixel_seconds: 0.000_286,
septr_seconds: 0.0, channel_layout: ChannelLayout::PdYcbcr,
};
#[cfg(test)]
#[allow(clippy::expect_used, clippy::float_cmp)]
mod tests {
use super::*;
#[test]
fn pd120_vis_code_resolves() {
let spec = lookup(0x5F).expect("PD120 VIS resolves");
assert_eq!(spec.mode, SstvMode::Pd120);
assert_eq!(spec.vis_code, 0x5F);
assert_eq!(spec.line_pixels, 640);
assert_eq!(spec.image_lines, 496);
assert_eq!(spec.channel_layout, ChannelLayout::PdYcbcr);
assert_eq!(spec.line_seconds, 0.508_48);
assert_eq!(spec.sync_seconds, 0.020);
assert_eq!(spec.porch_seconds, 0.002_08);
assert_eq!(spec.pixel_seconds, 0.000_19);
}
#[test]
fn pd180_vis_code_resolves() {
let spec = lookup(0x60).expect("PD180 VIS resolves");
assert_eq!(spec.mode, SstvMode::Pd180);
assert_eq!(spec.pixel_seconds, 0.000_286);
}
#[test]
fn unknown_vis_codes_return_none() {
assert!(lookup(0x00).is_none());
assert!(lookup(0x42).is_none()); assert!(lookup(0xFF).is_none());
}
#[test]
fn for_mode_returns_matching_spec() {
assert_eq!(for_mode(SstvMode::Pd120).vis_code, 0x5F);
assert_eq!(for_mode(SstvMode::Pd180).vis_code, 0x60);
}
#[test]
fn pd_modes_have_zero_septr_seconds() {
let pd120 = lookup(0x5F).expect("PD120");
let pd180 = lookup(0x60).expect("PD180");
assert_eq!(pd120.septr_seconds, 0.0);
assert_eq!(pd180.septr_seconds, 0.0);
}
}