use super::error::{GaiaDr3Error, Result};
use super::photometry::{PhotonFlux, StellarPhotometryModel};
use super::quality::GaiaDr3QualityFlags;
use super::raw::GaiaDr3RawSourceRow;
use crate::astro::proper_motion::ProperMotion;
use crate::qtty::{
angular_rate::AngularRate, unit, Degrees, JulianYears, KmPerSeconds, MilliArcsecond,
MilliArcseconds,
};
type GaiaProperMotionRate = AngularRate<MilliArcsecond, unit::JulianYear>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct GaiaSourceId(u64);
impl GaiaSourceId {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn value(self) -> u64 {
self.0
}
}
#[derive(Debug, Clone)]
pub struct GaiaDr3Astrometry {
pub direction: crate::coordinates::spherical::direction::ICRS,
pub epoch: JulianYears,
pub proper_motion: Option<ProperMotion>,
pub parallax: Option<MilliArcseconds>,
pub radial_velocity: Option<KmPerSeconds>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct GaiaDr3Photometry {
pub phot_g_mean_mag: Option<f64>,
pub phot_bp_mean_mag: Option<f64>,
pub phot_rp_mean_mag: Option<f64>,
}
#[derive(Debug, Clone)]
pub struct GaiaDr3Source {
pub source_id: GaiaSourceId,
pub astrometry: GaiaDr3Astrometry,
pub photometry: GaiaDr3Photometry,
pub quality: GaiaDr3QualityFlags,
}
impl TryFrom<GaiaDr3RawSourceRow> for GaiaDr3Source {
type Error = GaiaDr3Error;
fn try_from(row: GaiaDr3RawSourceRow) -> Result<Self> {
ensure_finite("ra_deg", row.ra_deg)?;
ensure_finite("dec_deg", row.dec_deg)?;
if !(0.0..360.0).contains(&row.ra_deg) {
return Err(GaiaDr3Error::RightAscensionOutOfRange { value: row.ra_deg });
}
if !(-90.0..=90.0).contains(&row.dec_deg) {
return Err(GaiaDr3Error::DeclinationOutOfRange { value: row.dec_deg });
}
let proper_motion = match (row.pmra_mas_per_yr, row.pmdec_mas_per_yr) {
(Some(pmra), Some(pmdec)) => Some(ProperMotion::from_mu_alpha_star(
gaia_proper_motion_rate("pmra", pmra)?,
gaia_proper_motion_rate("pmdec", pmdec)?,
)),
(None, None) => None,
(Some(_), None) => return Err(GaiaDr3Error::MissingRequiredField { field: "pmdec" }),
(None, Some(_)) => return Err(GaiaDr3Error::MissingRequiredField { field: "pmra" }),
};
let photometry = GaiaDr3Photometry {
phot_g_mean_mag: validate_optional_finite("phot_g_mean_mag", row.phot_g_mean_mag)?,
phot_bp_mean_mag: validate_optional_finite("phot_bp_mean_mag", row.phot_bp_mean_mag)?,
phot_rp_mean_mag: validate_optional_finite("phot_rp_mean_mag", row.phot_rp_mean_mag)?,
};
Ok(Self {
source_id: GaiaSourceId::new(row.source_id),
astrometry: GaiaDr3Astrometry {
direction: crate::coordinates::spherical::Direction::<
crate::coordinates::frames::ICRS,
>::new(
Degrees::new(row.ra_deg), Degrees::new(row.dec_deg)
),
epoch: julian_year_epoch("ref_epoch_jyr", row.ref_epoch_jyr)?,
proper_motion,
parallax: validate_optional_finite("parallax_mas", row.parallax_mas)?
.map(MilliArcseconds::new),
radial_velocity: validate_optional_finite(
"radial_velocity_km_s",
row.radial_velocity_km_s,
)?
.map(KmPerSeconds::new),
},
photometry,
quality: row.quality,
})
}
}
#[derive(Debug, Clone)]
pub struct PassbandIntegratedStellarSource {
pub source_id: u64,
pub direction: crate::coordinates::spherical::direction::ICRS,
pub epoch: JulianYears,
pub photon_flux: PhotonFlux,
pub photometry_model: StellarPhotometryModel,
}
impl PassbandIntegratedStellarSource {
pub fn from_gaia_source(
source: &GaiaDr3Source,
photon_flux: PhotonFlux,
photometry_model: StellarPhotometryModel,
) -> Self {
Self {
source_id: source.source_id.value(),
direction: source.astrometry.direction,
epoch: source.astrometry.epoch,
photon_flux,
photometry_model,
}
}
}
fn julian_year_epoch(field: &'static str, value: f64) -> Result<JulianYears> {
ensure_finite(field, value)?;
Ok(JulianYears::new(value))
}
fn gaia_proper_motion_rate(field: &'static str, value: f64) -> Result<GaiaProperMotionRate> {
ensure_finite(field, value)?;
Ok(GaiaProperMotionRate::new(value))
}
fn validate_optional_finite(field: &'static str, value: Option<f64>) -> Result<Option<f64>> {
if let Some(value) = value {
ensure_finite(field, value)?;
}
Ok(value)
}
fn ensure_finite(field: &'static str, value: f64) -> Result<()> {
if value.is_finite() {
Ok(())
} else {
Err(GaiaDr3Error::NonFinite { field, value })
}
}