use std::ops::Range;
use indicatif::ProgressBar;
use crate::{
builders::SourceBuilder,
wavefrontsensor::{Slopes, SlopesArray},
Builder, FromBuilder, Gmt, Propagation, SegmentWiseSensor,
};
#[derive(Debug, Clone)]
pub enum DOF {
Modes(Vec<usize>),
Range(Range<usize>),
}
impl IntoIterator for DOF {
type Item = usize;
type IntoIter = std::vec::IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
match self {
DOF::Modes(value) => value.into_iter(),
DOF::Range(value) => value.collect::<Vec<usize>>().into_iter(),
}
}
}
impl From<Vec<usize>> for DOF {
fn from(value: Vec<usize>) -> Self {
DOF::Modes(value)
}
}
impl From<Range<usize>> for DOF {
fn from(value: Range<usize>) -> Self {
DOF::Range(value)
}
}
impl DOF {
pub fn split_at(&self, idx: usize) -> (DOF, DOF) {
match self {
DOF::Modes(val) => {
let (left, right) = val.split_at(idx);
(DOF::Modes(left.to_vec()), DOF::Modes(right.to_vec()))
}
DOF::Range(val) => (DOF::Range(val.start..idx), DOF::Range(idx..val.end)),
}
}
pub fn modes(&self) -> Vec<usize> {
match self {
DOF::Modes(value) => value.clone(),
DOF::Range(value) => value.clone().collect(),
}
}
pub fn n_mode(&self) -> usize {
match self {
DOF::Modes(value) => value.len(),
DOF::Range(Range { start, end }) => end - start,
}
}
}
#[derive(Clone, Debug)]
pub enum RBM {
Txyz(Option<DOF>),
Rxyz(Option<DOF>),
TRxyz,
}
impl<S: Into<String>> From<S> for RBM {
fn from(value: S) -> Self {
let rbm: String = value.into();
match rbm.as_str() {
"Txyz" => RBM::Txyz(None),
"Txy" => RBM::Txyz(Some((0..2).into())),
"Rxyz" => RBM::Rxyz(None),
"Rxy" => RBM::Rxyz(Some((0..2).into())),
"TRxyz" => RBM::TRxyz,
_ => panic!(
r#"expected "Txyz", "Txy", "Rxyz", "Rxy" or "TRxyz" found {}"#,
rbm
),
}
}
}
#[derive(Clone, Debug, Copy)]
pub enum Stroke {
Scalar(f64),
RadialOrder(f64),
}
impl Stroke {
pub fn value(&self, i: usize) -> f64 {
match *self {
Stroke::Scalar(value) => value,
Stroke::RadialOrder(value) => {
let r = if i == 0 {
1_f64
} else {
(((8. * (i + 1) as f64 - 7.).sqrt() - 1.) * 0.5).floor()
};
value / r.sqrt()
}
}
}
}
impl From<f64> for Stroke {
fn from(value: f64) -> Self {
Self::Scalar(value)
}
}
#[derive(Clone, Debug)]
pub enum SegmentCalibration {
Modes {
stroke: Stroke,
name: String,
dof: DOF,
mirror: Mirror,
keep: bool,
},
RBM {
stroke: f64,
rbm: RBM,
mirror: Mirror,
keep: bool,
},
}
impl SegmentCalibration {
pub fn slip_at(&self, idx: usize) -> Option<(SegmentCalibration, SegmentCalibration)> {
let SegmentCalibration::Modes {
stroke,
ref name,
ref dof,
mirror,
keep,
} = *self
else {
return None;
};
let (left, right) = dof.split_at(idx);
Some((
SegmentCalibration::Modes {
stroke,
name: name.clone(),
dof: left,
mirror,
keep,
},
SegmentCalibration::Modes {
stroke,
name: name.into(),
dof: right,
mirror,
keep,
},
))
}
pub fn modes<S, D, M, T>(name: S, dof: D, mirror: M, stroke: T) -> Self
where
S: Into<String>,
D: Into<DOF>,
M: Into<Mirror>,
T: Into<Stroke>,
{
SegmentCalibration::Modes {
stroke: stroke.into(),
name: name.into(),
dof: dof.into(),
mirror: mirror.into(),
keep: true,
}
}
pub fn rbm<R, M>(rbm: R, mirror: M) -> Self
where
R: Into<RBM>,
M: Into<Mirror>,
{
SegmentCalibration::RBM {
stroke: 1e-6,
rbm: rbm.into(),
mirror: mirror.into(),
keep: true,
}
}
pub fn keep_all(self) -> Self {
match self {
SegmentCalibration::Modes {
stroke,
name,
dof,
mirror,
..
} => SegmentCalibration::Modes {
stroke,
name,
dof,
mirror,
keep: false,
},
SegmentCalibration::RBM {
stroke,
rbm,
mirror,
..
} => SegmentCalibration::RBM {
stroke,
rbm,
mirror,
keep: false,
},
}
}
}
#[derive(Clone, Debug, Copy)]
pub enum Mirror {
M1,
M2,
}
impl<S: Into<String>> From<S> for Mirror {
fn from(value: S) -> Self {
let m: String = value.into();
match m.as_str() {
"M1" => Mirror::M1,
"M2" => Mirror::M2,
_ => panic!(r#"expected "M1" or "M2", found {}"#, m),
}
}
}
impl SegmentCalibration {
pub fn calibrate<W>(
&self,
sid: usize,
wfs: &mut W,
src_builder: SourceBuilder,
pb: Option<ProgressBar>,
) -> SlopesArray
where
W: SegmentWiseSensor + Propagation,
{
let data_ref = wfs.zeroed_segment(sid, Some(src_builder.clone()));
let mut src = src_builder.build().unwrap();
let mut slopes = vec![];
let slopes = match self {
SegmentCalibration::Modes {
stroke,
name,
dof,
mirror,
keep,
} => {
let l = 1 + dof
.clone()
.into_iter()
.last()
.expect("expect some modes, found none");
let mut gmt = match mirror {
Mirror::M1 => Gmt::builder().m1(name, l),
Mirror::M2 => Gmt::builder().m2(name, l),
}
.build()
.unwrap();
if *keep {
gmt.keep(&[sid as i32]);
}
for kl_mode in dof.clone() {
pb.as_ref().map(|pb| pb.inc(1));
gmt.reset();
let kl_coef = stroke.value(kl_mode);
match mirror {
Mirror::M1 => gmt.m1_modes_ij(sid - 1, kl_mode, kl_coef),
Mirror::M2 => gmt.m2_modes_ij(sid - 1, kl_mode, kl_coef),
};
src.through(&mut gmt).xpupil().through(wfs);
let slopes_push: Slopes = wfs.into_slopes(&data_ref);
wfs.reset();
match mirror {
Mirror::M1 => gmt.m1_modes_ij(sid - 1, kl_mode, -kl_coef),
Mirror::M2 => gmt.m2_modes_ij(sid - 1, kl_mode, -kl_coef),
};
src.through(&mut gmt).xpupil().through(wfs);
let slopes_pull = wfs.into_slopes(&data_ref);
wfs.reset();
slopes.push((slopes_push - slopes_pull) / (2. * kl_coef as f32));
}
pb.as_ref().map(|pb| pb.finish());
slopes
}
SegmentCalibration::RBM {
stroke,
rbm,
mirror,
keep,
} => {
let mut gmt = Gmt::builder().build().unwrap();
if *keep {
gmt.keep(&[sid as i32]);
};
let dof = match rbm {
RBM::Txyz(dof) | RBM::Rxyz(dof) => {
dof.clone().unwrap_or(DOF::Range(0..3)).into_iter()
}
RBM::TRxyz => (0..6).collect::<Vec<usize>>().into_iter(),
};
for i in dof {
pb.as_ref().map(|pb| pb.inc(1));
gmt.reset();
let mut tr = [0f64; 6];
for s in [1f64, -1f64] {
match rbm {
RBM::Txyz(_) => tr[i] = *stroke * s,
RBM::Rxyz(_) => tr[i + 3] = *stroke * s,
RBM::TRxyz => tr[i] = *stroke * s,
}
match mirror {
Mirror::M1 => gmt.m1_segment_state(sid as i32, &tr[..3], &tr[3..]),
Mirror::M2 => gmt.m2_segment_state(sid as i32, &tr[..3], &tr[3..]),
};
src.through(&mut gmt).xpupil().through(wfs);
if s > 0f64 {
slopes.push(wfs.into_slopes(&data_ref));
} else {
slopes.last_mut().map(|mut s| {
s -= wfs.into_slopes(&data_ref);
s *= 0.5 / *stroke as f32
});
}
wfs.reset();
}
}
pb.as_ref().map(|pb| pb.finish());
slopes
}
};
(data_ref, slopes).into()
}
}