Skip to main content

fpm_rs/simulation/
presets.rs

1//! Small deterministic simulation presets for tests and benchmarks.
2//!
3//! These functions configure the existing [`super::Simulator`] and return its
4//! normal [`super::SimulationResult`]. They intentionally do not introduce a
5//! second synthetic-dataset abstraction.
6
7use crate::{
8    Result,
9    experiment::{LEDArray, Optics, PupilAberration},
10    model::{ImagePlaneModel, ReconstructionShape},
11};
12
13use super::{CameraModel, SimulationResult, Simulator, SyntheticObject};
14
15pub const NOISELESS_MIXED_PRESET: &str = "noiseless_mixed_v1";
16pub const ABERRATED_PUPIL_PRESET: &str = "aberrated_pupil_v1";
17pub const POISSON_GAUSSIAN_PRESET: &str = "poisson_gaussian_v1";
18
19/// A 3×3 LED, 32×32 measurement, 64×64 mixed-object ideal acquisition.
20pub fn noiseless_mixed_fpm(seed: u64) -> Result<SimulationResult> {
21    let model = base_model(base_optics())?;
22    Simulator::ideal(model)
23        .object(SyntheticObject::mixed_test_pattern((64, 64))?)
24        .seed(seed)
25        .simulate()
26}
27
28/// A known defocus/astigmatism mismatch suitable for EPRY pupil recovery.
29pub fn aberrated_pupil_fpm(seed: u64) -> Result<SimulationResult> {
30    let assumed_optics = base_optics();
31    let true_optics = Optics {
32        defocus_distance: Some(-18e-6),
33        pupil_aberration: Some(PupilAberration {
34            astigmatism: 0.25,
35            spherical: 0.12,
36            ..PupilAberration::default()
37        }),
38        ..assumed_optics.clone()
39    };
40    let true_model = base_model(true_optics)?;
41    let reconstruction_model = base_model(assumed_optics)?;
42    Simulator::new(true_model)
43        .object(SyntheticObject::mixed_test_pattern((64, 64))?)
44        .reconstruction_model(reconstruction_model)
45        .seed(seed)
46        .simulate()
47}
48
49/// A count-domain acquisition with shot noise, Gaussian read noise, offset,
50/// quantization, and a finite 16-bit detector range.
51pub fn poisson_gaussian_fpm(seed: u64) -> Result<SimulationResult> {
52    let model = base_model(base_optics())?;
53    let camera = CameraModel::new()
54        .photons_per_pixel(400.0)
55        .gain(1.5)
56        .offset_counts(100.0)
57        .read_noise_electrons(2.0)
58        .shot_noise(true)
59        .bit_depth(16)
60        .quantize(true);
61    Simulator::new(model)
62        .object(SyntheticObject::mixed_test_pattern((64, 64))?)
63        .camera(camera)
64        .seed(seed)
65        .simulate()
66}
67
68fn base_optics() -> Optics {
69    Optics {
70        wavelength: 532e-9,
71        objective_na: 0.10,
72        magnification: 4.0,
73        camera_pixel_size: 6.5e-6,
74        medium_index: 1.0,
75        defocus_distance: None,
76        pupil_aberration: None,
77    }
78}
79
80fn base_model(optics: Optics) -> Result<ImagePlaneModel> {
81    let illumination = LEDArray::new()
82        .grid_shape((3, 3))
83        .pitch(4e-3)
84        .distance(90e-3)
85        .center((1.0, 1.0));
86    ImagePlaneModel::from_experiment(
87        &optics,
88        &illumination,
89        (32, 32),
90        ReconstructionShape::Exact((64, 64)),
91    )
92}