Skip to content

Commit

Permalink
begin adding NEOS Visits (broken)
Browse files Browse the repository at this point in the history
  • Loading branch information
dahlend committed May 13, 2024
1 parent ef2a7ef commit 23897fa
Show file tree
Hide file tree
Showing 2 changed files with 173 additions and 5 deletions.
111 changes: 111 additions & 0 deletions src/neospy/rust/fovs/definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,15 @@ pub struct PyWiseCmos(pub fov::WiseCmos);
#[pyclass(module = "neospy", frozen, name = "NeosCmos")]
#[derive(Clone, Debug)]
#[allow(clippy::upper_case_acronyms)]

pub struct PyNeosCmos(pub fov::NeosCmos);

/// Field of view of a NEOS Visit.
#[pyclass(module = "neospy", frozen, name = "NeosVisit")]
#[derive(Clone, Debug)]
#[allow(clippy::upper_case_acronyms)]
pub struct PyNeosVisit(pub fov::NeosVisit);

/// Field of view of a Single ZTF chips/quad combination.
#[pyclass(module = "neospy", frozen, name = "ZtfCcdQuad")]
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -46,6 +53,7 @@ pub enum AllowedFOV {
Rectangle(PyGenericRectangle),
ZTF(PyZtfCcdQuad),
ZTFField(PyZtfField),
NEOSVisit(PyNeosVisit),
}

impl AllowedFOV {
Expand All @@ -56,6 +64,7 @@ impl AllowedFOV {
AllowedFOV::Rectangle(fov) => fov.0.observer().jd,
AllowedFOV::ZTF(fov) => fov.0.observer().jd,
AllowedFOV::ZTFField(fov) => fov.0.observer().jd,
AllowedFOV::NEOSVisit(fov) => fov.0.observer().jd,
}
}

Expand All @@ -67,6 +76,7 @@ impl AllowedFOV {
AllowedFOV::NEOS(fov) => fov.0.get_fov(idx),
AllowedFOV::ZTF(fov) => fov.0.get_fov(idx),
AllowedFOV::ZTFField(fov) => fov.0.get_fov(idx),
AllowedFOV::NEOSVisit(fov) => fov.0.get_fov(idx),
}
}

Expand All @@ -77,6 +87,7 @@ impl AllowedFOV {
AllowedFOV::NEOS(fov) => fov::FOV::NeosCmos(fov.0),
AllowedFOV::ZTF(fov) => fov::FOV::ZtfCcdQuad(fov.0),
AllowedFOV::ZTFField(fov) => fov::FOV::ZtfField(fov.0),
AllowedFOV::NEOSVisit(fov) => fov::FOV::NeosVisit(fov.0),
}
}

Expand All @@ -87,6 +98,7 @@ impl AllowedFOV {
AllowedFOV::NEOS(fov) => fov.__repr__(),
AllowedFOV::ZTF(fov) => fov.__repr__(),
AllowedFOV::ZTFField(fov) => fov.__repr__(),
AllowedFOV::NEOSVisit(fov) => fov.__repr__(),
}
}
}
Expand All @@ -99,6 +111,7 @@ impl IntoPy<PyObject> for AllowedFOV {
Self::Rectangle(fov) => fov.into_py(py),
Self::ZTF(fov) => fov.into_py(py),
Self::ZTFField(fov) => fov.into_py(py),
Self::NEOSVisit(fov) => fov.into_py(py),
}
}
}
Expand All @@ -111,6 +124,7 @@ impl From<fov::FOV> for AllowedFOV {
fov::FOV::NeosCmos(fov) => AllowedFOV::NEOS(PyNeosCmos(fov)),
fov::FOV::GenericRectangle(fov) => AllowedFOV::Rectangle(PyGenericRectangle(fov)),
fov::FOV::ZtfField(fov) => AllowedFOV::ZTFField(PyZtfField(fov)),
fov::FOV::NeosVisit(fov) => AllowedFOV::NEOSVisit(PyNeosVisit(fov)),
_ => {
unimplemented!("Python interface doesn't support this FOV.")
}
Expand Down Expand Up @@ -335,6 +349,103 @@ impl PyNeosCmos {
)
}
}
#[pymethods]
#[allow(clippy::too_many_arguments)]
impl PyNeosVisit {
#[new]
pub fn new(
pointing: VectorLike,
rotation: f64,
observer: PyState,
side_id: u16,
stack_id: u8,
quad_id: u8,
loop_id: u8,
subloop_id: u8,
exposure_id: u8,
cmos_id: u8,
band: u8,
) -> Self {
let pointing = pointing.into_vector(crate::frame::PyFrames::Ecliptic);
let pointing = pointing.raw.into();
PyNeosVisit(fov::NeosVisit::new(
pointing,
rotation.to_radians(),
observer.0,
side_id,
stack_id,
quad_id,
loop_id,
subloop_id,
exposure_id,
cmos_id,
band,
))
}

#[getter]
pub fn observer(&self) -> PyState {
self.0.observer().clone().into()
}

#[getter]
pub fn pointing(&self) -> Vector {
Vector::new(
self.0.patch.pointing().into_inner().into(),
self.0.observer().frame.into(),
)
}

#[getter]
pub fn side_id(&self) -> u16 {
self.0.side_id
}

#[getter]
pub fn stack_id(&self) -> u8 {
self.0.stack_id
}

#[getter]
pub fn quad_id(&self) -> u8 {
self.0.quad_id
}

#[getter]
pub fn loop_id(&self) -> u8 {
self.0.loop_id
}

#[getter]
pub fn subloop_id(&self) -> u8 {
self.0.subloop_id
}

#[getter]
pub fn exposure_id(&self) -> u8 {
self.0.exposure_id
}

#[getter]
pub fn rotation(&self) -> f64 {
self.0.rotation
}

fn __repr__(&self) -> String {
format!(
"NEOSVisit(pointing={}, rotation={}, observer={}, side_id={}, stack_id={}, quad_id={}, loop_id={}, subloop_id={}, exposure_id={})",
self.pointing().__repr__(),
self.rotation(),
self.observer().__repr__(),
self.side_id(),
self.stack_id(),
self.quad_id(),
self.loop_id(),
self.subloop_id(),
self.exposure_id()
)
}
}

