Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEOS chip gap calculation uneven #152

Merged
merged 1 commit into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Horizons orbit table query now includes, M1/2, K1/2, PC, and rotation period.

### Fixed

- NEOS Chip size calculation was slightly incorrect with regard to the placement of the
gaps between the chips.
- NEOS FOV rotation was being calculated in the ecliptic frame, whereas images will be
in the equatorial frame. Rotation is now defaulting to the equatorial frame.


## [v1.0.3]

Expand Down
12 changes: 10 additions & 2 deletions src/kete/neos.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import numpy as np
from .fov import NeosCmos, NeosVisit


__all__ = ["sunshield_rotation", "NeosCmos", "NeosVisit", "FOV_WIDTH", "FOV_HEIGHT"]


BANDS: list[float] = [4700.0, 8000.0]
"""Effective wavelength of the NC1 and NC2 bands in nm."""

FOV_WIDTH: float = 7.10
"""Expected effective field of view width in degrees"""
"""Expected effective field of view width in degrees. Approximate Value."""

FOV_HEIGHT: float = 1.68
"""Expected effective field of view height in degrees"""
"""Expected effective field of view height in degrees. Approximate Value."""

FOV_CHIP_GAP: float = 0.11
"""Expected effective gap between individual chips in degrees. Approximate Value."""

ZERO_MAGS: list[float] = [170.662, 64.13]
"""Zero point magnitude for nc1 and nc2"""
Expand Down
17 changes: 7 additions & 10 deletions src/kete/rust/fovs/definitions.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use kete_core::fov::{self, NeosBand};
use kete_core::fov::{self};
use kete_core::fov::{FovLike, SkyPatch};
use nalgebra::Vector3;
use pyo3::{exceptions, prelude::*};
Expand Down Expand Up @@ -607,7 +607,7 @@ impl PyNeosCmos {
subloop_id,
exposure_id,
cmos_id,
band.into(),
band,
))
}

Expand Down Expand Up @@ -677,11 +677,7 @@ impl PyNeosCmos {
/// Band Number
#[getter]
pub fn band(&self) -> u8 {
match self.0.band {
NeosBand::NC1 => 1,
NeosBand::NC2 => 2,
NeosBand::Undefined => 0,
}
self.0.band
}

/// Rotation angle of the FOV in degrees.
Expand Down Expand Up @@ -738,22 +734,23 @@ impl PyNeosVisit {
exposure_id: u8,
band: u8,
) -> Self {
let pointing = pointing.into_vector(crate::frame::PyFrames::Ecliptic);
let pointing = pointing.into_vector(crate::frame::PyFrames::Equatorial);
let pointing = pointing.raw.into();
let observer = observer.as_equatorial().unwrap().0;
PyNeosVisit(fov::NeosVisit::from_pointing(
x_width.to_radians(),
y_width.to_radians(),
gap_angle.to_radians(),
pointing,
rotation.to_radians(),
observer.0,
observer,
side_id,
stack_id,
quad_id,
loop_id,
subloop_id,
exposure_id,
band.into(),
band,
))
}

Expand Down
60 changes: 20 additions & 40 deletions src/kete_core/src/fov/neos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,6 @@ use crate::prelude::*;
use nalgebra::Vector3;
use serde::{Deserialize, Serialize};

/// NEOS bands
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq)]
pub enum NeosBand {
/// No Band defined.
Undefined,

/// NEOS NC1 Band.
NC1,

/// NEOS NC2 Band.
NC2,
}

/// Convert a NEOS band from u8
/// 1 is NC1 2 is NC2, everything else is Undefined.
impl From<u8> for NeosBand {
fn from(value: u8) -> Self {
match value {
1 => NeosBand::NC1,
2 => NeosBand::NC2,
_ => NeosBand::Undefined,
}
}
}

/// NEOS frame data, a single detector on a single band
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NeosCmos {
Expand Down Expand Up @@ -62,7 +37,7 @@ pub struct NeosCmos {
pub exposure_id: u8,

/// Wavelength band
pub band: NeosBand,
pub band: u8,

/// CMOS ID
/// ID number of the CMOS chip, 0, 1, 2, or 3
Expand All @@ -83,7 +58,7 @@ impl NeosCmos {
subloop_id: u8,
exposure_id: u8,
cmos_id: u8,
band: NeosBand,
band: u8,
) -> Self {
let patch =
OnSkyRectangle::new(pointing, rotation, NEOS_WIDTH, NEOS_HEIGHT, observer.frame);
Expand Down Expand Up @@ -164,7 +139,7 @@ pub struct NeosVisit {
pub exposure_id: u8,

/// Wavelength band
pub band: NeosBand,
pub band: u8,
}

impl NeosVisit {
Expand Down Expand Up @@ -235,7 +210,7 @@ impl NeosVisit {
loop_id: u8,
subloop_id: u8,
exposure_id: u8,
band: NeosBand,
band: u8,
) -> Self {
// 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
Expand All @@ -250,29 +225,34 @@ impl NeosVisit {
// orthogonal to the two existing vectors.
let up_vec = pointing.cross(&left_vec);

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

// the Y direction is bounded by 2 planes, calculate them one time
let y_top: Vector3<f64> = rotate_around(&up_vec, left_vec, y_width / 2.0);
let y_bottom: Vector3<f64> = rotate_around(&(-up_vec), left_vec, -y_width / 2.0);

let half_gap = gap_angle / 2.0;

// chip width in the x direction:
// 4 * chip_width + 3 * gap_angle = x_width
// chip_width = (x_width - 3 * gap_angle) / 4
let chip_width = (x_width - 3.0 * gap_angle) / 4.0;

// for each chip calculate the x bounds
let chip_1_a: Vector3<f64> = rotate_around(&left_vec, up_vec, -x_width / 2.0);
let chip_1_b: Vector3<f64> = -rotate_around(&left_vec, up_vec, -x_width / 4.0 - half_gap);
let chip_2_a: Vector3<f64> = rotate_around(&left_vec, up_vec, -x_width / 4.0 + half_gap);
let chip_1_b: Vector3<f64> = -rotate_around(&left_vec, up_vec, -x_width / 2.0 + chip_width);
let chip_2_a: Vector3<f64> = rotate_around(&left_vec, up_vec, -chip_width - half_gap);
let chip_2_b: Vector3<f64> = -rotate_around(&left_vec, up_vec, -half_gap);
let chip_3_a: Vector3<f64> = rotate_around(&left_vec, up_vec, half_gap);
let chip_3_b: Vector3<f64> = -rotate_around(&left_vec, up_vec, x_width / 4.0 - half_gap);
let chip_4_a: Vector3<f64> = rotate_around(&left_vec, up_vec, x_width / 4.0 + half_gap);
let chip_3_b: Vector3<f64> = -rotate_around(&left_vec, up_vec, chip_width + half_gap);
let chip_4_a: Vector3<f64> = rotate_around(&left_vec, up_vec, x_width / 2.0 - chip_width);
let chip_4_b: Vector3<f64> = -rotate_around(&left_vec, up_vec, x_width / 2.0);

// make the patches for each chip
Expand Down