use crate::{builders::CentroidingBuilder, Builder, FromBuilder};
use ffi::{centroiding, dev2host, host2dev_char, mask};
use crate::imaging::Frame;
pub struct Centroiding {
pub(crate) _c_: centroiding,
pub(crate) _c_mask_: mask,
pub n_lenslet_total: usize,
pub n_centroids: usize,
pub units: f32,
pub(crate) flux: Vec<f32>,
pub valid_lenslets: Vec<i8>,
pub n_valid_lenslet: Vec<usize>,
pub centroids: Vec<f32>,
pub(crate) xy_mean: Option<Vec<(f32, f32)>>,
}
impl FromBuilder for Centroiding {
type ComponentBuilder = CentroidingBuilder;
}
impl Centroiding {
pub fn process(&mut self, frame: &Frame, reference: Option<&Centroiding>) -> &mut Self {
match reference {
None => unsafe {
self._c_.get_data2(
frame.dev.as_ptr() as *mut _,
frame.n_px_camera as i32,
0.0,
0.0,
self.units,
);
},
Some(r) => {
assert_eq!(self.n_lenslet_total, r.n_lenslet_total);
unsafe {
self._c_.get_data3(
frame.dev.as_ptr() as *mut _,
frame.n_px_camera as i32,
r._c_.d__cx,
r._c_.d__cy,
self.units,
r._c_mask_.m,
);
}
}
};
self
}
pub fn grab(&mut self) -> &mut Self {
unsafe {
dev2host(
self.centroids.as_mut_ptr(),
self._c_.d__c,
self.n_centroids as i32,
);
}
self
}
pub fn remove_mean(&mut self, some_valid_lenslets: Option<&Vec<i8>>) -> &mut Self {
let valid_lenslets = some_valid_lenslets.unwrap_or_else(|| &self.valid_lenslets);
let xy_mean: Vec<_> = valid_lenslets
.chunks(self.n_lenslet_total)
.zip(self.centroids.chunks_mut(self.n_lenslet_total * 2))
.map(|(v, c)| {
let vc = c
.iter()
.zip(v.iter().cycle())
.filter_map(|(c, v)| if v.is_positive() { Some(*c) } else { None })
.collect::<Vec<_>>();
let n = vc.len() / 2;
let (x, y) = vc.split_at(n);
let x_mean = x.iter().sum::<f32>() / n as f32;
let y_mean = y.iter().sum::<f32>() / n as f32;
let (cx, cy) = c.split_at_mut(self.n_lenslet_total);
cx.iter_mut()
.zip(cy.iter_mut())
.zip(v.iter())
.for_each(|((cx, cy), v)| {
if v.is_positive() {
*cx -= x_mean;
*cy -= y_mean;
}
});
(x_mean, y_mean)
})
.collect();
self.xy_mean = Some(xy_mean);
self
}
pub fn valids(&self, some_valid_lenslets: Option<&Vec<i8>>) -> Vec<Vec<f32>> {
let mut valid_lenslets = some_valid_lenslets
.unwrap_or_else(|| &self.valid_lenslets)
.chunks(self.n_lenslet_total);
self.centroids
.chunks(self.n_lenslet_total * 2)
.map(|c| {
c.iter()
.zip(valid_lenslets.next().unwrap().iter().cycle())
.filter_map(|(c, v)| if v.is_positive() { Some(*c) } else { None })
.collect::<Vec<_>>()
})
.collect()
}
pub fn lenslet_flux(&self) -> &Vec<f32> {
unsafe {
dev2host(
self.flux.as_ptr() as *mut _,
self._c_.d__mass,
self.flux.len() as i32,
);
}
&self.flux
}
pub fn lenslet_array_flux(&mut self) -> Vec<f32> {
self.lenslet_flux()
.chunks(self.n_lenslet_total)
.map(|x| x.iter().sum())
.collect::<Vec<_>>()
}
pub fn integrated_flux(&mut self) -> f32 {
self.lenslet_flux().iter().sum()
}
pub fn valid_lenslets(
&mut self,
some_flux_threshold: Option<f64>,
some_valid_lenslets: Option<Vec<i8>>,
) -> &mut Self {
if let Some(some_flux_threshold) = some_flux_threshold {
let n = self.n_lenslet_total;
let lenslet_flux = self.lenslet_flux();
self.valid_lenslets = lenslet_flux
.chunks(n)
.flat_map(|flux| {
let threshold_flux = flux
.iter()
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap()
* some_flux_threshold as f32;
flux.iter()
.map(|x| if x >= &threshold_flux { 1i8 } else { 0i8 })
.collect::<Vec<_>>()
})
.collect();
}
if some_valid_lenslets.is_some() {
self.valid_lenslets = some_valid_lenslets.unwrap();
}
unsafe {
host2dev_char(
self._c_mask_.m,
self.valid_lenslets.as_mut_ptr(),
self.valid_lenslets.len() as i32,
);
self._c_mask_.set_filter();
}
self.n_valid_lenslet = self
.valid_lenslets
.chunks(self.n_lenslet_total)
.map(|x| x.iter().filter(|x| x.is_positive()).count())
.collect();
self
}
pub fn n_valid_lenslet_total(&self) -> usize {
self.n_valid_lenslet.iter().sum()
}
pub fn __ceo__(&mut self) -> (¢roiding, &mask) {
(&self._c_, &self._c_mask_)
}
pub fn __mut_ceo__(&mut self) -> (&mut centroiding, &mut mask) {
(&mut self._c_, &mut self._c_mask_)
}
}
impl Default for Centroiding {
fn default() -> Self {
Self::builder().build().unwrap()
}
}
impl Drop for Centroiding {
fn drop(&mut self) {
unsafe {
self._c_.cleanup();
self._c_mask_.cleanup();
}
}
}
#[cfg(test)]
mod tests {
use std::env;
use skyangle::Conversion;
use super::*;
use crate::{imaging::LensletArray, Builder, Gmt, Imaging, Source};
#[test]
fn shackhartmann() {
env::set_var("GMT_MODES_PATH", "/home/ubuntu/CEO/gmtMirrors/");
let n_side_lenslet = 6;
let n_px_lenslet = 16;
let pupil_sampling = n_side_lenslet * n_px_lenslet + 1;
let mut gmt = Gmt::builder().build().unwrap();
let n_gs = 2;
let mut src = Source::builder()
.pupil_sampling(pupil_sampling)
.size(n_gs)
.zenith_azimuth(vec![0f32; n_gs], vec![0f32; n_gs])
.build()
.unwrap();
let sensor = Imaging::builder()
.lenslet_array(
LensletArray::default()
.n_side_lenslet(n_side_lenslet)
.n_px_lenslet(n_px_lenslet),
)
.n_sensor(n_gs);
let mut cog0 = CentroidingBuilder::from(&sensor).build().unwrap();
let mut cog = CentroidingBuilder::from(&sensor).build().unwrap();
let mut sensor = sensor.build().unwrap();
src.through(&mut gmt).xpupil().through(&mut sensor);
cog0.process(&sensor.frame(), None)
.valid_lenslets(Some(0.85), None);
src.through(&mut gmt).xpupil().through(sensor.reset());
cog.process(&sensor.frame(), Some(&cog0)).grab();
let m2_rbm = vec![vec![0f64, 0f64, 0f64, 1e-6, 0f64, 0f64]; 7];
let mut m2_rbm = vec![vec![0f64; 6]; 7];
m2_rbm[0][3] = 1f64.from_arcsec();
gmt.update(None, Some(&m2_rbm), None, None);
src.through(&mut gmt).xpupil().through(sensor.reset());
dbg!(src.wfe_rms());
cog.process(&sensor.frame(), Some(&cog0)).grab();
{
println!("centroids");
for (i, c) in cog.centroids.chunks(n_side_lenslet.pow(2) * 2).enumerate() {
println!("GS #{}", i + 1);
let (cx, cy) = c.split_at(n_side_lenslet * n_side_lenslet);
cx.iter()
.zip(cy)
.for_each(|(x, y)| println!("{:+.3} {:+.3}", x, y));
}
}
let v = cog.valids(Some(&cog0.valid_lenslets));
{
println!("valid centroids");
for (i, v) in v.iter().enumerate() {
println!("GS #{}", i + 1);
let (cx, cy) = v.split_at(v.len() / 2);
cx.iter()
.zip(cy)
.for_each(|(x, y)| println!("{:+.3} {:+.3}", x, y));
}
}
let v0 = cog
.remove_mean(Some(&cog0.valid_lenslets))
.valids(Some(&cog0.valid_lenslets));
dbg!(&cog.xy_mean);
{
println!("zero mean valid centroids");
for (i, v) in v0.iter().enumerate() {
println!("GS #{}", i + 1);
let (cx, cy) = v.split_at(v.len() / 2);
cx.iter()
.zip(cy)
.for_each(|(x, y)| println!("{:+.3} {:+.3}", x, y));
}
}
}
}