use crate::{
gmt::{GmtM1, GmtM2, GmtMx},
Builder, CrseoError, Gmt, GmtError,
};
use serde::{Deserialize, Serialize};
use std::{env, ffi::CString, path::Path};
#[derive(Default, Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct MirrorBuilder {
pub mode_type: String,
pub n_mode: usize,
pub a: Vec<f64>,
}
impl MirrorBuilder {
pub fn mode_type(self, mode_type: &str) -> Self {
Self {
mode_type: mode_type.into(),
..self
}
}
pub fn n_mode(self, n_mode: usize) -> Self {
Self {
n_mode,
a: vec![0f64; 7 * n_mode],
..self
}
}
pub fn default_state(self, a: Vec<f64>) -> Self {
assert!(
a.len() == 7 * self.n_mode,
"Incorrect number of modal coeffcients, expected: {}, found: {}",
7 * self.n_mode,
a.len()
);
Self { a, ..self }
}
}
pub trait GmtMirrorBuilder<M: GmtMx> {
fn n_mode(self, n_mode: usize) -> Self;
}
impl GmtMirrorBuilder<GmtM1> for GmtBuilder {
#[inline]
fn n_mode(self, n_mode: usize) -> Self {
Self {
m1: self.m1.n_mode(n_mode),
..self
}
}
}
impl GmtMirrorBuilder<GmtM2> for GmtBuilder {
#[inline]
fn n_mode(self, n_mode: usize) -> Self {
Self {
m2: self.m2.n_mode(n_mode),
..self
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GmtBuilder {
pub m1: MirrorBuilder,
pub m2: MirrorBuilder,
pub pointing_error: Option<(f64, f64)>,
pub m1_truss_projection: bool,
}
impl Default for GmtBuilder {
fn default() -> Self {
GmtBuilder {
m1: MirrorBuilder {
mode_type: "bending modes".into(),
..Default::default()
},
m2: MirrorBuilder {
mode_type: "Karhunen-Loeve".into(),
..Default::default()
},
pointing_error: None,
m1_truss_projection: true,
}
}
}
impl GmtBuilder {
pub fn m1(self, mode_type: &str, n_mode: usize) -> Self {
Self {
m1: self.m1.mode_type(mode_type).n_mode(n_mode),
..self
}
}
pub fn n_mode<M: GmtMx>(self, n_mode: usize) -> Self
where
GmtBuilder: GmtMirrorBuilder<M>,
{
<Self as GmtMirrorBuilder<M>>::n_mode(self, n_mode)
}
pub fn m1_n_mode(self, n_mode: usize) -> Self {
Self {
m1: self.m1.n_mode(n_mode),
..self
}
}
pub fn m1_truss_projection(mut self, m1_truss_projection: bool) -> Self {
self.m1_truss_projection = m1_truss_projection;
self
}
pub fn m1_default_state(self, a: Vec<f64>) -> Self {
Self {
m1: self.m1.default_state(a),
..self
}
}
pub fn m2(self, mode_type: &str, n_mode: usize) -> Self {
Self {
m2: self.m2.mode_type(mode_type).n_mode(n_mode),
..self
}
}
pub fn m2_n_mode(self, n_mode: usize) -> Self {
Self {
m2: self.m2.n_mode(n_mode),
..self
}
}
pub fn m2_default_state(self, a: Vec<f64>) -> Self {
Self {
m2: self.m2.default_state(a),
..self
}
}
pub fn pointing_error(mut self, pointing_error: (f64, f64)) -> Self {
self.pointing_error = Some(pointing_error);
self
}
}
#[derive(Debug, thiserror::Error)]
pub enum GmtModesError {
#[error("the mirror modes file ({0}) does not exist")]
Path(String),
#[error(r#"the environment variable "GMT_MODES_PATH" is not set"#)]
EnvVar(#[from] std::env::VarError),
}
impl MirrorBuilder {
fn mode_path(&self) -> std::result::Result<String, GmtModesError> {
let mode_type = Path::new(&self.mode_type).with_extension("ceo");
if mode_type.is_file() {
Ok(mode_type.to_str().unwrap().to_owned())
} else {
let env_path = env::var("GMT_MODES_PATH")?;
let path = Path::new(&env_path).join(mode_type);
if path.is_file() {
Ok(path.to_str().unwrap().to_owned())
} else {
Err(GmtModesError::Path(path.to_str().unwrap().to_string()))
}
}
}
}
impl Builder for GmtBuilder {
type Component = Gmt;
fn build(self) -> std::result::Result<Gmt, CrseoError> {
let m1_mode_type = CString::new(self.m1.mode_path().map_err(|e| GmtError::from(e))?)?;
let m2_mode_type = CString::new(self.m2.mode_path().map_err(|e| GmtError::from(e))?)?;
let mut gmt = Gmt {
m1: self.m1.into(),
m2: self.m2.into(),
pointing_error: self.pointing_error,
m1_truss_projection: self.m1_truss_projection,
};
unsafe {
let n_mode = gmt.m1.n_mode;
gmt.m1.setup1(m1_mode_type.into_raw(), 7, n_mode as i32);
}
unsafe {
let n_mode = gmt.m2.n_mode;
gmt.m2.setup1(m2_mode_type.into_raw(), 7, n_mode as i32);
}
gmt.reset();
Ok(gmt)
}
}
impl From<&Gmt> for GmtBuilder {
fn from(gmt: &Gmt) -> Self {
Self {
m1: gmt.get_m1(),
m2: gmt.get_m2(),
pointing_error: gmt.pointing_error,
m1_truss_projection: gmt.m1_truss_projection,
}
}
}
#[cfg(test)]
pub mod tests {
use std::f64::consts::FRAC_PI_2;
use crate::{FromBuilder, Source};
use super::*;
use skyangle::Conversion;
#[test]
fn pointing_error() {
let mut gmt = Gmt::builder()
.pointing_error((1f64.from_arcsec(), FRAC_PI_2))
.build()
.unwrap();
let mut src = Source::builder().build().unwrap();
src.through(&mut gmt).xpupil();
let tt: Vec<_> = src.gradients().into_iter().map(|x| x.to_mas()).collect();
dbg!(tt);
}
}