use serde::{Deserialize, Serialize};
use std::{
f32,
ffi::CString,
fmt::Display,
ops::{Div, Mul},
path::Path,
};
use crate::builders::{AtmosphereBuilder, AtmosphereBuilderError};
use super::{Cu, FromBuilder, Propagation, Single, Source};
use ffi::atmosphere;
#[derive(Debug, thiserror::Error)]
pub enum AtmosphereError {
#[error("cannot create `::crseo::AtmosphereBuilder`")]
Builder(#[from] AtmosphereBuilderError),
}
pub type Result<T> = std::result::Result<T, AtmosphereError>;
#[allow(dead_code)]
#[derive(Deserialize, Debug)]
#[serde(rename = "lower_case")]
struct GmtAtmosphere {
r0: f32,
#[serde(rename = "L0")]
l_not: f32,
#[serde(rename = "L")]
length: f32,
nxy_pupil: i32,
fov: f32,
duration: f32,
n_duration: i32,
filename: String,
seed: i32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[doc(hidden)]
pub struct TurbulenceProfile {
pub n_layer: usize,
pub altitude: Vec<f32>,
pub xi0: Vec<f32>,
pub wind_speed: Vec<f32>,
pub wind_direction: Vec<f32>,
}
impl Default for TurbulenceProfile {
fn default() -> Self {
TurbulenceProfile {
n_layer: 7,
altitude: [25.0, 275.0, 425.0, 1_250.0, 4_000.0, 8_000.0, 13_000.0].to_vec(),
xi0: [0.1257, 0.0874, 0.0666, 0.3498, 0.2273, 0.0681, 0.0751].to_vec(),
wind_speed: [5.6540, 5.7964, 5.8942, 6.6370, 13.2925, 34.8250, 29.4187].to_vec(),
wind_direction: [0.0136, 0.1441, 0.2177, 0.5672, 1.2584, 1.6266, 1.7462].to_vec(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[doc(hidden)]
pub struct RayTracing {
pub(crate) width: f32,
pub(crate) n_width_px: i32,
pub(crate) field_size: f32,
pub(crate) duration: f32,
pub(crate) filepath: Option<String>,
pub(crate) n_duration: Option<i32>,
}
impl Default for RayTracing {
fn default() -> Self {
Self {
width: 25.5,
n_width_px: 512,
field_size: 0.0,
duration: 1.0,
filepath: None,
n_duration: None,
}
}
}
impl RayTracing {
pub fn width(mut self, width: f64) -> Self {
self.width = width as f32;
self
}
pub fn n_width_px(mut self, n_width_px: usize) -> Self {
self.n_width_px = n_width_px as i32;
self
}
pub fn field_size(mut self, field_size: f64) -> Self {
self.field_size = field_size as f32;
self
}
pub fn duration(mut self, duration: f64) -> Self {
self.duration = duration as f32;
self
}
pub fn filepath<P: AsRef<Path>>(mut self, filepath: P) -> Self {
let path = filepath.as_ref();
self.filepath = Some(path.to_str().unwrap().to_string());
self
}
pub fn n_duration(mut self, n_duration: u64) -> Self {
self.n_duration = Some(n_duration as i32);
self
}
}
pub struct Atmosphere {
pub(crate) _c_: atmosphere,
pub r0_at_zenith: f64,
pub oscale: f64,
pub zenith_angle: f64,
pub secs: f64,
pub(crate) propagate_ptr: fn(&mut Atmosphere, &mut Source, f32),
}
impl Display for Atmosphere {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
"Atmosphere: r0@{:.0}z={:.2}cm, outer scale={:.2}m",
self.zenith_angle.to_degrees(),
self.r0() * 1e2,
self.oscale,
)
}
}
impl FromBuilder for Atmosphere {
type ComponentBuilder = AtmosphereBuilder;
}
impl Atmosphere {
pub fn as_raw_mut_ptr(&mut self) -> &mut atmosphere {
&mut self._c_
}
pub fn raytrace_build(
&mut self,
r_not: f32,
l_not: f32,
n_layer: i32,
mut altitude: Vec<f32>,
mut xi0: Vec<f32>,
mut wind_speed: Vec<f32>,
mut wind_direction: Vec<f32>,
width: f32,
n_width_px: i32,
field_size: f32,
duration: f32,
filepath: Option<&str>,
n_duration: Option<i32>,
) -> &mut Self {
match filepath {
Some(file) => unsafe {
self._c_.setup2(
r_not,
l_not,
n_layer,
altitude.as_mut_ptr(),
xi0.as_mut_ptr(),
wind_speed.as_mut_ptr(),
wind_direction.as_mut_ptr(),
width,
n_width_px,
field_size,
duration,
CString::new(file.to_owned().into_bytes())
.unwrap()
.into_raw(),
n_duration.unwrap_or(1),
);
},
None => unsafe {
self._c_.setup1(
r_not,
l_not,
n_layer,
altitude.as_mut_ptr(),
xi0.as_mut_ptr(),
wind_speed.as_mut_ptr(),
wind_direction.as_mut_ptr(),
width,
n_width_px,
field_size,
duration,
);
},
}
self.propagate_ptr = |a, s, t| unsafe {
let n_xy = s.pupil_sampling;
let d_xy = (s.pupil_size / (n_xy - 1) as f64) as f32;
a._c_
.rayTracing1(s.as_raw_mut_ptr(), d_xy, n_xy, d_xy, n_xy, t);
};
self
}
pub fn gmt_build(&mut self, r_not: f32, l_not: f32) -> &mut Self {
unsafe {
self._c_.gmt_setup4(r_not, l_not, 2020);
}
self
}
pub fn get_phase_values<'a, T>(
&mut self,
src: &mut Source,
x: &'a [T],
y: &'a [T],
t: f64,
) -> Vec<T>
where
&'a [T]: Into<Cu<Single>>,
Cu<Single>: Into<Vec<T>>,
T: 'a,
{
let n = x.len();
let mut gx: Cu<Single> = x.into();
let mut gy: Cu<Single> = y.into();
let mut ps = Cu::<Single>::vector(n);
ps.malloc();
unsafe {
self._c_.get_phase_screen(
ps.as_mut_ptr(),
gx.as_mut_ptr(),
gy.as_mut_ptr(),
n as i32,
src.as_raw_mut_ptr(),
t as f32,
)
}
ps.into()
}
pub fn get_phase_screen<'a, T>(
&mut self,
src: &mut Source,
t: f64,
(s_x, n_x): (T, usize),
other_side: Option<(T, usize)>,
) -> Vec<T>
where
T: 'a + Copy + From<u32> + Div<Output = T> + Mul<Output = T>,
Vec<T>: Into<Cu<Single>>,
Cu<Single>: Into<Vec<T>>,
{
let (s_y, n_y) = other_side.unwrap_or((s_x, n_x));
let n = n_x * n_y;
let mut x: Vec<T> = Vec::with_capacity(n);
let mut y: Vec<T> = Vec::with_capacity(n);
let delta_x = s_x / T::try_from(n_x as u32 - 1).unwrap();
let delta_y = s_y / T::try_from(n_x as u32 - 1).unwrap();
for i in 0..n_x {
for j in 0..n_y {
x.push(delta_x * T::try_from(i as u32).unwrap());
y.push(delta_y * T::try_from(j as u32).unwrap());
}
}
let mut gx: Cu<Single> = x.into();
let mut gy: Cu<Single> = y.into();
let mut ps = Cu::<Single>::vector(n);
ps.malloc();
unsafe {
self._c_.get_phase_screen(
ps.as_mut_ptr(),
gx.as_mut_ptr(),
gy.as_mut_ptr(),
n as i32,
src.as_raw_mut_ptr(),
t as f32,
)
}
ps.into()
}
pub fn update_r0(&mut self, new_r0: f64) {
self._c_.r0 = new_r0 as f32;
}
pub fn r0(&self) -> f64 {
let secz = 1f64 / self.zenith_angle.cos();
(self.r0_at_zenith.powf(-5.0 / 3.0) * secz).powf(-3.0 / 5.0)
}
pub fn reset(&mut self) {
unsafe {
self._c_.reset();
}
}
}
impl Drop for Atmosphere {
fn drop(&mut self) {
unsafe {
self._c_.cleanup();
}
}
}
impl Propagation for Atmosphere {
fn time_propagate(&mut self, secs: f64, src: &mut Source) {
(self.propagate_ptr)(self, src, secs as f32);
}
fn propagate(&mut self, src: &mut Source) {
self.time_propagate(self.secs, src)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn atmosphere_new() {
crate::ceo!(Atmosphere);
}
#[test]
fn dump_toml() -> anyhow::Result<()> {
let builder = AtmosphereBuilder::default().ray_tracing(Default::default());
builder.save("atm_builder.toml")?;
Ok(())
}
#[test]
fn load_toml() -> anyhow::Result<()> {
let builder = AtmosphereBuilder::load("atm_builder.toml")?;
dbg!(&builder);
Ok(())
}
}