fpm_rs/experiment/
optics.rs1use serde::{Deserialize, Serialize};
2
3use crate::error::{Error, Result};
4
5#[derive(Clone, Debug, Serialize, Deserialize)]
7pub struct Optics {
8 pub wavelength: f64,
9 pub objective_na: f64,
10 pub magnification: f64,
11 pub camera_pixel_size: f64,
12 pub medium_index: f64,
13 pub defocus_distance: Option<f64>,
15 pub pupil_aberration: Option<PupilAberration>,
21}
22
23#[derive(Clone, Debug, Default, Serialize, Deserialize)]
36pub struct PupilAberration {
37 pub astigmatism: f64,
39 pub coma: f64,
41 pub spherical: f64,
43 pub edge_apodization: f64,
46}
47
48impl PupilAberration {
49 pub fn validate(&self) -> Result<()> {
50 if [
51 self.astigmatism,
52 self.coma,
53 self.spherical,
54 self.edge_apodization,
55 ]
56 .iter()
57 .any(|value| !value.is_finite())
58 || self.edge_apodization < 0.0
59 {
60 return Err(Error::InvalidParameter {
61 name: "pupil_aberration",
62 reason: "phase coefficients must be finite and edge apodization must be finite and non-negative"
63 .into(),
64 });
65 }
66 Ok(())
67 }
68}
69
70impl Optics {
71 pub fn validate(&self) -> Result<()> {
72 for (name, value) in [
73 ("wavelength", self.wavelength),
74 ("objective_na", self.objective_na),
75 ("magnification", self.magnification),
76 ("camera_pixel_size", self.camera_pixel_size),
77 ("medium_index", self.medium_index),
78 ] {
79 if !value.is_finite() || value <= 0.0 {
80 return Err(Error::InvalidParameter {
81 name,
82 reason: format!("must be finite and positive, got {value}"),
83 });
84 }
85 }
86 if self.objective_na > self.medium_index {
87 return Err(Error::InvalidParameter {
88 name: "objective_na",
89 reason: "cannot exceed the medium refractive index".into(),
90 });
91 }
92 if self
93 .defocus_distance
94 .is_some_and(|value| !value.is_finite())
95 {
96 return Err(Error::InvalidParameter {
97 name: "defocus_distance",
98 reason: "must be finite".into(),
99 });
100 }
101 if let Some(aberration) = &self.pupil_aberration {
102 aberration.validate()?;
103 }
104 Ok(())
105 }
106
107 pub fn object_pixel_size(&self) -> f64 {
108 self.camera_pixel_size / self.magnification
109 }
110
111 pub fn medium_wavenumber(&self) -> f64 {
112 std::f64::consts::TAU * self.medium_index / self.wavelength
113 }
114}