From bca3f808aef65a5bf726cd40b28a7e41ed0914ed Mon Sep 17 00:00:00 2001 From: Don Cross Date: Sun, 8 Dec 2019 13:48:27 -0500 Subject: [PATCH] C: Added Astronomy_VectorFromSphere, astro_spherical_t. Added new data type astro_spherical_t that represents generic spherical coordinates. Implemented Astronomy_VectorFromSphere to convert spherical coordinates to Cartesian coordinates. Included unit test to verify it is working as expected. --- generate/ctest.c | 50 +++++++++++++++++++++++++++++++++++ generate/template/astronomy.c | 38 ++++++++++++++++++++++++++ source/c/README.md | 42 +++++++++++++++++++++++++++++ source/c/astronomy.c | 38 ++++++++++++++++++++++++++ source/c/astronomy.h | 13 +++++++++ 5 files changed, 181 insertions(+) diff --git a/generate/ctest.c b/generate/ctest.c index 3a2cf8f8..5e37bcfa 100644 --- a/generate/ctest.c +++ b/generate/ctest.c @@ -1835,11 +1835,61 @@ fail: return error; } +static int TestVectorFromAngles(double lat, double lon, double x, double y, double z) +{ + astro_spherical_t sphere; + astro_vector_t vector; + astro_time_t time; + double diff, dx, dy, dz; + + /* Confirm the expected vector really is a unit vector. */ + diff = fabs((x*x + y*y + z*z) - 1.0); + if (diff > 1.0e-16) + { + fprintf(stderr, "TestVectorFromAngles: EXCESSIVE unit error = %lg\n", diff); + return 1; + } + + sphere.status = ASTRO_SUCCESS; + sphere.lat = lat; + sphere.lon = lon; + sphere.dist = 1.0; + + time = Astronomy_MakeTime(2015, 3, 5, 12, 0, 0.0); + vector = Astronomy_VectorFromSphere(sphere, time); + + if (vector.status != ASTRO_SUCCESS) + { + fprintf(stderr, "ERROR(TestVectorFromAngles): vector.status = %d\n", vector.status); + return 1; + } + + dx = x - vector.x; + dy = y - vector.y; + dz = z - vector.z; + diff = sqrt(dx*dx + dy*dy + dz*dz); + + printf("TestVectorFromAngles(%lf, %lf): diff = %lg\n", lat, lon, diff); + if (diff > 2.0e-16) + { + fprintf(stderr, "TestVectorFromAngles: EXCESSIVE ERROR.\n"); + return 1; + } + return 0; +} + static int RotationTest(void) { int error; CHECK(Rotation_MatrixInverse()); CHECK(Rotation_MatrixMultiply()); + CHECK(TestVectorFromAngles(0.0, 0.0, 1.0, 0.0, 0.0)); + CHECK(TestVectorFromAngles(0.0, 90.0, 0.0, 1.0, 0.0)); + CHECK(TestVectorFromAngles(0.0, 180.0, -1.0, 0.0, 0.0)); + CHECK(TestVectorFromAngles(0.0, 270.0, 0.0, -1.0, 0.0)); + CHECK(TestVectorFromAngles(+90.0, 0.0, 0.0, 0.0, 1.0)); + CHECK(TestVectorFromAngles(-90.0, 0.0, 0.0, 0.0, -1.0)); + CHECK(TestVectorFromAngles(-30.0, +60.0, 0.43301270189221946, 0.75, -0.5)); error = 0; fail: return error; diff --git a/generate/template/astronomy.c b/generate/template/astronomy.c index 66bfb074..d2f8a116 100644 --- a/generate/template/astronomy.c +++ b/generate/template/astronomy.c @@ -4143,6 +4143,44 @@ astro_rotation_t Astronomy_CombineRotation(astro_rotation_t a, astro_rotation_t return c; } +/** + * @brief Converts spherical coordinates to Cartesian coordinates. + * + * Given spherical coordinates and a time at which they are valid, + * returns a vector of Cartesian coordinates. The returned value + * includes the time, as required by the type #astro_vector_t. + * + * @param sphere + * Spherical coordinates to be converted. + * + * @param time + * The time that should be included in the return value. + * + * @return + * The vector form of the supplied spherical coordinates. + */ +astro_vector_t Astronomy_VectorFromSphere(astro_spherical_t sphere, astro_time_t time) +{ + astro_vector_t vector; + double radlat, radlon, rcoslat; + + if (sphere.status != ASTRO_SUCCESS) + return VecError(ASTRO_INVALID_PARAMETER, time); + + radlat = sphere.lat * DEG2RAD; + radlon = sphere.lon * DEG2RAD; + rcoslat = sphere.dist * cos(radlat); + + vector.status = ASTRO_SUCCESS; + vector.t = time; + vector.x = rcoslat * cos(radlon); + vector.y = rcoslat * sin(radlon); + vector.z = sphere.dist * sin(radlat); + + return vector; +} + + #if 0 /** * @brief diff --git a/source/c/README.md b/source/c/README.md index 31588714..ee73d632 100644 --- a/source/c/README.md +++ b/source/c/README.md @@ -1136,6 +1136,31 @@ After calculating the date and time of an astronomical event in the form of an [ +--- + + +### Astronomy_VectorFromSphere(sphere, time) ⇒ [`astro_vector_t`](#astro_vector_t) + +**Converts spherical coordinates to Cartesian coordinates.** + + + +Given spherical coordinates and a time at which they are valid, returns a vector of Cartesian coordinates. The returned value includes the time, as required by the type [`astro_vector_t`](#astro_vector_t). + + + +**Returns:** The vector form of the supplied spherical coordinates. + + + +| Type | Parameter | Description | +| --- | --- | --- | +| [`astro_spherical_t`](#astro_spherical_t) | `sphere` | Spherical coordinates to be converted. | +| [`astro_time_t`](#astro_time_t) | `time` | The time that should be included in the return value. | + + + + --- @@ -1588,6 +1613,23 @@ You can create this structure directly, or you can call the convenience function | [`astro_time_t`](#astro_time_t) | `dec_solstice` | The date and time of the December solstice for the specified year. | +--- + + +### `astro_spherical_t` + +**Spherical coordinates: latitude, longitude, distance.** + + + +| Type | Member | Description | +| ---- | ------ | ----------- | +| [`astro_status_t`](#astro_status_t) | `status` | ASTRO_SUCCESS if this struct is valid; otherwise an error code. | +| `double` | `lat` | The latitude angle: -90..+90 degrees. | +| `double` | `lon` | The longitude angle: 0..360 degrees. | +| `double` | `dist` | Distance in AU. | + + --- diff --git a/source/c/astronomy.c b/source/c/astronomy.c index 65325b8b..d047d971 100644 --- a/source/c/astronomy.c +++ b/source/c/astronomy.c @@ -5382,6 +5382,44 @@ astro_rotation_t Astronomy_CombineRotation(astro_rotation_t a, astro_rotation_t return c; } +/** + * @brief Converts spherical coordinates to Cartesian coordinates. + * + * Given spherical coordinates and a time at which they are valid, + * returns a vector of Cartesian coordinates. The returned value + * includes the time, as required by the type #astro_vector_t. + * + * @param sphere + * Spherical coordinates to be converted. + * + * @param time + * The time that should be included in the return value. + * + * @return + * The vector form of the supplied spherical coordinates. + */ +astro_vector_t Astronomy_VectorFromSphere(astro_spherical_t sphere, astro_time_t time) +{ + astro_vector_t vector; + double radlat, radlon, rcoslat; + + if (sphere.status != ASTRO_SUCCESS) + return VecError(ASTRO_INVALID_PARAMETER, time); + + radlat = sphere.lat * DEG2RAD; + radlon = sphere.lon * DEG2RAD; + rcoslat = sphere.dist * cos(radlat); + + vector.status = ASTRO_SUCCESS; + vector.t = time; + vector.x = rcoslat * cos(radlon); + vector.y = rcoslat * sin(radlon); + vector.z = sphere.dist * sin(radlat); + + return vector; +} + + #if 0 /** * @brief diff --git a/source/c/astronomy.h b/source/c/astronomy.h index ad6573b6..7e667e70 100644 --- a/source/c/astronomy.h +++ b/source/c/astronomy.h @@ -170,6 +170,18 @@ typedef struct } astro_vector_t; +/** + * @brief Spherical coordinates: latitude, longitude, distance. + */ +typedef struct +{ + astro_status_t status; /**< ASTRO_SUCCESS if this struct is valid; otherwise an error code. */ + double lat; /**< The latitude angle: -90..+90 degrees. */ + double lon; /**< The longitude angle: 0..360 degrees. */ + double dist; /**< Distance in AU. */ +} +astro_spherical_t; + /** * @brief An angular value expressed in degrees. */ @@ -602,6 +614,7 @@ astro_apsis_t Astronomy_NextLunarApsis(astro_apsis_t apsis); astro_rotation_t Astronomy_InverseRotation(astro_rotation_t rotation); astro_rotation_t Astronomy_CombineRotation(astro_rotation_t a, astro_rotation_t b); +astro_vector_t Astronomy_VectorFromSphere(astro_spherical_t sphere, astro_time_t time); #if 0 astro_rotation_t Astronomy_Rotation_EQD_EQJ(astro_time_t time);