1use serde::{Deserialize, Serialize};
2
3use crate::error::{Error, Result};
4
5#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
6pub enum CoordinateConvention {
7 CenteredPositiveK,
10}
11
12#[derive(Clone, Debug, Serialize, Deserialize)]
13pub struct Sampling {
14 pub low_res_pixel_size: f64,
15 pub high_res_pixel_size: f64,
16 pub dkx: f64,
17 pub dky: f64,
18 pub wavelength: Option<f64>,
19 pub synthetic_na: Option<f64>,
20 pub coordinate_convention: CoordinateConvention,
21}
22
23impl Sampling {
24 pub fn new(
25 low_res_pixel_size: f64,
26 high_res_pixel_size: f64,
27 dkx: f64,
28 dky: f64,
29 ) -> Result<Self> {
30 let sampling = Self {
31 low_res_pixel_size,
32 high_res_pixel_size,
33 dkx,
34 dky,
35 wavelength: None,
36 synthetic_na: None,
37 coordinate_convention: CoordinateConvention::CenteredPositiveK,
38 };
39 sampling.validate()?;
40 Ok(sampling)
41 }
42
43 pub fn validate(&self) -> Result<()> {
44 for (name, value) in [
45 ("low_res_pixel_size", self.low_res_pixel_size),
46 ("high_res_pixel_size", self.high_res_pixel_size),
47 ("dkx", self.dkx),
48 ("dky", self.dky),
49 ] {
50 if !value.is_finite() || value <= 0.0 {
51 return Err(Error::InvalidParameter {
52 name,
53 reason: "must be finite and positive".into(),
54 });
55 }
56 }
57 if self.high_res_pixel_size > self.low_res_pixel_size {
58 return Err(Error::InvalidParameter {
59 name: "high_res_pixel_size",
60 reason: "must not exceed low-resolution pixel size".into(),
61 });
62 }
63 if self
64 .wavelength
65 .is_some_and(|value| !value.is_finite() || value <= 0.0)
66 {
67 return Err(Error::InvalidParameter {
68 name: "wavelength",
69 reason: "must be finite and positive when present".into(),
70 });
71 }
72 if self
73 .synthetic_na
74 .is_some_and(|value| !value.is_finite() || value <= 0.0)
75 {
76 return Err(Error::InvalidParameter {
77 name: "synthetic_na",
78 reason: "must be finite and positive when present".into(),
79 });
80 }
81 Ok(())
82 }
83}