use crate::spatial::DEGRA;
use crate::target::Target;
use crate::time::Time;
pub struct Observer<'a> {
pub name: Option<&'a str>,
pub lat: f64,
pub lon: f64,
pub elevation: f64, }
impl<'a> Observer<'a> {
pub fn new(lat: f64, lon: f64, elevation: f64, name: Option<&str>) -> Observer {
Observer {
name,
lat,
lon,
elevation,
}
}
pub fn local_sidereal_time(&self, time: &Time) -> f64 {
let gst = time.to_gst();
let lst = gst + self.lon + 360.0;
lst % 360.0
}
pub fn targets_airmasses(&self, targets: &Vec<Target>, times: &Vec<Time>) -> Vec<Vec<f64>> {
let lat = self.lat;
let lsts = times
.iter()
.map(|time| self.local_sidereal_time(time))
.collect::<Vec<f64>>();
let ra_array = targets.iter().map(|target| target.ra).collect::<Vec<f64>>();
let dec_array = targets
.iter()
.map(|target| target.dec)
.collect::<Vec<f64>>();
let mut airmasses = vec![vec![0.0; times.len()]; targets.len()];
for (i, _target) in targets.iter().enumerate() {
for (j, _time) in times.iter().enumerate() {
let ha = ((lsts[j] - ra_array[i]) % 360.0) * DEGRA;
let lat = lat * DEGRA;
let dec = dec_array[i] * DEGRA;
let alt = (dec.sin() * lat.sin() + dec.cos() * lat.cos() * ha.cos()).asin() / DEGRA;
let alt = alt - 0.0347 * (90.0 - alt).tan().powi(2);
let sinarg = alt + 244.0 / (165.0 + 47.0 * alt.powf(1.1));
airmasses[i][j] = 1.0 / (sinarg * DEGRA).sin();
}
}
airmasses
}
pub fn to_string(&self) -> String {
if let Some(name) = &self.name {
return format!(
"Name: {}, Lat: {}, Lon: {}, Elevation: {}",
name, self.lat, self.lon, self.elevation
);
}
format!(
"Lat: {}, Lon: {}, Elevation: {} (no name)",
self.lat, self.lon, self.elevation
)
}
pub fn sun_set_time(&self, after: Option<&Time>, solar_alt: Option<f64>) -> (Time, Time) {
let after = match after {
Some(time) => time,
None => &Time::now(),
};
let solar_alt = solar_alt.unwrap_or(-0.833);
let jd = after.to_jd();
let n = (jd - (2451545.0 + 0.0009) - 69.184 / 86400.0).ceil();
let jstar = n + 0.0009 - self.lon / 360.0;
let m = (357.5291 + 0.98560028 * jstar) % 360.0;
let m_rad = m.to_radians();
let c = 1.9148 * m_rad.sin() + 0.0200 * (2.0 * m_rad).sin() + 0.0003 * (3.0 * m_rad).sin();
let lambda = (m + c + 180.0 + 102.9372) % 360.0;
let lambda_rad = lambda.to_radians();
let jtransit = 2451545.0 + jstar + 0.0053 * m_rad.sin() - 0.0069 * (2.0 * lambda_rad).sin();
let delta_sin = lambda_rad.sin() * 23.4397_f64.to_radians().sin();
let delta_cos = delta_sin.asin().cos();
let w0_cos = ((solar_alt - 2.076 * self.elevation.sqrt() / 60.0)
.to_radians()
.sin()
- self.lat.to_radians().sin() * delta_sin)
/ (self.lat.to_radians().cos() * delta_cos);
let w0_rad = w0_cos.acos();
let w0 = w0_rad.to_degrees();
let jrise = jtransit - w0 / 360.0;
let jset = jtransit + w0 / 360.0;
let sunrise = Time::from_jd(jrise);
let sunset = Time::from_jd(jset);
(sunrise, sunset)
}
pub fn twilight_astronomical(&self, after: Option<&Time>) -> (Time, Time) {
self.sun_set_time(after, Some(-18.0))
}
pub fn twilight_nautical(&self, after: Option<&Time>) -> (Time, Time) {
self.sun_set_time(after, Some(-12.0))
}
pub fn twilight_civil(&self, after: Option<&Time>) -> (Time, Time) {
self.sun_set_time(after, Some(-6.0))
}
}
impl<'a> std::fmt::Display for Observer<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if let Some(name) = &self.name {
write!(
f,
"Name: {}, Lat: {}, Lon: {}, Elevation: {}",
name, self.lat, self.lon, self.elevation
)
} else {
write!(
f,
"Lat: {}, Lon: {}, Elevation: {} (no name)",
self.lat, self.lon, self.elevation
)
}
}
}