use std::fmt;
use crate::{
MJDTT,
coordinates::equatorial::EquCoord,
observation_dataset::{ObsId, index::ObsIndex},
observer::dataset::ObserverId,
photometry::Photometry,
};
#[derive(Debug, Clone)]
pub struct ObservationInput {
pub id: ObsId,
pub equ_coord: EquCoord,
pub photometry: Photometry,
pub mjd_tt: MJDTT,
pub observer: Option<ObserverId>,
}
impl ObservationInput {
pub fn new(
id: ObsId,
equ_coord: EquCoord,
photometry: Photometry,
mjd_tt: MJDTT,
observer: Option<ObserverId>,
) -> Self {
Self {
id,
equ_coord,
photometry,
mjd_tt,
observer,
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Observation {
index: ObsIndex,
pub(crate) id: ObsId,
pub(crate) equ_coord: EquCoord,
pub(crate) photometry: Photometry,
pub(crate) mjd_tt: MJDTT,
pub(crate) observer: Option<ObserverId>,
}
impl PartialEq for Observation {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl Eq for Observation {}
impl Ord for Observation {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.mjd_tt
.total_cmp(&other.mjd_tt)
.then(self.id.cmp(&other.id))
}
}
impl PartialOrd for Observation {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl fmt::Display for Observation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if f.alternate() {
write!(
f,
"[{:.6} MJD] {} | {}",
self.mjd_tt, self.equ_coord, self.photometry
)
} else {
writeln!(f, "Observation")?;
writeln!(f, " Epoch : {:.6} MJD (TT)", self.mjd_tt)?;
writeln!(f, " Position : {}", self.equ_coord)?;
writeln!(f, " Photometry: {}", self.photometry)?;
match &self.observer {
Some(obs) => write!(f, " Observer : {obs}"),
None => write!(f, " Observer : unknown"),
}
}
}
}
impl Observation {
pub(crate) fn place(input: ObservationInput, index: ObsIndex) -> Self {
Self {
index,
id: input.id,
equ_coord: input.equ_coord,
photometry: input.photometry,
mjd_tt: input.mjd_tt,
observer: input.observer,
}
}
pub(crate) fn reindex(self, new_idx: ObsIndex) -> Self {
Self {
index: new_idx,
..self
}
}
pub fn index(&self) -> ObsIndex {
self.index
}
pub fn id(&self) -> &ObsId {
&self.id
}
pub fn observer_id(&self) -> Option<&ObserverId> {
self.observer.as_ref()
}
pub fn equ_coord(&self) -> &EquCoord {
&self.equ_coord
}
pub fn photometry(&self) -> &Photometry {
&self.photometry
}
pub fn mjd_tt(&self) -> MJDTT {
self.mjd_tt
}
}
#[cfg(test)]
mod observation_tests {
use super::*;
use crate::{
coordinates::equatorial::EquCoord,
observation_dataset::index::ObsIndex,
observer::dataset::ObserverId,
photometry::{Filter, Photometry},
};
fn make_photometry() -> Photometry {
Photometry {
magnitude: 15.0,
error: 0.1,
filter: Filter::String("V".to_string()),
}
}
fn make_input(id: u64, mjd: f64) -> ObservationInput {
ObservationInput::new(
id,
EquCoord::new(0.5, 1e-5, 0.2, 1e-5),
make_photometry(),
mjd,
None,
)
}
fn make_obs(id: u64, mjd: f64) -> Observation {
Observation::place(make_input(id, mjd), 0)
}
#[test]
fn place_sets_correct_index() {
let input = make_input(42, 60000.0);
let obs = Observation::place(input, 7);
assert_eq!(obs.index(), 7);
}
#[test]
fn getters_return_correct_values() {
let obs = make_obs(99, 60123.5);
assert_eq!(*obs.id(), 99);
assert_eq!(obs.mjd_tt(), 60123.5);
assert_eq!(obs.equ_coord().ra, 0.5);
assert_eq!(obs.photometry().magnitude, 15.0);
}
#[test]
fn observer_field_preserved() {
let input = ObservationInput::new(
1,
EquCoord::new(0.0, 1e-5, 0.0, 1e-5),
make_photometry(),
60000.0,
Some(ObserverId::MpcCode(*b"T05")),
);
let obs = Observation::place(input, 0);
assert_eq!(*obs.id(), 1);
}
#[test]
fn eq_by_id() {
let a = make_obs(7, 60000.0);
let b = make_obs(7, 60001.0); assert_eq!(a, b);
}
#[test]
fn ne_different_ids() {
let a = make_obs(1, 60000.0);
let b = make_obs(2, 60000.0);
assert_ne!(a, b);
}
#[test]
fn ord_by_epoch() {
let earlier = make_obs(1, 59000.0);
let later = make_obs(2, 60000.0);
assert!(earlier < later);
}
#[test]
fn ord_tie_broken_by_id() {
let a = make_obs(1, 60000.0);
let b = make_obs(2, 60000.0);
assert!(a < b);
}
#[test]
fn clone_is_equal() {
let obs = make_obs(5, 60000.0);
let cloned = obs.clone();
assert_eq!(obs, cloned);
assert_eq!(obs.mjd_tt(), cloned.mjd_tt());
}
#[test]
fn index_is_set_by_place() {
let input = make_input(1, 60000.0);
let obs = Observation::place(input, ObsIndex::from(3usize));
assert_eq!(obs.index(), ObsIndex::from(3usize));
}
}