#[pymethods]
#[allow(clippy::too_many_arguments)]
Expand Down
67 changes: 62 additions & 5 deletions src/neospy_core/src/fov/neos.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! # NEOS field of views
use super::{closest_inside, Contains, FovLike, OnSkyRectangle, SkyPatch, FOV};
use crate::constants::{NEOS_HEIGHT, NEOS_WIDTH};
use crate::frames::rotate_around;
use crate::prelude::*;
use nalgebra::Vector3;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -101,11 +102,11 @@ impl FovLike for NeosCmos {
}
}

/// NEOS frame data, all 8 chips of a visit.
/// NEOS frame data, all 4 chips of a visit.
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NeosVisit {
/// Individual CMOS fields
chips: Box<[NeosCmos; 8]>,
chips: Box<[NeosCmos; 4]>,

/// Observer position
observer: State,
Expand Down Expand Up @@ -136,12 +137,12 @@ impl NeosVisit {
/// Construct a new NeosVisit from a list of cmos fovs.
/// These cmos fovs must be from the same metadata when appropriate.
pub fn new(chips: Vec<NeosCmos>) -> Result<Self, NEOSpyError> {
if chips.len() != 8 {
if chips.len() != 4 {
return Err(NEOSpyError::ValueError(
"Visit must contains 8 NeosCmos fovs".into(),
"Visit must contains 4 NeosCmos fovs".into(),
));
}
let chips: Box<[NeosCmos; 8]> = Box::new(chips.try_into().unwrap());
let chips: Box<[NeosCmos; 4]> = Box::new(chips.try_into().unwrap());

let first = chips.first().unwrap();

Expand Down Expand Up @@ -181,6 +182,62 @@ impl NeosVisit {
exposure_id,
})
}

/// x_width is the longer dimension in radians
pub fn from_pointing(
x_width: f64,
y_width: f64,
gap_fraction: f64,
pointing: Vector3<f64>,
rotation: f64,
observer: State,
side_id: u16,
stack_id: u8,
quad_id: u8,
loop_id: u8,
subloop_id: u8,
exposure_id: u8,
cmos_id: u8,
band: u8,
) -> Self {
let chip_x_width = (1.0 - 3.0 * gap_fraction) * x_width / 4.0;

// Rotate the Z axis to match the defined rotation angle, this vector is not
// orthogonal to the pointing vector, but is in the correct plane of the final
// up vector.
let up_vec = rotate_around(&Vector3::new(0.0, 0.0, 1.0), pointing, -rotation);

// construct the vector orthogonal to the pointing and rotate z axis vectors.
// left = cross(up, pointing)
let left_vec = pointing.cross(&up_vec);

// Given the new left vector, and the existing orthogonal pointing vector,
// construct a new up vector which is in the same plane as it was before, but now
// orthogonal to the two existing vectors.
// up = cross(pointing, left)
let up_vec = pointing.cross(&left_vec);

// +------+-+------+-+------+-+------+ ^
// | 1 |g| 2 |g| 3 |g| 4 | |
// | |a| |a| |a| | y
// | |p| |p| |p| | |
// +------+-+------+-+------+-+------+ _
// <-cf-> x ->
//
// pointing vector is in the middle of the 'a' in the central gap.

let outer: Vector3<f64> = rotate_around(&left_vec, up_vec, -lon_width / 2.0);
let n2: Vector3<f64> = rotate_around(&(-left_vec), up_vec, lon_width / 2.0);

let long_top: Vector3<f64> = rotate_around(&up_vec, left_vec, y_width / 2.0);
let long_bottom: Vector3<f64> = rotate_around(&(-up_vec), left_vec, -y_width / 2.0);

// construct the 4 normal vectors
Self {
edge_normals: [n1.into(), n2.into(), n3.into(), n4.into()],
frame,
}
}
}

impl FovLike for NeosVisit {
Expand Down

0 comments on commit 23897fa

Please sign in to comment.