libswe_sys/swerust/swe03/
handler.rs

1use crate::raw;
2use crate::sweconst::Bodies;
3use crate::swerust;
4use std::ffi::{CStr, CString};
5
6/*
7 * 3. The functions swe_calc_ut() and swe_calc()
8 *
9 * Before calling one of these functions or any other Swiss Ephemeris function,
10 * it is strongly recommended to call the function swe_set_ephe_path(). Even if
11 * you don’t want to set an ephemeris path and use the Moshier ephemeris, it is
12 * nevertheless recommended to call swe_set_ephe_path(NULL), because this
13 * function makes important initializations. If you don’t do that, the Swiss
14 * Ephemeris may work but the results may be not 100% consistent.
15 */
16
17/*
18 * xx
19 *
20 * Ecliptic position               Equatorial position (SEFLG_EQUATORIAL)
21 * Longitude                       right ascension
22 * Latitude                        declination
23 * Distance in AU                  distance in AU
24 * Speed in longitude (deg/day)    speed in right ascension (deg/day)
25 * Speed in latitude (deg/day)     speed in declination (deg/day)
26 * Speed in distance (AU/day)      speed in distance (AU/day)
27 */
28#[derive(Debug)]
29pub struct CalcUtResult {
30    pub longitude: f64,
31    pub latitude: f64,
32    pub distance_au: f64,
33    pub speed_longitude: f64,
34    pub speed_latitude: f64,
35    pub speed_distance_au: f64,
36    pub status: i32,
37    pub serr: String,
38}
39
40pub fn calc_ut(tjd_ut: f64, ipl: Bodies, iflag: i32) -> CalcUtResult {
41    let mut xx: [f64; 6] = [0.0; 6];
42    let mut serr = [0; 255];
43    let result;
44    result = unsafe {
45        let p_xx = xx.as_mut_ptr();
46        let p_serr = serr.as_mut_ptr();
47        let status;
48        if ipl == Bodies::SouthNode {
49            status = raw::swe_calc_ut(
50                tjd_ut,
51                Bodies::TrueNode as i32,
52                iflag,
53                p_xx,
54                p_serr,
55            );
56        } else {
57            status = raw::swe_calc_ut(tjd_ut, ipl as i32, iflag, p_xx, p_serr);
58        }
59        let s_serr = CString::from(CStr::from_ptr(p_serr))
60            .to_str()
61            .unwrap()
62            .to_string();
63        if ipl == Bodies::SouthNode {
64            xx[0] += 180.0;
65            if xx[0] >= 360.0 {
66                xx[0] -= 360.0;
67            }
68        }
69        CalcUtResult {
70            longitude: xx[0],
71            latitude: xx[1],
72            distance_au: xx[2],
73            speed_longitude: xx[3],
74            speed_latitude: xx[4],
75            speed_distance_au: xx[5],
76            serr: s_serr,
77            status,
78        }
79    };
80    result
81}
82
83/// Fortuna Part
84/// Only lng is valid, the speed is unknow because this object is calculated
85pub fn calc_ut_fp(
86    tjd_ut: f64,
87    geolat: f64,
88    geolong: f64,
89    hsys: char,
90    iflag: i32,
91) -> CalcUtResult {
92    let ipl = Bodies::FortunaPart;
93    let mut xx: [f64; 6] = [0.0; 6];
94    let mut serr = [0; 255];
95    let result = unsafe {
96        let p_xx = xx.as_mut_ptr();
97        let p_serr = serr.as_mut_ptr();
98        let status = raw::swe_calc_ut(tjd_ut, ipl as i32, iflag, p_xx, p_serr);
99        let s_serr = CString::from(CStr::from_ptr(p_serr))
100            .to_str()
101            .unwrap()
102            .to_string();
103        let mut xx_sun: [f64; 6] = [0.0; 6];
104        let mut xx_moon: [f64; 6] = [0.0; 6];
105        let p_xx_sun = xx_sun.as_mut_ptr();
106        let p_serr_sun = serr.as_mut_ptr();
107        let _status_sun = raw::swe_calc_ut(
108            tjd_ut,
109            Bodies::Sun as i32,
110            iflag,
111            p_xx_sun,
112            p_serr_sun,
113        );
114        let p_xx_moon = xx_moon.as_mut_ptr();
115        let p_serr_moon = serr.as_mut_ptr();
116        let _status_moon = raw::swe_calc_ut(
117            tjd_ut,
118            Bodies::Moon as i32,
119            iflag,
120            p_xx_moon,
121            p_serr_moon,
122        );
123        let _s_serr_sun = CString::from(CStr::from_ptr(p_serr_sun))
124            .to_str()
125            .unwrap()
126            .to_string();
127        let _s_serr_moon = CString::from(CStr::from_ptr(p_serr_moon))
128            .to_str()
129            .unwrap()
130            .to_string();
131        let result_houses =
132            swerust::handler_swe14::houses(tjd_ut, geolat, geolong, hsys);
133        let asc_lon = result_houses.cusps[1];
134        let mc_lon = result_houses.cusps[10];
135        let mc_lat = 0.0;
136        let compute_sun = eq_coords(xx_sun[0], xx_sun[1]);
137        let compute_mc = eq_coords(mc_lon, mc_lat);
138        let sw_is_diurnal = is_above_horizon(
139            compute_sun.0,
140            compute_sun.1,
141            compute_mc.0,
142            compute_mc.1,
143        );
144        /*println!(
145            "sw_is_diurnal: {}asc: {}moon: {}sun: {}",
146            sw_is_diurnal.clone(),
147            asc_lon.clone(),
148            xx_moon[0].clone(),
149            xx_sun[0].clone()
150        );*/
151        let mut lon = if sw_is_diurnal {
152            asc_lon + xx_moon[0] - xx_sun[0]
153        } else {
154            asc_lon + xx_sun[0] - xx_moon[0]
155        };
156        let mut done = false;
157        while !done {
158            if lon < 0.0 {
159                lon += 360.0;
160            } else {
161                done = true;
162            }
163        }
164        done = false;
165        while !done {
166            if lon >= 360.0 {
167                lon -= 360.0;
168            } else {
169                done = true;
170            }
171        }
172        CalcUtResult {
173            longitude: lon,
174            latitude: xx[1],
175            distance_au: xx[2],
176            speed_longitude: xx[3],
177            speed_latitude: xx[4],
178            speed_distance_au: xx[5],
179            serr: s_serr,
180            status,
181        }
182    };
183    result
184}
185
186/// Converts from ecliptical to equatorial coordinates.
187fn eq_coords(lon: f64, lat: f64) -> (f64, f64) {
188    // Convert to radian
189    let lambda = lon.to_radians();
190    let beta = lat.to_radians();
191    let epson = (23.44_f64).to_radians(); // the earth inclinaison
192
193    // Declinaison in radian
194    let decl = (epson.sin() * lambda.sin() * beta.cos()
195        + epson.cos() * beta.sin())
196    .asin();
197
198    // Equatorial distance
199    let ed = (lambda.cos() * beta.cos() / decl.cos()).acos();
200
201    // RA in radian
202    let mut ra = if lon < 100.0 {
203        ed
204    } else {
205        (360.0_f64).to_radians() - ed
206    };
207
208    // Correctness of RA if longitude is close to 0° or 180° in a radius of 5°
209    if (closest_distance(lon, 0.0)).abs() < 5.0
210        || (closest_distance(lon, 180.0)).abs() < 5.0
211    {
212        let a = ra.sin() * decl.cos();
213        let b =
214            epson.cos() * lambda.sin() * beta.cos() - epson.sin() * beta.sin();
215        if (a - b).abs() > 0.0003 {
216            ra = (360.0_f64).to_radians() - ra;
217        }
218    }
219    (ra.to_degrees(), decl.to_degrees())
220}
221
222/// Boolean if is above horizon
223/// Returns if an object's ra and decl is above the horizon at a specific
224/// latitude, given the MC's right ascension.
225///
226/// This function checks if the equatorial distance from the object to the MC
227/// is within its diurnal semi-arc.
228fn is_above_horizon(ra: f64, decl: f64, mc_ra: f64, lat: f64) -> bool {
229    let d_arc_tulpe = dnarcs(decl, lat);
230    let dist = (closest_distance(mc_ra, ra)).abs();
231    dist <= d_arc_tulpe.0 / 2.0 + 0.0003 // 1 arc-second
232}
233
234/// Returns the diurnal and nocturnal arcs of a point.
235fn dnarcs(decl: f64, lat: f64) -> (f64, f64) {
236    let d_arc = 180.0 + 2.0 * ascdiff(decl, lat);
237    let n_arc = 360.0 - d_arc;
238    (d_arc, n_arc)
239}
240
241/// Returns the Ascensional Difference of a point.
242fn ascdiff(decl: f64, lat: f64) -> f64 {
243    let delta = decl.to_radians();
244    let phi = lat.to_radians();
245    let ad = (delta.tan() * phi.tan()).asin();
246    ad.to_degrees()
247}
248
249/// Closest distance between 2 point
250fn closest_distance(angle1: f64, angle2: f64) -> f64 {
251    znorm(angle2 - angle1)
252}
253
254/// Normalize angle between -180° and 180°
255fn znorm(mut angle: f64) -> f64 {
256    angle %= 360.0;
257    if angle <= 180.0 {
258        angle
259    } else {
260        angle - 180.0
261    }
262}