pub mod shack_hartmann;
use std::path::Path;
use gmt_dos_actors::system::{Sys, SystemError};
use gmt_dos_clients_crseo::{
OpticalModel, OpticalModelBuilder, OpticalModelError,
crseo::{
FromBuilder, Source,
builders::{AtmosphereBuilder, AtmosphereBuilderError, GmtBuilder},
imaging::{Detector, LensletArray},
},
sensors::{
Camera, WaveSensor,
builders::{CameraBuilder, WaveSensorBuilder},
},
};
use gmt_dos_clients_io::optics::{Dev, Frame};
use interface::TryRead;
use shack_hartmann::{ShackHartmannBuilder, ShackHartmannBuilderError};
use crate::{
Agws,
agws::{
AgwsParts,
sh24::{Sh24, kernel::Sh24Kern},
sh48::{Sh48, kernel::Sh48Kern},
},
kernels::{Kernel, KernelError, KernelFrame, KernelSpecs},
};
#[derive(Debug, thiserror::Error)]
pub enum AgwsBuilderError {
#[error("failed to build AGWS optical model")]
OpticalModel(#[from] OpticalModelError),
#[error("AGWS kernel error")]
AgwsKernel(#[from] KernelError),
#[error("AGWS system error")]
AgwsSystem(#[from] SystemError),
#[error("failed load atmospheric turbulence model")]
Atmosphere(#[from] AtmosphereBuilderError),
#[error("ShackHartmann builder error")]
ShackHartmann(#[from] ShackHartmannBuilderError),
}
pub struct AgwsShackHartmann;
impl AgwsShackHartmann {
pub fn sh48<const I: usize>() -> CameraBuilder<I> {
Camera::<I>::builder()
.n_sensor(3)
.lenslet_array(LensletArray::default().n_side_lenslet(48).n_px_lenslet(18))
.detector(Detector::default().n_px_imagelet(24).n_px_framelet(8))
.lenslet_flux(0.75)
}
pub fn sh24<const I: usize>() -> CameraBuilder<I> {
Camera::<I>::builder()
.lenslet_array(LensletArray::default().n_side_lenslet(24).n_px_lenslet(36))
.detector(Detector::default().n_px_imagelet(72).n_px_framelet(12))
.lenslet_flux(0.75)
}
}
#[allow(dead_code)]
pub struct AgwsBuilder<
const SH48_I: usize = 1,
const SH24_I: usize = 1,
K48 = Sh48<SH48_I>,
K24 = Sh24<SH24_I>,
> where
K48: KernelSpecs,
K24: KernelSpecs,
{
sh48: ShackHartmannBuilder<<K48 as KernelSpecs>::Estimator, SH48_I>,
sh24: ShackHartmannBuilder<<K24 as KernelSpecs>::Estimator, SH24_I>,
gmt: Option<GmtBuilder>,
atm: Option<(AtmosphereBuilder, f64)>,
sh24_controller: Option<<K24 as KernelSpecs>::Controller>,
}
impl<const SH48_I: usize, const SH24_I: usize, K48, K24> Default
for AgwsBuilder<SH48_I, SH24_I, K48, K24>
where
K48: KernelSpecs,
K24: KernelSpecs,
{
fn default() -> Self {
Self {
sh48: ShackHartmannBuilder::sh48(),
sh24: ShackHartmannBuilder::sh24(),
gmt: None,
atm: None,
sh24_controller: None,
}
}
}
impl<const SH48_I: usize, const SH24_I: usize, K48, K24> AgwsBuilder<SH48_I, SH24_I, K48, K24>
where
K48: KernelSpecs<Input = Frame<Dev>, Sensor = Camera<SH48_I>> + 'static + Send + Sync,
K24: KernelSpecs<Input = Frame<Dev>, Sensor = Camera<SH24_I>> + 'static + Send + Sync,
Sh24Kern<K24>: TryRead<KernelFrame<K24>>,
Sh48Kern<K48>: TryRead<KernelFrame<K48>>,
{
pub fn new() -> Self {
Default::default()
}
pub fn gmt(mut self, gmt: GmtBuilder) -> Self {
self.gmt = Some(gmt);
self
}
pub fn load_atmosphere(
mut self,
path: impl AsRef<Path>,
sampling_frequency: f64,
) -> Result<Self, AgwsBuilderError> {
self.atm = Some((AtmosphereBuilder::load(path)?, sampling_frequency));
Ok(self)
}
pub fn atmosphere(mut self, atm: AtmosphereBuilder, sampling_frequency: f64) -> Self {
self.atm = Some((atm, sampling_frequency));
self
}
#[cfg(feature = "sh48")]
pub fn sh48(
mut self,
sh48: ShackHartmannBuilder<<K48 as KernelSpecs>::Estimator, SH48_I>,
) -> Self {
self.sh48 = sh48;
self
}
#[cfg(feature = "sh24")]
pub fn sh24(
mut self,
sh24: ShackHartmannBuilder<<K24 as KernelSpecs>::Estimator, SH24_I>,
) -> Self {
self.sh24 = sh24;
self
}
#[cfg(feature = "sh24")]
pub fn sh24_controller(mut self, sh24_controller: <K24 as KernelSpecs>::Controller) -> Self {
self.sh24_controller = Some(sh24_controller);
self
}
#[cfg(feature = "sh24")]
pub fn sh24_calibration(mut self, sh24_recon: <K24 as KernelSpecs>::Estimator) -> Self {
self.sh24 = self.sh24.reconstructor(sh24_recon);
self
}
#[cfg(feature = "sh48")]
pub fn sh48_calibration(mut self, sh48_recon: <K48 as KernelSpecs>::Estimator) -> Self {
self.sh48 = self.sh48.reconstructor(sh48_recon);
self
}
pub fn wave_sensor(&self) -> OpticalModelBuilder<WaveSensorBuilder> {
let zenith: Vec<_> = self
.sh24
.src
.zenith
.iter()
.chain(&self.sh48.src.zenith)
.cloned()
.collect();
let azimuth: Vec<_> = self
.sh24
.src
.azimuth
.iter()
.chain(&self.sh48.src.azimuth)
.cloned()
.collect();
let src = Source::builder()
.size(zenith.len())
.zenith_azimuth(zenith, azimuth);
OpticalModel::<WaveSensor>::builder()
.gmt(self.gmt.clone().unwrap_or_default())
.source(src.clone())
.sensor(
WaveSensor::builder()
.gmt(self.gmt.clone().unwrap_or_default())
.source(src),
)
}
#[allow(unused_mut)]
pub fn build(mut self) -> Result<Sys<Agws<SH48_I, SH24_I, K48, K24>>, AgwsBuilderError> {
#[allow(unused_variables)]
let (sh24_label, sh48_label) = if self.atm.is_none() {
(
format!("GMT Optics\nw/ SH24<{}>", SH24_I),
format!("GMT Optics\nw/ {} SH48<{}>", self.sh48.src.size, SH48_I),
)
} else {
(
format!("GMT Optics & Atmosphere\nw/ SH24<{}>", SH24_I),
format!(
"GMT Optics & Atmosphere\nw/ {} SH48<{}>",
self.sh48.src.size, SH48_I
),
)
};
let AgwsParts {
sh48,
sh24,
sh24_kernel,
sh48_kernel,
..
} = self.parts()?;
Ok(Sys::new(Agws {
#[cfg(feature = "sh24")]
sh24: (sh24, sh24_label).into(),
#[cfg(feature = "sh48")]
sh48: (sh48, sh48_label).into(),
#[cfg(feature = "shk24")]
sh24_kernel: sh24_kernel.into(),
#[cfg(feature = "shk48")]
sh48_kernel: sh48_kernel.into(),
})
.build()?)
}
#[allow(unused_mut)]
pub fn parts(mut self) -> Result<AgwsParts<SH48_I, SH24_I, K48, K24>, AgwsBuilderError> {
let mut sh48 = OpticalModelBuilder::from(&self.sh48);
let mut sh24 = OpticalModelBuilder::from(&self.sh24);
if let Some((atm, sampling_frequency)) = self.atm {
sh48 = sh48
.atmosphere(atm.clone())
.sampling_frequency(sampling_frequency);
sh24 = sh24
.atmosphere(atm.clone())
.sampling_frequency(sampling_frequency);
}
if let Some(gmt) = self.gmt {
sh48 = sh48.gmt(gmt.clone());
sh24 = sh24.gmt(gmt);
}
let sh48 = sh48.build()?;
log::info!("SH48:\n{}", sh48);
let sh24 = sh24.build()?;
log::info!("SH24:\n{}", sh24);
let sh24_kern = Kernel::<K24>::try_from(self.sh24)
.map_err(|_e| ShackHartmannBuilderError::Reconstructor)
.map(|sh24_kern| {
if let Some(sh24_controller) = self.sh24_controller.take() {
sh24_kern.controller(sh24_controller)
} else {
sh24_kern
}
})?;
let sh48_kern = Kernel::<K48>::try_from(self.sh48)
.map_err(|_e| ShackHartmannBuilderError::Reconstructor)?;
Ok(AgwsParts {
sh24: Sh24(sh24),
sh48: Sh48(sh48),
sh24_kernel: Sh24Kern(sh24_kern),
sh48_kernel: Sh48Kern(sh48_kern),
})
}
}