diff --git a/CHANGELOG.md b/CHANGELOG.md
index b791513c..1ed985ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,9 +7,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## [1.2.0-rc2] - 2024-11-18
+## [Unreleased]
-Release candidate for the next feature release, expected around 1 February 2025.
+Changes exzpected for the next feature release, expected around 1 February 2025.
### Added
@@ -57,10 +57,10 @@ Release candidate for the next feature release, expected around 1 February 2025.
[GNU standard](https://www.gnu.org/prep/standards/html_node/Directory-Variables.html) to further customize the
install locations.
- - #95: Added support for using orbital elements. `object.type` can now be set to `NOVAS_ORBITAL_OBJECT`, whose orbit
+ - #95, #98: Added support for using orbital elements. `object.type` can now be set to `NOVAS_ORBITAL_OBJECT`, whose orbit
can be defined by the set of `novas_orbital_elements`, relative to a `novas_orbital_system`. You can initialize an
`object` with a set of orbital elements using `make_orbital_object()`, and for planetary satellite orbits you might
- use `novas_set_orbital_pole()`. For orbital objects, `ephemeris()` will call on the new `novas_orbit_posvel()` to
+ use `novas_set_orbsys_pole()`. For orbital objects, `ephemeris()` will call on the new `novas_orbit_posvel()` to
calculate positions. While orbital elements do not always yield precise positions, they can for shorter periods,
provided that the orbital elements are up-to-date. For example, the Minor Planer Center (MPC) publishes accurate
orbital elements for all known asteroids and comets regularly. For newly discovered objects, this may be the only
@@ -69,6 +69,9 @@ Release candidate for the next feature release, expected around 1 February 2025.
- #97: Added `NOVAS_EMB` (Earth-Moon Barycenter) and `NOVAS_PLUTO_BARYCENTER` to `enum novas_planets` to distinguish
from the planet center in calculations.
+ - #98: Added `gcrs_to_tod()` / `tod_to_gcrs()` and `gcrs_to_mod()` / `mod_to_gcrs()` vector conversion functions for
+ convenience.
+
- SuperNOVAS headers now include each other as system-headers, not local headers. This is unlikely to affect anything
really but it is more proper for an installation of the library, and works with our own `Makefile` too.
@@ -80,6 +83,7 @@ Release candidate for the next feature release, expected around 1 February 2025.
constants.
+
## [1.1.1] - 2024-10-28
Bug fix release. Nothing too scary, mainly just a collection of smaller fixes and improvements.
diff --git a/README.md b/README.md
index 296f4578..2ed7103c 100644
--- a/README.md
+++ b/README.md
@@ -915,7 +915,7 @@ before that level of accuracy is reached.
- Added support for using orbital elements. `object.type` can now be set to `NOVAS_ORBITAL_OBJECT`, whose orbit
can be defined by the set of `novas_orbital_elements`, relative to a `novas_orbital_system`. You can initialize an
`object` with a set of orbital elements using `make_orbital_object()`, and for planetary satellite orbits you might
- use `novas_set_orbital_pole()`. For orbital objects, `ephemeris()` will call on the new `novas_orbit_posvel()` to
+ use `novas_set_orbsys_pole()`. For orbital objects, `ephemeris()` will call on the new `novas_orbit_posvel()` to
calculate positions. While orbital elements do not always yield precise positions, they can for shorter periods,
provided that the orbital elements are up-to-date. For example, the Minor Planer Center (MPC) publishes accurate
orbital elements for all known asteroids and comets regularly. For newly discovered objects, this may be the only
@@ -923,7 +923,9 @@ before that level of accuracy is reached.
- Added `NOVAS_EMB` (Earth-Moon Barycenter) and `NOVAS_PLUTO_BARYCENTER` to `enum novas_planets` to distinguish
from the corresponding planet centers in calculations.
-
+
+ - Added `gcrs_to_tod()` / `tod_to_gcrs()` and `gcrs_to_mod()` / `mod_to_gcrs()` vector conversion functions for
+ convenience.
### Refinements to the NOVAS C API
diff --git a/include/novas.h b/include/novas.h
index 5753b80a..8b238f1c 100644
--- a/include/novas.h
+++ b/include/novas.h
@@ -715,15 +715,14 @@ typedef struct {
* @author Attila Kovacs
* @since 1.2
*
- * @sa novas_set_orbital_pole()
+ * @sa novas_set_obsys_pole()
* @sa novas_orbital_elements
* @sa NOVAS_ORBITAL_SYSTEM_INIT
*/
typedef struct {
enum novas_planet center; ///< major planet or barycenter at the center of the orbit.
enum novas_reference_plane plane; ///< reference plane NOVAS_ECLIPTIC_PLANE or NOVAS_EQUATORIAL_PLANE
- enum novas_equator_type type; ///< the type of equator in which orientation is referenced (NOVAS_TRUE_EQUATOR,
- ///< NOVAS_MEAN_EQUATOR, or NOVAS_GCRS_EQUATOR).
+ enum novas_reference_system type; ///< the coordinate reference system used for the reference plane and orbitals.
double obl; ///< [rad] relative obliquity of orbital reference plane
///< (e.g. 90° - δpole)
double Omega; ///< [rad] relative argument of ascending node of the orbital reference plane
@@ -737,7 +736,7 @@ typedef struct {
* @author Attila Kovacs
* @since 1.2
*/
-#define NOVAS_ORBITAL_SYSTEM_INIT { NOVAS_SUN, NOVAS_ECLIPTIC_PLANE, NOVAS_GCRS_EQUATOR, 0.0, 0.0 }
+#define NOVAS_ORBITAL_SYSTEM_INIT { NOVAS_SUN, NOVAS_ECLIPTIC_PLANE, NOVAS_GCRS, 0.0, 0.0 }
/**
* Keplerian orbital elements for `NOVAS_ORBITAL_OBJECT` type. Orbital elements can be used to provide
@@ -1486,12 +1485,19 @@ double novas_z_inv(double z);
enum novas_planet novas_planet_for_name(const char *name);
-int novas_set_orbital_pole(double ra, double dec, novas_orbital_system *sys);
+int novas_set_orbsys_pole(enum novas_reference_system type, double ra, double dec, novas_orbital_system *sys);
int make_orbital_object(const char *name, long num, const novas_orbital_elements *orbit, object *body);
int novas_orbit_posvel(double jd_tdb, const novas_orbital_elements *orb, enum novas_accuracy accuracy, double *pos, double *vel);
+int gcrs_to_tod(double jd_tdb, enum novas_accuracy accuracy, const double *in, double *out);
+
+int tod_to_gcrs(double jd_tdb, enum novas_accuracy accuracy, const double *in, double *out);
+
+int gcrs_to_mod(double jd_tdb, const double *in, double *out);
+
+int mod_to_gcrs(double jd_tdb, const double *in, double *out);
// <================= END of SuperNOVAS API =====================>
diff --git a/src/novas.c b/src/novas.c
index a7fe2f87..008d876f 100644
--- a/src/novas.c
+++ b/src/novas.c
@@ -470,6 +470,58 @@ int gcrs_to_j2000(const double *in, double *out) {
return 0;
}
+/**
+ * Transforms a rectangular equatorial (x, y, z) vector from the Geocentric Celestial
+ * Reference System (GCRS) to the Mean of Date (MOD) reference frame at the given epoch
+ *
+ * @param jd_tdb [day] Barycentric Dynamical Time (TT) based Julian date that defines the
+ * output epoch. Typically it does not require much precision, and Julian
+ * dates in other time measures will be unlikely to affect the result
+ * @param in GCRS Input (x, y, z) position or velocity vector
+ * @param[out] out Output position or velocity 3-vector in the Mean wquinox of Date coordinate
+ * frame. It can be the same vector as the input.
+ * @return 0 if successful, or -1 if either of the vector arguments is NULL.
+ *
+ * @sa mod_to_gcrs()
+ * @sa gcrs_to_tod()
+ *
+ * @since 1.2
+ * @author Attila Kovacs
+ */
+int gcrs_to_mod(double jd_tdb, const double *in, double *out) {
+ static const char *fn = "gcrs_to_tod [internal]";
+ prop_error(fn, frame_tie(in, ICRS_TO_J2000, out), 0);
+ prop_error(fn, precession(NOVAS_JD_J2000, out, jd_tdb, out), 0);
+ return 0;
+}
+
+/**
+ * Transforms a rectangular equatorial (x, y, z) vector from Mean of Date (MOD) reference
+ * frame at the given epoch to the Geocentric Celestial Reference System(GCRS)
+ *
+ * @param jd_tdb [day] Barycentric Dynamical Time (TDB) based Julian date that defines the
+ * input epoch. Typically it does not require much precision, and Julian dates
+ * in other time measures will be unlikely to affect the result
+ * @param in Input (x, y, z) position or velocity 3-vector in the Mean equinox of Date
+ * coordinate frame.
+ * @param[out] out Output GCRS position or velocity vector. It can be the same vector as the
+ * input.
+ * @return 0 if successful, or -1 if either of the vector arguments is NULL.
+ *
+ * @sa gcrs_to_mod()
+ * @sa tod_to_gcrs()
+ *
+ * @since 1.2
+ * @author Attila Kovacs
+ */
+int mod_to_gcrs(double jd_tdb, const double *in, double *out) {
+ static const char *fn = "tod_to_gcrs [internal]";
+ prop_error(fn, precession(jd_tdb, in, NOVAS_JD_J2000, out), 0);
+ prop_error(fn, frame_tie(out, J2000_TO_ICRS, out), 0);
+ return 0;
+}
+
+
/**
* Transforms a rectangular equatorial (x, y, z) vector from the Geocentric Celestial
* Reference System (GCRS) to the True of Date (TOD) reference frame at the given epoch
@@ -487,10 +539,10 @@ int gcrs_to_j2000(const double *in, double *out) {
* @sa tod_to_gcrs()
* @sa j2000_to_tod()
*
- * @since 1.0
+ * @since 1.2
* @author Attila Kovacs
*/
-static int gcrs_to_tod(double jd_tdb, enum novas_accuracy accuracy, const double *in, double *out) {
+int gcrs_to_tod(double jd_tdb, enum novas_accuracy accuracy, const double *in, double *out) {
static const char *fn = "gcrs_to_tod [internal]";
prop_error(fn, frame_tie(in, ICRS_TO_J2000, out), 0);
prop_error(fn, j2000_to_tod(jd_tdb, accuracy, out, out), 0);
@@ -516,10 +568,10 @@ static int gcrs_to_tod(double jd_tdb, enum novas_accuracy accuracy, const double
* @sa tod_to_j2000()
* @sa tod_to_itrs()
*
- * @since 1.0
+ * @since 1.2
* @author Attila Kovacs
*/
-static int tod_to_gcrs(double jd_tdb, enum novas_accuracy accuracy, const double *in, double *out) {
+int tod_to_gcrs(double jd_tdb, enum novas_accuracy accuracy, const double *in, double *out) {
static const char *fn = "tod_to_gcrs [internal]";
prop_error(fn, tod_to_j2000(jd_tdb, accuracy, in, out), 0);
prop_error(fn, frame_tie(out, J2000_TO_ICRS, out), 0);
@@ -1678,7 +1730,7 @@ short place(double jd_tt, const object *source, const observer *location, double
prop_error(fn, grav_planets(pos, pob, &planets, pos), 70);
// ---------------------------------------------------------------------
- // Apply light and aberration.
+ // Apply aberration correction.
// ---------------------------------------------------------------------
aberration(pos, vob, t_light, pos);
}
@@ -1695,8 +1747,7 @@ short place(double jd_tt, const object *source, const observer *location, double
case NOVAS_MOD: {
// Transform to equator and equinox of date.
- gcrs_to_j2000(pos, pos);
- precession(NOVAS_JD_J2000, pos, jd_tdb, pos);
+ gcrs_to_mod(jd_tdb, pos, pos);
break;
}
@@ -2237,9 +2288,7 @@ short gcrs2equ(double jd_tt, enum novas_dynamical_type sys, enum novas_accuracy
break;
case NOVAS_DYNAMICAL_MOD: {
- double pos3[3];
- frame_tie(pos1, ICRS_TO_J2000, pos3);
- precession(JD_J2000, pos3, jd_tdb, pos2);
+ gcrs_to_mod(jd_tdb, pos1, pos2);
break;
}
@@ -2349,7 +2398,7 @@ short sidereal_time(double jd_ut1_high, double jd_ut1_low, double ut1_to_tt, enu
case EROT_ERA: {
// Use 'CIO-TIO-theta' method. See Circular 179, Section 6.5.4.
const double ux[3] = { 1.0, 0.0, 0.0 };
- double ra_cio, ha_eq, x[3], y[3], z[3], w1[3], w2[3], eq[3];
+ double ra_cio, ha_eq, x[3], y[3], z[3], eq[3];
short ref_sys;
// Obtain the basis vectors, in the GCRS, of the celestial intermediate system.
@@ -2358,9 +2407,7 @@ short sidereal_time(double jd_ut1_high, double jd_ut1_low, double ut1_to_tt, enu
cio_basis(jd_tdb, ra_cio, ref_sys, accuracy, x, y, z);
// Compute the direction of the true equinox in the GCRS.
- nutation(jd_tdb, NUTATE_TRUE_TO_MEAN, accuracy, ux, w1);
- precession(jd_tdb, w1, JD_J2000, w2);
- frame_tie(w2, J2000_TO_ICRS, eq);
+ tod_to_gcrs(jd_tdb, accuracy, ux, eq);
// Compute the hour angle of the equinox wrt the TIO meridian
// (near Greenwich, but passes through the CIP and TIO).
@@ -2996,8 +3043,7 @@ int polar_dxdy_to_dpsideps(double jd_tt, double dx, double dy, double *dpsi, dou
double dp[3] = { dx * MAS, dy * MAS, dz * MAS };
// Precess pole offset vector to mean equator and equinox of date.
- frame_tie(dp, ICRS_TO_J2000, dp);
- precession(JD_J2000, dp, jd_tt, dp);
+ gcrs_to_mod(jd_tt, dp, dp);
// Compute delta-delta-psi and delta-delta-epsilon in arcseconds.
if(dpsi) {
@@ -5835,27 +5881,29 @@ static int change_pole(const double *in, double theta, double phi, double *out)
* Converts equatorial coordinates of a given type to GCRS equatorial coordinates
*
* @param jd_tdb [day] Barycentric Dynamical Time (TDB) based Julian Date
- * @param[in] in input 3-vector
* @param sys the type of equator assumed for the input (mean, true, or GCRS).
- * @param[out] out output 3-vector. It may be the same as the input.
+ * @param[in, out] vec vector to change to GCRS.
* @return 0 if successful, or else -1 (errno set to EINVAL) if the 'sys'
* argument is invalid.
*
* @author Attila Kovacs
* @since 1.2
*/
-static int equ2gcrs(double jd_tdb, const double *in, enum novas_equator_type sys, double *out) {
+static int equ2gcrs(double jd_tdb, enum novas_reference_system sys, double *vec) {
switch(sys) {
- case NOVAS_GCRS_EQUATOR:
- memcpy(out, in, XYZ_VECTOR_SIZE);
+ case NOVAS_GCRS:
+ case NOVAS_ICRS:
return 0;
- case NOVAS_TRUE_EQUATOR:
- return tod_to_gcrs(jd_tdb, NOVAS_REDUCED_ACCURACY, in, out);
- case NOVAS_MEAN_EQUATOR:
- precession(jd_tdb, in, NOVAS_JD_J2000, out);
- return j2000_to_gcrs(out, out);
+ case NOVAS_CIRS:
+ return cirs_to_gcrs(jd_tdb, NOVAS_REDUCED_ACCURACY, vec, vec);
+ case NOVAS_J2000:
+ return j2000_to_gcrs(vec, vec);
+ case NOVAS_TOD:
+ return tod_to_gcrs(jd_tdb, NOVAS_REDUCED_ACCURACY, vec, vec);
+ case NOVAS_MOD:
+ return mod_to_gcrs(jd_tdb, vec, vec);
default:
- return novas_error(-1, EINVAL, "equ2gcrs", "invalid equator type: %d", sys);
+ return novas_error(-1, EINVAL, "equ2gcrs", "invalid reference system: %d", sys);
}
}
@@ -5874,18 +5922,43 @@ static int equ2gcrs(double jd_tdb, const double *in, enum novas_equator_type sys
static int orbit2gcrs(double jd_tdb, const novas_orbital_system *sys, enum novas_accuracy accuracy, double *vec) {
static const char *fn = "orbit2gcrs";
+ if(accuracy != NOVAS_FULL_ACCURACY && accuracy != NOVAS_REDUCED_ACCURACY)
+ return novas_error(-1, EINVAL, fn, "invalid accuracy: %d", accuracy);
+
if(sys->obl)
change_pole(vec, sys->obl, sys->Omega, vec);
-
if(sys->plane == NOVAS_ECLIPTIC_PLANE) {
- if(ecl2equ_vec(jd_tdb, sys->type, accuracy, vec, vec) != 0)
- return novas_trace(fn, -1, 0);
+ enum novas_equator_type eq;
+ double jd = jd_tdb;
+
+ switch(sys->type) {
+ case NOVAS_GCRS:
+ case NOVAS_ICRS:
+ eq = NOVAS_GCRS_EQUATOR;
+ jd = NOVAS_JD_J2000;
+ break;
+ case NOVAS_J2000:
+ eq = NOVAS_TRUE_EQUATOR;
+ jd = NOVAS_JD_J2000;
+ break;
+ case NOVAS_TOD:
+ case NOVAS_CIRS:
+ eq = NOVAS_TRUE_EQUATOR;
+ break;
+ case NOVAS_MOD:
+ eq = NOVAS_MEAN_EQUATOR;
+ break;
+ default:
+ return novas_error(-1, EINVAL, fn, "invalid reference system: %d", sys->type);
+ }
+
+ ecl2equ_vec(jd, eq, accuracy, vec, vec);
}
else if(sys->plane != NOVAS_EQUATORIAL_PLANE)
return novas_error(-1, EINVAL, fn, "invalid orbital system reference plane type: %d", sys->type);
- prop_error(fn, equ2gcrs(jd_tdb, vec, sys->type, vec), 0);
+ prop_error(fn, equ2gcrs(jd_tdb, sys->type, vec), 0);
return 0;
}
diff --git a/src/super.c b/src/super.c
index 3376c533..f2a22a35 100644
--- a/src/super.c
+++ b/src/super.c
@@ -1381,6 +1381,7 @@ enum novas_planet novas_planet_for_name(const char *name) {
* on appropriate ephemerides, or else on up-to-date short-term orbital elements.
*
*
+ * @param type Coordinate reference system in which `ra` and `dec` are defined (e.g. NOVAS_GCRS).
* @param ra [h] the R.A. of the pole of the oribtal reference plane.
* @param dec [deg] the declination of the pole of the oribtal reference plane.
* @param[out] sys The orbital system
@@ -1392,11 +1393,12 @@ enum novas_planet novas_planet_for_name(const char *name) {
*
* @sa make_orbital_object()
*/
-int novas_set_orbital_pole(double ra, double dec, novas_orbital_system *sys) {
+int novas_set_orbsys_pole(enum novas_reference_system type, double ra, double dec, novas_orbital_system *sys) {
if (!sys)
- return novas_error(-1, EINVAL, "novas_set_orbital_pole", "input system is NULL");
+ return novas_error(-1, EINVAL, "novas_set_obsys_pole", "input system is NULL");
sys->plane = NOVAS_EQUATORIAL_PLANE;
+ sys->type = type;
sys->obl = remainder(90.0 - dec, 360.0);
sys->Omega = remainder(15.0 * ra + 90.0, 360.0);
diff --git a/test/src/test-errors.c b/test/src/test-errors.c
index dca1b562..98815fca 100644
--- a/test/src/test-errors.c
+++ b/test/src/test-errors.c
@@ -307,6 +307,48 @@ static int test_tod_to_j2000() {
return n;
}
+static int test_gcrs_to_tod() {
+ double p[3];
+ int n = 0;
+
+ if(check("gcrs_to_tod:in", -1, gcrs_to_tod(0.0, NOVAS_FULL_ACCURACY, NULL, p))) n++;
+ if(check("gcrs_to_tod:out", -1, gcrs_to_tod(0.0, NOVAS_FULL_ACCURACY, p, NULL))) n++;
+ if(check("gcrs_to_tod:accuracy", -1, gcrs_to_tod(0.0, -1, p, p))) n++;
+
+ return n;
+}
+
+static int test_tod_to_gcrs() {
+ double p[3] = {1.0};
+ int n = 0;
+
+ if(check("tod_to_gcrs:in", -1, tod_to_gcrs(0.0, NOVAS_FULL_ACCURACY, NULL, p))) n++;
+ if(check("tod_to_gcrs:out", -1, tod_to_gcrs(0.0, NOVAS_FULL_ACCURACY, p, NULL))) n++;
+ if(check("tod_to_gcrs:accuracy", -1, tod_to_gcrs(0.0, -1, p, p))) n++;
+
+ return n;
+}
+
+static int test_gcrs_to_mod() {
+ double p[3];
+ int n = 0;
+
+ if(check("gcrs_to_mod:in", -1, gcrs_to_mod(0.0, NULL, p))) n++;
+ if(check("gcrs_to_mod:out", -1, gcrs_to_mod(0.0, p, NULL))) n++;
+
+ return n;
+}
+
+static int test_mod_to_gcrs() {
+ double p[3] = {1.0};
+ int n = 0;
+
+ if(check("mod_to_gcrs:in", -1, mod_to_gcrs(0.0, NULL, p))) n++;
+ if(check("mod_to_gcrs:out", -1, mod_to_gcrs(0.0, p, NULL))) n++;
+
+ return n;
+}
+
static int test_gcrs_to_cirs() {
double p[3] = {1.0};
int n = 0;
@@ -1511,38 +1553,40 @@ static int test_orbit_posvel() {
orbit.a = 1.0;
- if(check("set_orbital_pole:orbit", -1, novas_orbit_posvel(0.0, NULL, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
- if(check("set_orbital_pole:pos=vel", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, pos))) n++;
- if(check("set_orbital_pole:pos=vel:NULL", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, NULL, NULL))) n++;
+ if(check("set_obsys_pole:orbit", -1, novas_orbit_posvel(0.0, NULL, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
+ if(check("set_obsys_pole:pos=vel", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, pos))) n++;
+ if(check("set_obsys_pole:pos=vel:NULL", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, NULL, NULL))) n++;
+ if(check("set_obsys_pole:accuracy:-1", -1, novas_orbit_posvel(0.0, &orbit, -1, pos, vel))) n++;
+ if(check("set_obsys_pole:accuracy:2", -1, novas_orbit_posvel(0.0, &orbit, 2, pos, vel))) n++;
- if(check("set_orbital_pole:orbit:converge", 0, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
+ if(check("set_obsys_pole:orbit:converge", 0, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
novas_inv_max_iter = 0;
- if(check("set_orbital_pole:orbit:converge", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
- else if(check("set_orbital_pole:orbit:converge:errno", ECANCELED, errno)) n++;
+ if(check("set_obsys_pole:orbit:converge", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
+ else if(check("set_obsys_pole:orbit:converge:errno", ECANCELED, errno)) n++;
novas_inv_max_iter = saved;
orbit.system.type = -1;
- if(check("set_orbital_pole:orbit:type:-1", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
+ if(check("set_obsys_pole:orbit:type:-1", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
- orbit.system.type = NOVAS_EQUATOR_TYPES;
- if(check("set_orbital_pole:orbit:type:hi", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
+ orbit.system.type = NOVAS_REFERENCE_SYSTEMS;
+ if(check("set_obsys_pole:orbit:type:hi", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
orbit.system.plane = NOVAS_EQUATORIAL_PLANE;
- orbit.system.type = NOVAS_EQUATOR_TYPES;
- if(check("set_orbital_pole:orbit:type:-1:eq", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
+ orbit.system.type = NOVAS_REFERENCE_SYSTEMS;
+ if(check("set_obsys_pole:orbit:type:-1:eq", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
- orbit.system.type = NOVAS_GCRS_EQUATOR;
+ orbit.system.type = NOVAS_GCRS;
orbit.system.plane = -1;
- if(check("set_orbital_pole:orbit:plane:-1", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
+ if(check("set_obsys_pole:orbit:plane:-1", -1, novas_orbit_posvel(0.0, &orbit, NOVAS_REDUCED_ACCURACY, pos, vel))) n++;
return n;
}
-static int test_set_orbital_pole() {
+static int test_set_obsys_pole() {
int n = 0;
- if(check("set_orbital_pole:orbit", -1, novas_set_orbital_pole(0.0, 0.0, NULL))) n++;
+ if(check("set_obsys_pole:orbit", -1, novas_set_orbsys_pole(NOVAS_GCRS, 0.0, 0.0, NULL))) n++;
return n;
}
@@ -1677,9 +1721,14 @@ int main() {
if(test_planet_for_name()) n++;
if(test_make_orbital_object()) n++;
- if(test_set_orbital_pole()) n++;
+ if(test_set_obsys_pole()) n++;
if(test_orbit_posvel()) n++;
+ if(test_gcrs_to_tod()) n++;
+ if(test_tod_to_gcrs()) n++;
+ if(test_gcrs_to_mod()) n++;
+ if(test_mod_to_gcrs()) n++;
+
if(n) fprintf(stderr, " -- FAILED %d tests\n", n);
else fprintf(stderr, " -- OK\n");
diff --git a/test/src/test-super.c b/test/src/test-super.c
index d427fcf7..48d3609a 100644
--- a/test/src/test-super.c
+++ b/test/src/test-super.c
@@ -76,7 +76,7 @@ static int check_equal_pos(const double *posa, const double *posb, double tol) {
if(fabs(posa[i] - posb[i]) <= tol) continue;
if(isnan(posa[i]) && isnan(posb[i])) continue;
- fprintf(stderr, " A[%d] = %.9g vs B[%d] = %.9g\n", i, posa[i], i, posb[i]);
+ fprintf(stderr, " A[%d] = %.9g vs B[%d] = %.9g (delta=%.1g)\n", i, posa[i], i, posb[i], posa[i] - posb[i]);
return i + 1;
}
@@ -744,7 +744,43 @@ static int test_transform_inv() {
return 0;
}
+static int test_gcrs_to_tod() {
+ double pos1[3] = {}, pos2[3] = {}, d;
+ int n = 0;
+
+ d = novas_vlen(pos0);
+
+ if(!is_ok("gcrs_to_tod", gcrs_to_tod(tdb, NOVAS_FULL_ACCURACY, pos0, pos1))) n++;
+
+ gcrs_to_j2000(pos0, pos2);
+ j2000_to_tod(tdb, NOVAS_FULL_ACCURACY, pos2, pos2);
+
+ if(!is_ok("gcrs_to_tod:check", check_equal_pos(pos1, pos2, 1e-9 * d))) n++;
+
+ if(!is_ok("gcrs_to_tod:tod_to_gcrs", tod_to_gcrs(tdb, NOVAS_FULL_ACCURACY, pos1, pos2))) n++;
+ if(!is_ok("gcrs_to_tod:tod_to_gcrs:check", check_equal_pos(pos2, pos0, 1e-9 * d))) n++;
+
+ return n;
+}
+static int test_gcrs_to_mod() {
+ double pos1[3] = {}, pos2[3] = {}, d;
+ int n = 0;
+
+ d = novas_vlen(pos0);
+
+ if(!is_ok("gcrs_to_mod", gcrs_to_mod(tdb, pos0, pos1))) n++;
+
+ gcrs_to_j2000(pos0, pos2);
+ precession(NOVAS_JD_J2000, pos2, tdb, pos2);
+
+ if(!is_ok("gcrs_to_mod:check", check_equal_pos(pos1, pos2, 1e-9 * d))) n++;
+
+ if(!is_ok("gcrs_to_mod:mod_to_gcrs", mod_to_gcrs(tdb, pos1, pos2))) n++;
+ if(!is_ok("gcrs_to_mod:mod_to_gcrs:check", check_equal_pos(pos2, pos0, 1e-9 * d))) n++;
+
+ return n;
+}
static int test_source() {
int k, n = 0;
@@ -782,6 +818,9 @@ static int test_source() {
if(test_transform_mod_tod()) n++;
if(test_transform_inv()) n++;
+ if(test_gcrs_to_tod()) n++;
+ if(test_gcrs_to_mod()) n++;
+
for(k = 0; k < NOVAS_REFERENCE_SYSTEMS; k++) if(test_app_hor(k)) n++;
for(k = 0; k < NOVAS_REFERENCE_SYSTEMS; k++) if(test_app_geom(k)) n++;
@@ -2218,6 +2257,7 @@ static int test_orbit_place() {
novas_orbital_elements orbit = NOVAS_ORBIT_INIT;
observer obs = {};
sky_pos pos = {};
+ double p0[3] = {}, p1[3] = {};
// Nov 14 0 UTC, geocentric from JPL Horizons.
double tjd = 2460628.50079861; // 0 UT as TT.
@@ -2247,10 +2287,50 @@ static int test_orbit_place() {
if(!is_equal("orbit_place:dist", pos.dis, r, 1e-4)) n++;
if(!is_equal("orbit_place:vrad", pos.rv, rv0, 1e-2)) n++;
- return n;
-}
+ if(!is_ok("orbit_place", novas_orbit_posvel(tjd, &orbit, NOVAS_FULL_ACCURACY, p0, NULL))) return 1;
+ equ2ecl_vec(tjd, NOVAS_GCRS_EQUATOR, NOVAS_FULL_ACCURACY, p0, p0);
+
+ orbit.system.type = NOVAS_ICRS;
+ if(!is_ok("orbit_place:icrs", novas_orbit_posvel(tjd, &orbit, NOVAS_FULL_ACCURACY, p1, NULL))) n++;
+ else {
+ equ2ecl_vec(tjd, NOVAS_GCRS_EQUATOR, NOVAS_FULL_ACCURACY, p1, p1);
+ if(!is_ok("orbit_place:icrs:check", check_equal_pos(p1, p0, 1e-9))) n++;
+ }
+ orbit.system.type = NOVAS_CIRS;
+ if(!is_ok("orbit_place:cirs", novas_orbit_posvel(tjd, &orbit, NOVAS_FULL_ACCURACY, p1, NULL))) n++;
+ else {
+ gcrs_to_cirs(tjd, NOVAS_REDUCED_ACCURACY, p1, p1);
+ equ2ecl_vec(tjd, NOVAS_TRUE_EQUATOR, NOVAS_FULL_ACCURACY, p1, p1);
+ if(!is_ok("orbit_place:cirs:check", check_equal_pos(p1, p0, 1e-9))) n++;
+ }
+ orbit.system.type = NOVAS_J2000;
+ if(!is_ok("orbit_place:j2000", novas_orbit_posvel(tjd, &orbit, NOVAS_FULL_ACCURACY, p1, NULL))) n++;
+ else {
+ gcrs_to_j2000(p1, p1);
+ equ2ecl_vec(NOVAS_JD_J2000, NOVAS_TRUE_EQUATOR, NOVAS_FULL_ACCURACY, p1, p1);
+ if(!is_ok("orbit_place:j2000:check", check_equal_pos(p1, p0, 1e-9))) n++;
+ }
+
+ orbit.system.type = NOVAS_MOD;
+ if(!is_ok("orbit_place:mod", novas_orbit_posvel(tjd, &orbit, NOVAS_FULL_ACCURACY, p1, NULL))) n++;
+ else {
+ gcrs_to_mod(tjd, p1, p1);
+ equ2ecl_vec(tjd, NOVAS_MEAN_EQUATOR, NOVAS_FULL_ACCURACY, p1, p1);
+ if(!is_ok("orbit_place:mod:check", check_equal_pos(p1, p0, 1e-9))) n++;
+ }
+
+ orbit.system.type = NOVAS_TOD;
+ if(!is_ok("orbit_place:tod", novas_orbit_posvel(tjd, &orbit, NOVAS_FULL_ACCURACY, p1, NULL))) n++;
+ else {
+ gcrs_to_tod(tjd, NOVAS_FULL_ACCURACY, p1, p1);
+ equ2ecl_vec(tjd, NOVAS_TRUE_EQUATOR, NOVAS_FULL_ACCURACY, p1, p1);
+ if(!is_ok("orbit_place:tod:check", check_equal_pos(p1, p0, 1e-9))) n++;
+ }
+
+ return n;
+}
static int test_orbit_posvel_callisto() {
@@ -2279,7 +2359,7 @@ static int test_orbit_posvel_callisto() {
// https://ssd.jpl.nasa.gov/sats/elem/sep.html
// 1882700. 0.007 43.8 87.4 0.3 309.1 16.690440 277.921 577.264 268.7 64.8
sys->center = NOVAS_JUPITER;
- novas_set_orbital_pole(268.7 / 15.0, 64.8, sys);
+ novas_set_orbsys_pole(NOVAS_GCRS, 268.7 / 15.0, 64.8, sys);
orbit.jd_tdb = NOVAS_JD_J2000;
orbit.a = 1882700.0 * 1e3 / AU;
@@ -2308,27 +2388,34 @@ static int test_orbit_posvel_callisto() {
if(!is_equal("orbit_posvel_callisto:ra", dra / ARCSEC, dRA / ARCSEC, 15.0)) n++;
if(!is_equal("orbit_posvel_callisto:dec", ddec / ARCSEC, dDEC / ARCSEC, 15.0)) n++;
-
-
if(!is_ok("orbit_posvel_callisto:vel:null", novas_orbit_posvel(tjd, &orbit, NOVAS_FULL_ACCURACY, pos1, NULL))) n++;
if(!is_ok("orbit_posvel_callisto:vel:null:check", check_equal_pos(pos1, pos0, 1e-8))) n++;
if(!is_ok("orbit_posvel_callisto:pos:null", novas_orbit_posvel(tjd, &orbit, NOVAS_FULL_ACCURACY, NULL, vel1))) n++;
if(!is_ok("orbit_posvel_callisto:pos:null:check", check_equal_pos(vel1, vel, 1e-8))) n++;
-
- sys->type = NOVAS_MEAN_EQUATOR;
+ sys->type = NOVAS_MOD;
if(!is_ok("orbit_posvel_callisto:mod", novas_orbit_posvel(tjd, &orbit, NOVAS_FULL_ACCURACY, pos1, NULL))) n++;
precession(tjd, pos0, NOVAS_JD_J2000, pos);
j2000_to_gcrs(pos, pos);
if(!is_ok("orbit_posvel_callisto:mod:check", check_equal_pos(pos1, pos, 1e-8))) n++;
- sys->type = NOVAS_TRUE_EQUATOR;
+ sys->type = NOVAS_TOD;
if(!is_ok("orbit_posvel_callisto:mod", novas_orbit_posvel(tjd, &orbit, NOVAS_FULL_ACCURACY, pos1, NULL))) n++;
tod_to_j2000(tjd, NOVAS_FULL_ACCURACY, pos0, pos);
j2000_to_gcrs(pos, pos);
if(!is_ok("orbit_posvel_callisto:mod:check", check_equal_pos(pos1, pos, 1e-8))) n++;
+ sys->type = NOVAS_CIRS;
+ if(!is_ok("orbit_posvel_callisto:cirs", novas_orbit_posvel(tjd, &orbit, NOVAS_FULL_ACCURACY, pos1, NULL))) n++;
+ cirs_to_gcrs(tjd, NOVAS_FULL_ACCURACY, pos0, pos);
+ if(!is_ok("orbit_posvel_callisto:cirs:check", check_equal_pos(pos1, pos, 1e-8))) n++;
+
+ sys->type = NOVAS_J2000;
+ if(!is_ok("orbit_posvel_callisto:j2000", novas_orbit_posvel(tjd, &orbit, NOVAS_FULL_ACCURACY, pos1, NULL))) n++;
+ j2000_to_gcrs(pos0, pos);
+ if(!is_ok("orbit_posvel_callisto:j2000:check", check_equal_pos(pos1, pos, 1e-8))) n++;
+
return n;
}