diff --git a/generate/template/astronomy.py b/generate/template/astronomy.py index 8e9c2832..8c4ccd47 100644 --- a/generate/template/astronomy.py +++ b/generate/template/astronomy.py @@ -1556,6 +1556,42 @@ def RefractionAngle(refraction, altitude): refr = 0.0 return refr +def InverseRefractionAngle(refraction, bent_altitude): + """Calculates the inverse of an atmospheric refraction angle. + + Given an observed altitude angle that includes atmospheric refraction, + calculate the negative angular correction to obtain the unrefracted + altitude. This is useful for cases where observed horizontal + coordinates are to be converted to another orientation system, + but refraction first must be removed from the observed position. + + Parameters + ---------- + refraction : Refraction + `Refraction.Normal` - corrects for atmospheric refraction (recommended). + `Refraction.Airless` - no correction is performed. + `Refraction.JplHorizons` - For JPL Horizons compatibility testing only. + bent_altitude : float + The apparent altitude that includes atmospheric refraction. + + Returns + ------- + float + The angular adjustment in degrees, to be added to the + altitude angle to correct for atmospheric lensing. + This will be less than or equal to zero. + """ + if bent_altitude < -90.0 or bent_altitude > +90.0: + return 0.0 # No attempt to correct an invalid altitude + # Find the pre-adjusted altitude whose refraction correction leads to 'altitude'. + altitude = bent_altitude - RefractionAngle(refraction, bent_altitude) + while True: + # See how close we got. Keep iterating until the solution converges. + diff = (altitude + RefractionAngle(refraction, altitude)) - bent_altitude + if abs(diff) < 1.0e-14: + return altitude - bent_altitude + altitude -= diff + class EclipticCoordinates: """Ecliptic angular and Cartesian coordinates. @@ -1647,6 +1683,8 @@ def Ecliptic(equ): on 1 January 2000), this function converts those coordinates to J2000 ecliptic coordinates, which are relative to the plane of the Earth's orbit around the Sun. + Parameters + ---------- equ : EquatorialCoordinates Equatorial coordinates in the J2000 frame of reference. @@ -2983,3 +3021,28 @@ def NextLunarApsis(apsis): if next.kind + apsis.kind != 1: raise InternalError() return next + +def RotateVector(rotation, vector): + """Applies a rotation to a vector, yielding a rotated vector. + + This function transforms a vector in one orientation to a vector + in another orientation. + + Parameters + ---------- + rotation : RotationMatrix + A rotation matrix that specifies how the orientation of the vector is to be changed. + vector : Vector + The vector whose orientation is to be changed. + + Returns + ------- + Vector + A vector in the orientation specified by `rotation`. + """ + return Vector( + rotation.rot[0][0]*vector.x + rotation.rot[1][0]*vector.y + rotation.rot[2][0]*vector.z, + rotation.rot[0][1]*vector.x + rotation.rot[1][1]*vector.y + rotation.rot[2][1]*vector.z, + rotation.rot[0][2]*vector.x + rotation.rot[1][2]*vector.y + rotation.rot[2][2]*vector.z, + vector.t + ) diff --git a/source/python/README.md b/source/python/README.md index eedffedd..61fac607 100644 --- a/source/python/README.md +++ b/source/python/README.md @@ -518,8 +518,10 @@ Otherwise, returns `Body.Invalid`. Given coordinates relative to the Earth's equator at J2000 (the instant of noon UTC on 1 January 2000), this function converts those coordinates to J2000 ecliptic coordinates, which are relative to the plane of the Earth's orbit around the Sun. -equ : EquatorialCoordinates - Equatorial coordinates in the J2000 frame of reference. + +| Type | Parameter | Description | +| --- | --- | --- | +| [`EquatorialCoordinates`](#EquatorialCoordinates) | `equ` | Equatorial coordinates in the J2000 frame of reference. | ### Returns: #EclipticCoordinates Ecliptic coordinates in the J2000 frame of reference. @@ -755,6 +757,29 @@ the rings appear edge-on, and are thus nearly invisible from the Earth. The `rin --- + +### InverseRefractionAngle(refraction, bent_altitude) + +**Calculates the inverse of an atmospheric refraction angle.** + +Given an observed altitude angle that includes atmospheric refraction, +calculate the negative angular correction to obtain the unrefracted +altitude. This is useful for cases where observed horizontal +coordinates are to be converted to another orientation system, +but refraction first must be removed from the observed position. + +| Type | Parameter | Description | +| --- | --- | --- | +| [`Refraction`](#Refraction) | `refraction` | `Refraction.Normal` - corrects for atmospheric refraction (recommended). `Refraction.Airless` - no correction is performed. `Refraction.JplHorizons` - For JPL Horizons compatibility testing only. | +| `float` | `bent_altitude` | The apparent altitude that includes atmospheric refraction. | + +### Returns: float +The angular adjustment in degrees, to be added to the +altitude angle to correct for atmospheric lensing. +This will be less than or equal to zero. + +--- + ### LongitudeFromSun(body, time) @@ -868,6 +893,24 @@ option selected by the `refraction` parameter. --- + +### RotateVector(rotation, vector) + +**Applies a rotation to a vector, yielding a rotated vector.** + +This function transforms a vector in one orientation to a vector +in another orientation. + +| Type | Parameter | Description | +| --- | --- | --- | +| [`RotationMatrix`](#RotationMatrix) | `rotation` | A rotation matrix that specifies how the orientation of the vector is to be changed. | +| [`Vector`](#Vector) | `vector` | The vector whose orientation is to be changed. | + +### Returns: Vector +A vector in the orientation specified by `rotation`. + +--- + ### Search(func, context, t1, t2, dt_tolerance_seconds) diff --git a/source/python/astronomy.py b/source/python/astronomy.py index 9a588c99..c0c26b22 100644 --- a/source/python/astronomy.py +++ b/source/python/astronomy.py @@ -3617,6 +3617,42 @@ def RefractionAngle(refraction, altitude): refr = 0.0 return refr +def InverseRefractionAngle(refraction, bent_altitude): + """Calculates the inverse of an atmospheric refraction angle. + + Given an observed altitude angle that includes atmospheric refraction, + calculate the negative angular correction to obtain the unrefracted + altitude. This is useful for cases where observed horizontal + coordinates are to be converted to another orientation system, + but refraction first must be removed from the observed position. + + Parameters + ---------- + refraction : Refraction + `Refraction.Normal` - corrects for atmospheric refraction (recommended). + `Refraction.Airless` - no correction is performed. + `Refraction.JplHorizons` - For JPL Horizons compatibility testing only. + bent_altitude : float + The apparent altitude that includes atmospheric refraction. + + Returns + ------- + float + The angular adjustment in degrees, to be added to the + altitude angle to correct for atmospheric lensing. + This will be less than or equal to zero. + """ + if bent_altitude < -90.0 or bent_altitude > +90.0: + return 0.0 # No attempt to correct an invalid altitude + # Find the pre-adjusted altitude whose refraction correction leads to 'altitude'. + altitude = bent_altitude - RefractionAngle(refraction, bent_altitude) + while True: + # See how close we got. Keep iterating until the solution converges. + diff = (altitude + RefractionAngle(refraction, altitude)) - bent_altitude + if abs(diff) < 1.0e-14: + return altitude - bent_altitude + altitude -= diff + class EclipticCoordinates: """Ecliptic angular and Cartesian coordinates. @@ -3708,6 +3744,8 @@ def Ecliptic(equ): on 1 January 2000), this function converts those coordinates to J2000 ecliptic coordinates, which are relative to the plane of the Earth's orbit around the Sun. + Parameters + ---------- equ : EquatorialCoordinates Equatorial coordinates in the J2000 frame of reference. @@ -5044,3 +5082,28 @@ def NextLunarApsis(apsis): if next.kind + apsis.kind != 1: raise InternalError() return next + +def RotateVector(rotation, vector): + """Applies a rotation to a vector, yielding a rotated vector. + + This function transforms a vector in one orientation to a vector + in another orientation. + + Parameters + ---------- + rotation : RotationMatrix + A rotation matrix that specifies how the orientation of the vector is to be changed. + vector : Vector + The vector whose orientation is to be changed. + + Returns + ------- + Vector + A vector in the orientation specified by `rotation`. + """ + return Vector( + rotation.rot[0][0]*vector.x + rotation.rot[1][0]*vector.y + rotation.rot[2][0]*vector.z, + rotation.rot[0][1]*vector.x + rotation.rot[1][1]*vector.y + rotation.rot[2][1]*vector.z, + rotation.rot[0][2]*vector.x + rotation.rot[1][2]*vector.y + rotation.rot[2][2]*vector.z, + vector.t + )