use gmt_dos_clients_crseo::{
OpticalModel, OpticalModelBuilder, OpticalModelError,
crseo::{FromBuilder, Source, builders::SourceBuilder},
sensors::{Camera, builders::CameraBuilder},
};
use skyangle::Conversion;
use crate::kernels::{Kernel, KernelError, KernelSpecs};
use super::AgwsShackHartmann;
pub struct AgwsGuideStar;
impl AgwsGuideStar {
pub fn sh48() -> SourceBuilder {
Source::builder().band("VIS").size(3).zenith_azimuth(
vec![6f32.from_arcmin(), 5f32.from_arcmin(), 7f32.from_arcmin()],
vec![15f32.to_radians(), 145f32.to_radians(), 255f32.to_radians()],
)
}
pub fn sh24() -> SourceBuilder {
Source::builder()
.band("VIS")
.zenith_azimuth(vec![4f32.from_arcmin()], vec![180f32.to_radians()])
}
}
#[derive(Debug, thiserror::Error)]
pub enum ShackHartmannBuilderError {
#[error("missing Shack-Hartmann reconstructor")]
Reconstructor,
#[error("failed to build Kernel from ShackHartmannBuilder")]
Kernel(#[from] KernelError),
}
#[derive(Debug, Clone)]
pub struct ShackHartmannBuilder<R, const I: usize = 1> {
sh: CameraBuilder<I>,
pub(crate) src: SourceBuilder,
recon: Option<R>,
calibration_src_fwhm: Option<f64>,
use_calibration_src: bool,
}
impl<R, const I: usize> Default for ShackHartmannBuilder<R, I> {
fn default() -> Self {
Self {
sh: Default::default(),
src: Default::default(),
recon: Default::default(),
calibration_src_fwhm: Default::default(),
use_calibration_src: Default::default(),
}
}
}
impl<R, const I: usize> ShackHartmannBuilder<R, I> {
pub fn new() -> Self {
Default::default()
}
pub fn sh24() -> Self {
Self::default()
.sensor(AgwsShackHartmann::sh24())
.source(AgwsGuideStar::sh24())
.calibration_src_fwhm(12.)
}
pub fn sh48() -> Self {
Self::default()
.sensor(AgwsShackHartmann::sh48())
.source(AgwsGuideStar::sh48())
.calibration_src_fwhm(6.)
}
pub fn sensor(mut self, sh: CameraBuilder<I>) -> Self {
self.sh = sh;
self
}
pub fn source(mut self, src: SourceBuilder) -> Self {
self.src = src;
self
}
pub fn reconstructor(mut self, recon: R) -> Self {
self.recon = Some(recon);
self
}
pub fn calibration_src_fwhm(mut self, fwhm: f64) -> Self {
self.calibration_src_fwhm = Some(fwhm);
self
}
pub fn use_calibration_src(mut self) -> Self {
self.use_calibration_src = true;
self
}
}
impl<R, const I: usize> From<&ShackHartmannBuilder<R, I>>
for OpticalModelBuilder<CameraBuilder<I>>
{
fn from(value: &ShackHartmannBuilder<R, I>) -> Self {
OpticalModel::<Camera<I>>::builder()
.sensor(value.sh.clone())
.source(if value.use_calibration_src {
value
.src
.clone()
.fwhm(value.calibration_src_fwhm.unwrap_or_default())
} else {
value.src.clone()
})
}
}
impl<R, const I: usize> TryFrom<&ShackHartmannBuilder<R, I>> for OpticalModel<Camera<I>> {
type Error = OpticalModelError;
fn try_from(value: &ShackHartmannBuilder<R, I>) -> Result<Self, Self::Error> {
OpticalModelBuilder::<CameraBuilder<I>>::from(value).build()
}
}
impl<R, T, const I: usize> TryFrom<ShackHartmannBuilder<R, I>> for Kernel<T>
where
T: KernelSpecs<Sensor = Camera<I>, Estimator = R>,
{
type Error = ShackHartmannBuilderError;
fn try_from(value: ShackHartmannBuilder<R, I>) -> Result<Self, Self::Error> {
let ShackHartmannBuilder {
sh,
mut src,
recon,
calibration_src_fwhm,
..
} = value;
if let Some(fwhm) = calibration_src_fwhm {
src = src.fwhm(fwhm);
}
let model = OpticalModel::<Camera<I>>::builder().sensor(sh).source(src);
if let Some(estimator) = recon {
Ok(Kernel::new(&model)?.estimator(estimator))
} else {
Ok(Kernel::new(&model)?)
}
}
}