use crate::*;
use std::ffi::{CStr, CString};
use std::os::raw::c_int;
#[derive(Debug, Clone)]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen(getter_with_clone))]
pub struct SwissEphError {
pub message: String,
pub code: i32,
}
impl std::fmt::Display for SwissEphError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SwissEph error ({}): {}", self.code, self.message)
}
}
impl std::error::Error for SwissEphError {}
pub type Result<T> = std::result::Result<T, SwissEphError>;
#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen)]
#[derive(Debug, Clone, Copy)]
pub struct Position {
pub longitude: f64,
pub latitude: f64,
pub distance: f64,
pub longitude_speed: f64,
pub latitude_speed: f64,
pub distance_speed: f64,
}
#[derive(Debug, Clone)]
pub struct HouseCusps {
pub cusps: [f64; 12],
pub ascendant: f64,
pub mc: f64,
pub armc: f64,
pub vertex: f64,
pub equatorial_ascendant: f64,
pub co_ascendant_koch: f64,
pub co_ascendant_munkasey: f64,
pub polar_ascendant: f64,
}
#[derive(Debug, Clone, Copy)]
pub struct NodeApsides {
pub ascending: f64,
pub descending: f64,
pub perihelion: f64,
pub aphelion: f64,
}
#[derive(Debug, Clone, Copy)]
pub struct Phenomenon {
pub phase_angle: f64,
pub phase: f64,
pub elongation: f64,
pub diameter_apparent: f64,
pub magnitude: f64,
}
#[derive(Debug, Clone, Copy)]
pub struct EclipseAttributes {
pub time_max: f64,
pub time_beg: f64,
pub time_end: f64,
pub tot_beg: f64,
pub tot_end: f64,
pub center_line: bool,
pub annular: bool,
pub total: bool,
pub eclipse_magnitude: f64,
pub saros_series: i32,
pub saros_member: i32,
}
#[derive(Debug, Clone, Copy)]
pub struct GeoPos {
pub longitude: f64,
pub latitude: f64,
pub altitude: f64,
}
#[derive(Debug, Clone, Copy)]
pub struct RiseSetEvent {
pub time: f64,
pub valid: bool,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct CalcFlags {
flags: i32,
}
impl CalcFlags {
pub fn new() -> Self {
Self { flags: SEFLG_SWIEPH }
}
pub fn with_speed(mut self) -> Self {
self.flags |= SEFLG_SPEED;
self
}
pub fn with_true_position(mut self) -> Self {
self.flags |= SEFLG_TRUEPOS;
self
}
pub fn with_no_aberration(mut self) -> Self {
self.flags |= SEFLG_NOABERR;
self
}
pub fn with_no_nutation(mut self) -> Self {
self.flags |= SEFLG_NONUT;
self
}
pub fn with_equatorial(mut self) -> Self {
self.flags |= SEFLG_EQUATORIAL;
self
}
pub fn with_heliocentric(mut self) -> Self {
self.flags |= SEFLG_HELCTR;
self
}
pub fn with_topocentric(mut self) -> Self {
self.flags |= SEFLG_TOPOCTR;
self
}
pub fn with_sidereal(mut self) -> Self {
self.flags |= SEFLG_SIDEREAL;
self
}
pub fn with_moshier(mut self) -> Self {
self.flags = (self.flags & !SEFLG_DEFAULTEPH) | SEFLG_MOSEPH;
self
}
pub fn with_swiss_ephemeris(mut self) -> Self {
self.flags = (self.flags & !SEFLG_MOSEPH & !SEFLG_JPLEPH) | SEFLG_SWIEPH;
self
}
pub fn with_jpl(mut self) -> Self {
self.flags = (self.flags & !SEFLG_DEFAULTEPH) | SEFLG_JPLEPH;
self
}
pub fn raw(&self) -> i32 {
self.flags
}
}
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum HouseSystem {
Placidus = b'P',
Koch = b'K',
Porphyrius = b'O',
Regiomontanus = b'R',
Campanus = b'C',
Equal = b'E',
WholeSign = b'W',
Alcabitus = b'B',
Morinus = b'M',
Topocentric = b'T',
Vehlow = b'V',
}
impl HouseSystem {
fn as_char(&self) -> c_int {
*self as c_int
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum Planet {
Sun = SE_SUN,
Moon = SE_MOON,
Mercury = SE_MERCURY,
Venus = SE_VENUS,
Mars = SE_MARS,
Jupiter = SE_JUPITER,
Saturn = SE_SATURN,
Uranus = SE_URANUS,
Neptune = SE_NEPTUNE,
Pluto = SE_PLUTO,
MeanNode = SE_MEAN_NODE,
TrueNode = SE_TRUE_NODE,
MeanApog = SE_MEAN_APOG,
OscuApog = SE_OSCU_APOG,
Earth = SE_EARTH,
Chiron = SE_CHIRON,
Pholus = SE_PHOLUS,
Ceres = SE_CERES,
Pallas = SE_PALLAS,
Juno = SE_JUNO,
Vesta = SE_VESTA,
IntpApog = SE_INTP_APOG,
IntpPerg = SE_INTP_PERG,
}
impl Planet {
pub fn to_int(&self) -> i32 {
*self as i32
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(i32)]
pub enum SiderealMode {
FaganBradley = SE_SIDM_FAGAN_BRADLEY,
Lahiri = SE_SIDM_LAHIRI,
DeLuce = SE_SIDM_DELUCE,
Raman = SE_SIDM_RAMAN,
Ushashashi = SE_SIDM_USHASHASHI,
Krishnamurti = SE_SIDM_KRISHNAMURTI,
DjwhalKhul = SE_SIDM_DJWHAL_KHUL,
Yukteshwar = SE_SIDM_YUKTESHWAR,
JNBhasin = SE_SIDM_JN_BHASIN,
BabylKugler1 = SE_SIDM_BABYL_KUGLER1,
BabylKugler2 = SE_SIDM_BABYL_KUGLER2,
BabylKugler3 = SE_SIDM_BABYL_KUGLER3,
BabylHuber = SE_SIDM_BABYL_HUBER,
BabylEtpsc = SE_SIDM_BABYL_ETPSC,
Aldebaran15Tau = SE_SIDM_ALDEBARAN_15TAU,
Hipparchos = SE_SIDM_HIPPARCHOS,
Sassanian = SE_SIDM_SASSANIAN,
Galcent0Sag = SE_SIDM_GALCENT_0SAG,
J2000 = SE_SIDM_J2000,
J1900 = SE_SIDM_J1900,
B1950 = SE_SIDM_B1950,
Suryasiddhanta = SE_SIDM_SURYASIDDHANTA,
SuryasiddhantaMsun = SE_SIDM_SURYASIDDHANTA_MSUN,
Aryabhata = SE_SIDM_ARYABHATA,
AryabhataMsun = SE_SIDM_ARYABHATA_MSUN,
SsRevati = SE_SIDM_SS_REVATI,
SsCitra = SE_SIDM_SS_CITRA,
TrueCitra = SE_SIDM_TRUE_CITRA,
TrueRevati = SE_SIDM_TRUE_REVATI,
TruePushya = SE_SIDM_TRUE_PUSHYA,
GalcentRgilbrand = SE_SIDM_GALCENT_RGILBRAND,
GalequIau1958 = SE_SIDM_GALEQU_IAU1958,
GalequTrue = SE_SIDM_GALEQU_TRUE,
GalequMula = SE_SIDM_GALEQU_MULA,
GalalignMardyks = SE_SIDM_GALALIGN_MARDYKS,
TrueMula = SE_SIDM_TRUE_MULA,
GalcentMulaWilhelm = SE_SIDM_GALCENT_MULA_WILHELM,
Aryabhata522 = SE_SIDM_ARYABHATA_522,
BabylBritton = SE_SIDM_BABYL_BRITTON,
TrueSheoran = SE_SIDM_TRUE_SHEORAN,
GalcentCochrane = SE_SIDM_GALCENT_COCHRANE,
GalequFiorenza = SE_SIDM_GALEQU_FIORENZA,
ValensMoon = SE_SIDM_VALENS_MOON,
Lahiri1940 = SE_SIDM_LAHIRI_1940,
LahiriVp285 = SE_SIDM_LAHIRI_VP285,
KrishnamurtiVp291 = SE_SIDM_KRISHNAMURTI_VP291,
LahiriIcrc = SE_SIDM_LAHIRI_ICRC,
User = SE_SIDM_USER,
}
impl SiderealMode {
pub fn to_int(&self) -> i32 {
*self as i32
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct RiseTransFlags {
flags: i32,
}
impl RiseTransFlags {
pub fn new() -> Self {
Self { flags: 0 }
}
pub fn with_rise(mut self) -> Self {
self.flags |= SE_CALC_RISE;
self
}
pub fn with_set(mut self) -> Self {
self.flags |= SE_CALC_SET;
self
}
pub fn with_mtransit(mut self) -> Self {
self.flags |= SE_CALC_MTRANSIT;
self
}
pub fn with_itransit(mut self) -> Self {
self.flags |= SE_CALC_ITRANSIT;
self
}
pub fn with_disc_center(mut self) -> Self {
self.flags |= SE_BIT_DISC_CENTER;
self
}
pub fn with_no_refraction(mut self) -> Self {
self.flags |= SE_BIT_NO_REFRACTION;
self
}
pub fn raw(&self) -> i32 {
self.flags
}
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen)]
pub fn set_ephe_path(path: &str) {
let c_path = CString::new(path).unwrap();
unsafe {
swe_set_ephe_path(c_path.as_ptr());
}
}
pub fn set_topo(longitude: f64, latitude: f64, altitude: f64) {
unsafe {
swe_set_topo(longitude, latitude, altitude);
}
}
pub fn set_sidereal_mode(mode: SiderealMode) {
unsafe {
swe_set_sid_mode(mode.to_int(), 0.0, 0.0);
}
}
pub fn close() {
unsafe {
swe_close();
}
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen)]
pub fn version() -> String {
let mut buf = [0i8; 256];
unsafe {
swe_version(buf.as_mut_ptr());
CStr::from_ptr(buf.as_ptr()).to_string_lossy().into_owned()
}
}
pub fn julday(year: i32, month: i32, day: i32, hour: f64) -> f64 {
unsafe { swe_julday(year, month, day, hour, SE_GREG_CAL) }
}
pub fn revjul(jd: f64) -> (i32, i32, i32, f64) {
let mut year = 0;
let mut month = 0;
let mut day = 0;
let mut hour = 0.0;
unsafe {
swe_revjul(jd, SE_GREG_CAL, &mut year, &mut month, &mut day, &mut hour);
}
(year, month, day, hour)
}
pub fn deltat(jd: f64) -> f64 {
unsafe { swe_deltat(jd) }
}
pub fn sidereal_time(jd_ut: f64) -> f64 {
unsafe { swe_sidtime(jd_ut) }
}
pub fn calc(jd: f64, planet: Planet, flags: CalcFlags) -> Result<Position> {
let mut xx = [0.0f64; 6];
let mut serr = [0i8; 256];
let ret = unsafe {
swe_calc(jd, planet.to_int(), flags.raw(), xx.as_mut_ptr(), serr.as_mut_ptr())
};
if ret < 0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy()
.into_owned();
return Err(SwissEphError { message: msg, code: ret });
}
Ok(Position {
longitude: xx[0],
latitude: xx[1],
distance: xx[2],
longitude_speed: xx[3],
latitude_speed: xx[4],
distance_speed: xx[5],
})
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen::prelude::wasm_bindgen)]
pub fn calc_ut(jd_ut: f64, planet: i32, flags: i32) -> std::result::Result<Position, SwissEphError> {
let mut xx = [0.0f64; 6];
let mut serr = [0i8; 256];
let ret = unsafe {
swe_calc_ut(jd_ut, planet, flags, xx.as_mut_ptr(), serr.as_mut_ptr())
};
if ret < 0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy()
.into_owned();
return Err(SwissEphError { message: msg, code: ret });
}
Ok(Position {
longitude: xx[0],
latitude: xx[1],
distance: xx[2],
longitude_speed: xx[3],
latitude_speed: xx[4],
distance_speed: xx[5],
})
}
pub fn calc_star(jd: f64, star: &str, flags: CalcFlags) -> Result<(String, Position)> {
let mut xx = [0.0f64; 6];
let mut serr = [0i8; 256];
let mut star_buf = [0i8; 512];
let c_star = CString::new(star).map_err(|e| SwissEphError {
message: format!("Invalid star name: {}", e),
code: -1,
})?;
let bytes = c_star.as_bytes_with_nul();
if bytes.len() > 255 {
return Err(SwissEphError {
message: "Star name too long".to_string(),
code: -1,
});
}
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), star_buf.as_mut_ptr() as *mut u8, bytes.len());
}
let ret = unsafe {
swe_fixstar2(star_buf.as_mut_ptr(), jd, flags.raw(), xx.as_mut_ptr(), serr.as_mut_ptr())
};
if ret < 0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy()
.into_owned();
return Err(SwissEphError { message: msg, code: ret });
}
let returned_name = unsafe { CStr::from_ptr(star_buf.as_ptr()) }
.to_string_lossy()
.into_owned();
Ok((returned_name, Position {
longitude: xx[0],
latitude: xx[1],
distance: xx[2],
longitude_speed: xx[3],
latitude_speed: xx[4],
distance_speed: xx[5],
}))
}
pub fn rise_trans(
jd_ut: f64,
planet: Planet,
star_name: Option<&str>,
geopos: GeoPos,
flags: RiseTransFlags,
) -> Result<f64> {
let mut tret = 0.0f64;
let mut serr = [0i8; 256];
let mut dgeo = [geopos.longitude, geopos.latitude, geopos.altitude];
let mut star_buf = [0i8; 512];
if let Some(name) = star_name {
let c_star = CString::new(name).map_err(|e| SwissEphError {
message: format!("Invalid star name: {}", e),
code: -1,
})?;
let bytes = c_star.as_bytes_with_nul();
if bytes.len() < 256 {
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), star_buf.as_mut_ptr() as *mut u8, bytes.len());
}
}
}
let star_ptr = if star_name.is_some() { star_buf.as_mut_ptr() } else { std::ptr::null_mut() };
let atpress = 1013.25;
let attemp = 10.0;
let ret = unsafe {
swe_rise_trans(
jd_ut,
planet.to_int(),
star_ptr,
0, flags.raw(),
dgeo.as_mut_ptr(),
atpress,
attemp,
&mut tret,
serr.as_mut_ptr()
)
};
if ret < 0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy()
.into_owned();
return Err(SwissEphError { message: msg, code: ret });
} else if ret != 0 {
return Err(SwissEphError { message: "Event not found (e.g. circumpolar)".to_string(), code: -2 });
}
Ok(tret)
}
pub fn solar_eclipse_where(jd: f64, flags: i32) -> Result<(f64, f64, f64, f64)> {
let mut geopos = [0.0; 10];
let mut attr = [0.0; 20];
let mut serr = [0i8; 256];
let ret = unsafe {
swe_sol_eclipse_where(jd, flags, geopos.as_mut_ptr(), attr.as_mut_ptr(), serr.as_mut_ptr())
};
if ret < 0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy()
.into_owned();
return Err(SwissEphError { message: msg, code: ret });
}
Ok((geopos[0], geopos[1], attr[0], attr[1]))
}
pub fn solar_eclipse_when_loc(
jd_start: f64,
flags: i32,
geopos: GeoPos,
backward: bool
) -> Result<(f64, EclipseAttributes)> {
let mut tret = [0.0; 10];
let mut attr = [0.0; 20];
let mut serr = [0i8; 256];
let mut dgeo = [geopos.longitude, geopos.latitude, geopos.altitude];
let ret = unsafe {
swe_sol_eclipse_when_loc(
jd_start,
flags,
dgeo.as_mut_ptr(),
tret.as_mut_ptr(),
attr.as_mut_ptr(),
backward as i32,
serr.as_mut_ptr()
)
};
if ret < 0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy()
.into_owned();
return Err(SwissEphError { message: msg, code: ret });
}
Ok((tret[0], EclipseAttributes {
time_max: tret[0],
time_beg: tret[1],
time_end: tret[2],
tot_beg: tret[3],
tot_end: tret[4],
center_line: attr[0] != 0.0,
annular: (ret & SE_ECL_ANNULAR) != 0,
total: (ret & SE_ECL_TOTAL) != 0,
eclipse_magnitude: attr[8],
saros_series: attr[10] as i32,
saros_member: attr[11] as i32,
}))
}
pub fn heliacal_event(
jd_start: f64,
geopos: GeoPos,
datm: [f64; 4],
dobs: [f64; 6],
object: &str,
event_type: i32,
flags: i32
) -> Result<f64> {
let mut dret = [0.0; 50];
let mut serr = [0i8; 256];
let mut dgeo = [geopos.longitude, geopos.latitude, geopos.altitude];
let mut datm_mut = datm; let mut dobs_mut = dobs;
let c_obj = CString::new(object).unwrap();
let mut obj_buf = [0i8; 256];
let bytes = c_obj.as_bytes_with_nul();
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), obj_buf.as_mut_ptr() as *mut u8, bytes.len());
}
let ret = unsafe {
swe_heliacal_ut(
jd_start,
dgeo.as_mut_ptr(),
datm_mut.as_mut_ptr(),
dobs_mut.as_mut_ptr(),
obj_buf.as_mut_ptr(),
event_type,
flags,
dret.as_mut_ptr(),
serr.as_mut_ptr()
)
};
if ret < 0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy()
.into_owned();
return Err(SwissEphError { message: msg, code: ret });
}
Ok(dret[0])
}
pub fn azimuth_altitude(
jd_ut: f64,
flags: i32,
geopos: GeoPos,
coord: Position
) -> Result<(f64, f64)> {
let mut med_geopos = [geopos.longitude, geopos.latitude, geopos.altitude];
let mut xin = [coord.longitude, coord.latitude, coord.distance];
let mut xaz = [0.0; 3];
unsafe {
swe_azalt(
jd_ut,
flags,
med_geopos.as_mut_ptr(),
1013.25, 10.0, xin.as_mut_ptr(),
xaz.as_mut_ptr()
);
}
Ok((xaz[0], xaz[1]))
}
pub fn lunar_eclipse_when_loc(
jd_start: f64,
flags: i32,
geopos: GeoPos,
backward: bool
) -> Result<(f64, EclipseAttributes)> {
let mut tret = [0.0; 10];
let mut attr = [0.0; 20];
let mut serr = [0i8; 256];
let mut dgeo = [geopos.longitude, geopos.latitude, geopos.altitude];
let ret = unsafe {
swe_lun_eclipse_when_loc(
jd_start,
flags,
dgeo.as_mut_ptr(),
tret.as_mut_ptr(),
attr.as_mut_ptr(),
backward as i32,
serr.as_mut_ptr()
)
};
if ret < 0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy()
.into_owned();
return Err(SwissEphError { message: msg, code: ret });
}
Ok((tret[0], EclipseAttributes {
time_max: tret[0],
time_beg: tret[1],
time_end: tret[2],
tot_beg: tret[3],
tot_end: tret[4],
center_line: false, annular: false, total: (ret & SE_ECL_TOTAL) != 0,
eclipse_magnitude: attr[8],
saros_series: attr[10] as i32,
saros_member: attr[11] as i32,
}))
}
pub fn utc_to_jd(year: i32, month: i32, day: i32, hour: i32, min: i32, sec: f64, gregflag: i32) -> Result<(f64, f64)> {
let mut dret = [0.0; 2];
let mut serr = [0i8; 256];
let ret = unsafe {
swe_utc_to_jd(year, month, day, hour, min, sec, gregflag, dret.as_mut_ptr(), serr.as_mut_ptr())
};
if ret < 0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy()
.into_owned();
return Err(SwissEphError { message: msg, code: ret });
}
Ok((dret[0], dret[1]))
}
pub fn jdet_to_utc(jd_et: f64, gregflag: i32) -> (i32, i32, i32, i32, i32, f64) {
let mut year = 0;
let mut month = 0;
let mut day = 0;
let mut hour = 0;
let mut min = 0;
let mut sec = 0.0;
unsafe {
swe_jdet_to_utc(jd_et, gregflag, &mut year, &mut month, &mut day, &mut hour, &mut min, &mut sec);
}
(year, month, day, hour, min, sec)
}
pub fn coordinate_transform(position: Position, obliquity: f64) -> Position {
let mut xin = [position.longitude, position.latitude, position.distance];
let mut xout = [0.0; 3];
unsafe {
swe_cotrans(xin.as_mut_ptr(), xout.as_mut_ptr(), obliquity);
}
Position {
longitude: xout[0],
latitude: xout[1],
distance: xout[2],
longitude_speed: 0.0, latitude_speed: 0.0,
distance_speed: 0.0,
}
}
pub fn house_pos(armc: f64, geolat: f64, eps: f64, hsys: char, xpin: [f64; 2]) -> Result<f64> {
let mut serr = [0i8; 256];
let mut xpin_mut = xpin;
let ret = unsafe {
swe_house_pos(armc, geolat, eps, hsys as i32, xpin_mut.as_mut_ptr(), serr.as_mut_ptr())
};
if ret == 0.0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy();
if !msg.is_empty() {
return Err(SwissEphError { message: msg.into_owned(), code: -1 });
}
}
Ok(ret)
}
pub fn gauquelin_sector(
jd_ut: f64,
ipl: i32,
star_name: Option<&str>,
flags: i32,
imeth: i32,
geopos: GeoPos,
atpress: f64,
attemp: f64
) -> Result<f64> {
let mut dgsect = [0.0; 5];
let mut serr = [0i8; 256];
let mut geopos_arr = [geopos.longitude, geopos.latitude, geopos.altitude];
let mut star_buf = [0i8; 256];
if let Some(name) = star_name {
let c_star = CString::new(name).map_err(|e| SwissEphError {
message: format!("Invalid star name: {}", e),
code: -1,
})?;
let bytes = c_star.as_bytes_with_nul();
if bytes.len() < 256 {
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), star_buf.as_mut_ptr() as *mut u8, bytes.len());
}
}
}
let ret = unsafe {
swe_gauquelin_sector(
jd_ut,
ipl,
star_buf.as_mut_ptr(),
flags,
imeth,
geopos_arr.as_mut_ptr(),
atpress,
attemp,
dgsect.as_mut_ptr(),
serr.as_mut_ptr()
)
};
if ret < 0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy()
.into_owned();
return Err(SwissEphError { message: msg, code: ret });
}
Ok(dgsect[0])
}
pub fn refraction(inalt: f64, atpress: f64, attemp: f64, calc_flag: i32) -> f64 {
unsafe {
swe_refrac(inalt, atpress, attemp, calc_flag)
}
}
pub fn get_ayanamsa(jd: f64) -> f64 {
unsafe { swe_get_ayanamsa(jd) }
}
pub fn nodes_apsides(jd: f64, planet: Planet, flags: CalcFlags, method: i32) -> Result<NodeApsides> {
let mut xnasc = [0.0; 6];
let mut xndsc = [0.0; 6];
let mut xperi = [0.0; 6];
let mut xaphe = [0.0; 6];
let mut serr = [0i8; 256];
let ret = unsafe {
swe_nod_aps(
jd,
planet.to_int(),
flags.raw(),
method,
xnasc.as_mut_ptr(),
xndsc.as_mut_ptr(),
xperi.as_mut_ptr(),
xaphe.as_mut_ptr(),
serr.as_mut_ptr(),
)
};
if ret < 0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy()
.into_owned();
return Err(SwissEphError { message: msg, code: ret });
}
Ok(NodeApsides {
ascending: xnasc[0],
descending: xndsc[0],
perihelion: xperi[0],
aphelion: xaphe[0],
})
}
pub fn phenomena(jd: f64, planet: Planet, flags: CalcFlags) -> Result<Phenomenon> {
let mut attr = [0.0; 20];
let mut serr = [0i8; 256];
let ret = unsafe {
swe_pheno(jd, planet.to_int(), flags.raw(), attr.as_mut_ptr(), serr.as_mut_ptr())
};
if ret < 0 {
let msg = unsafe { CStr::from_ptr(serr.as_ptr()) }
.to_string_lossy()
.into_owned();
return Err(SwissEphError { message: msg, code: ret });
}
Ok(Phenomenon {
phase_angle: attr[0],
phase: attr[1],
elongation: attr[2],
diameter_apparent: attr[3],
magnitude: attr[4],
})
}
pub fn houses(jd_ut: f64, latitude: f64, longitude: f64, system: HouseSystem) -> Result<HouseCusps> {
let mut cusps = [0.0f64; 13];
let mut ascmc = [0.0f64; 10];
let ret = unsafe {
swe_houses(
jd_ut,
latitude,
longitude,
system.as_char(),
cusps.as_mut_ptr(),
ascmc.as_mut_ptr(),
)
};
if ret < 0 {
return Err(SwissEphError {
message: "House calculation failed".to_string(),
code: ret,
});
}
let mut result_cusps = [0.0f64; 12];
for i in 0..12 {
result_cusps[i] = cusps[i + 1];
}
Ok(HouseCusps {
cusps: result_cusps,
ascendant: ascmc[0],
mc: ascmc[1],
armc: ascmc[2],
vertex: ascmc[3],
equatorial_ascendant: ascmc[4],
co_ascendant_koch: ascmc[5],
co_ascendant_munkasey: ascmc[6],
polar_ascendant: ascmc[7],
})
}
pub fn get_planet_name(planet: Planet) -> String {
let mut buf = [0i8; 256];
unsafe {
swe_get_planet_name(planet.to_int(), buf.as_mut_ptr());
CStr::from_ptr(buf.as_ptr()).to_string_lossy().into_owned()
}
}
pub fn normalize_degrees(deg: f64) -> f64 {
unsafe { swe_degnorm(deg) }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_safe_julday() {
let jd = julday(2000, 1, 1, 12.0);
assert!((jd - 2451545.0).abs() < 0.0001);
}
#[test]
fn test_safe_revjul() {
let (year, month, day, hour) = revjul(2451545.0);
assert_eq!(year, 2000);
assert_eq!(month, 1);
assert_eq!(day, 1);
assert!((hour - 12.0).abs() < 0.0001);
}
#[test]
fn test_safe_calc() {
let jd = 2451545.0; let flags = CalcFlags::new().with_speed();
let pos = calc(jd, Planet::Sun, flags).unwrap();
assert!(pos.longitude > 270.0 && pos.longitude < 290.0);
assert!(pos.latitude.abs() < 1.0);
}
#[test]
fn test_safe_houses() {
let jd_ut = 2451545.0;
let cusps = houses(jd_ut, 47.3769, 8.5417, HouseSystem::Placidus).unwrap();
assert!(cusps.ascendant >= 0.0 && cusps.ascendant < 360.0);
assert!(cusps.mc >= 0.0 && cusps.mc < 360.0);
}
#[test]
fn test_version() {
let v = version();
assert!(!v.is_empty());
assert!(v.chars().next().unwrap().is_ascii_digit());
}
}