Skip to main content

scopinator_types/
exposure.rs

1use bytes::Bytes;
2use serde::{Deserialize, Serialize};
3
4/// Settings for a camera exposure.
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6pub struct ExposureSettings {
7    pub duration_seconds: f64,
8    pub gain: Option<i32>,
9    pub offset: Option<i32>,
10    pub bin_x: u32,
11    pub bin_y: u32,
12    pub light: bool,
13}
14
15impl Default for ExposureSettings {
16    fn default() -> Self {
17        Self {
18            duration_seconds: 1.0,
19            gain: None,
20            offset: None,
21            bin_x: 1,
22            bin_y: 1,
23            light: true,
24        }
25    }
26}
27
28/// Kind of image frame.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
30#[non_exhaustive]
31pub enum FrameKind {
32    /// Live view / preview frame.
33    Preview,
34    /// Stacked result.
35    Stack,
36}
37
38/// Bayer pattern for raw sensor data.
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
40#[non_exhaustive]
41pub enum BayerPattern {
42    Grbg,
43    Rggb,
44    Bggr,
45    Gbrg,
46}
47
48/// Image data from a camera or imaging connection.
49#[derive(Debug, Clone)]
50pub struct ImageData {
51    pub width: u32,
52    pub height: u32,
53    pub data: Bytes,
54    pub bit_depth: u32,
55    pub is_color: bool,
56    pub bayer_pattern: Option<BayerPattern>,
57    pub frame_kind: FrameKind,
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    use proptest::prelude::*;
64
65    fn frame_kinds() -> impl Strategy<Value = FrameKind> {
66        prop_oneof![Just(FrameKind::Preview), Just(FrameKind::Stack)]
67    }
68
69    fn bayer_patterns() -> impl Strategy<Value = BayerPattern> {
70        prop_oneof![
71            Just(BayerPattern::Grbg),
72            Just(BayerPattern::Rggb),
73            Just(BayerPattern::Bggr),
74            Just(BayerPattern::Gbrg),
75        ]
76    }
77
78    proptest! {
79        #[test]
80        fn frame_kind_roundtrip(kind in frame_kinds()) {
81            let s = serde_json::to_string(&kind).unwrap();
82            let back: FrameKind = serde_json::from_str(&s).unwrap();
83            prop_assert_eq!(kind, back);
84        }
85
86        #[test]
87        fn bayer_pattern_roundtrip(pat in bayer_patterns()) {
88            let s = serde_json::to_string(&pat).unwrap();
89            let back: BayerPattern = serde_json::from_str(&s).unwrap();
90            prop_assert_eq!(pat, back);
91        }
92
93        #[test]
94        fn exposure_settings_roundtrip(
95            // Millisecond precision in [0, 3600s]: exactly representable as f64.
96            duration_ms in 0u32..=3_600_000,
97            gain in proptest::option::of(any::<i32>()),
98            offset in proptest::option::of(any::<i32>()),
99            bin_x in 1u32..=16,
100            bin_y in 1u32..=16,
101            light in any::<bool>(),
102        ) {
103            let duration_seconds = f64::from(duration_ms) / 1000.0;
104            let exp = ExposureSettings { duration_seconds, gain, offset, bin_x, bin_y, light };
105            let s = serde_json::to_string(&exp).unwrap();
106            let back: ExposureSettings = serde_json::from_str(&s).unwrap();
107            prop_assert_eq!(exp, back);
108        }
109    }
110}