diff --git a/demo/nodejs/calendar/astronomy.ts b/demo/nodejs/calendar/astronomy.ts
index 6ba14d9d..342688e2 100644
--- a/demo/nodejs/calendar/astronomy.ts
+++ b/demo/nodejs/calendar/astronomy.ts
@@ -4038,7 +4038,7 @@ export function HelioDistance(body: Body, date: FlexibleDateTime): number {
export abstract class PositionFunction {
/**
* @brief Returns a relative position vector for a given time.
- * @param {Body} time
+ * @param {AstroTime} time
* The time at which to evaluate a relative position vector.
*/
abstract Position(time: AstroTime): Vector;
diff --git a/demo/python/astronomy.py b/demo/python/astronomy.py
index 918ff944..4a3db707 100644
--- a/demo/python/astronomy.py
+++ b/demo/python/astronomy.py
@@ -35,6 +35,7 @@ import math
import datetime
import enum
import re
+import abc
def _cbrt(x):
if x < 0.0:
@@ -4425,6 +4426,161 @@ def HelioDistance(body, time):
return HelioVector(body, time).Length()
+class PositionFunction(abc.ABC):
+ """A function for which to solve a light-travel time problem.
+
+ This abstract class defines the contract for wrapping a
+ position vector as a function of time. A class derived from
+ `PositionFunction` must define a `Position` method that
+ returns a position vector for a given time.
+
+ The function #CorrectLightTravel solves a generalized
+ problem of deducing how far in the past light must have
+ left a target object to be seen by an observer at a
+ specified time. It is passed an instance of `PositionFunction`
+ that expresses a relative position vector function.
+ """
+ def __init__(self):
+ pass
+
+ @abc.abstractmethod
+ def Position(self, time):
+ """Returns a relative position vector for a given time.
+
+ Parameters
+ ----------
+ time : Time
+ The time at which to evaluate a relative position vector.
+
+ Returns
+ -------
+ Vector
+ """
+
+def CorrectLightTravel(func, time):
+ """Solve for light travel time of a vector function.
+
+ When observing a distant object, for example Jupiter as seen from Earth,
+ the amount of time it takes for light to travel from the object to the
+ observer can significantly affect the object's apparent position.
+ This function is a generic solver that figures out how long in the
+ past light must have left the observed object to reach the observer
+ at the specified observation time. It uses #PositionFunction
+ to express an arbitrary position vector as a function of time.
+
+ This function repeatedly calls `func.Position`, passing a series of time
+ estimates in the past. Then `func.Position` must return a relative state vector between
+ the observer and the target. `CorrectLightTravel` keeps calling
+ `func.Position` with more and more refined estimates of the time light must have
+ left the target to arrive at the observer.
+
+ For common use cases, it is simpler to use #BackdatePosition
+ for calculating the light travel time correction of one body observing another body.
+
+ Parameters
+ ----------
+ func : PositionFunction
+ An arbitrary position vector as a function of time.
+
+ time : Time
+ The observation time for which to solve for light travel delay.
+
+ Returns
+ -------
+ Vector
+ The position vector at the solved backdated time.
+ The `t` field holds the time that light left the observed
+ body to arrive at the observer at the observation time.
+ """
+ ltime = time
+ for _ in range(10):
+ pos = func.Position(ltime)
+ ltime2 = time.AddDays(-pos.Length() / C_AUDAY)
+ dt = abs(ltime2.tt - ltime.tt)
+ if dt < 1.0e-9: # 86.4 microseconds
+ return pos
+ ltime = ltime2
+ raise NoConvergeError()
+
+
+class _BodyPosition(PositionFunction):
+ def __init__(self, observerBody, targetBody, aberration, observerPos):
+ super().__init__()
+ self.observerBody = observerBody
+ self.targetBody = targetBody
+ self.aberration = aberration
+ self.observerPos = observerPos
+
+ def Position(self, time):
+ if self.aberration:
+ # The following discussion is worded with the observer body being the Earth,
+ # which is often the case. However, the same reasoning applies to any observer body
+ # without loss of generality.
+ #
+ # To include aberration, make a good first-order approximation
+ # by backdating the Earth's position also.
+ # This is confusing, but it works for objects within the Solar System
+ # because the distance the Earth moves in that small amount of light
+ # travel time (a few minutes to a few hours) is well approximated
+ # by a line segment that substends the angle seen from the remote
+ # body viewing Earth. That angle is pretty close to the aberration
+ # angle of the moving Earth viewing the remote body.
+ # In other words, both of the following approximate the aberration angle:
+ # (transverse distance Earth moves) / (distance to body)
+ # (transverse speed of Earth) / (speed of light).
+ observerPos = HelioVector(self.observerBody, time)
+ else:
+ # No aberration, so use the pre-calculated initial position of
+ # the observer body that is already stored in this object.
+ observerPos = self.observerPos
+ # Subtract the bodies' heliocentric positions to obtain a relative position vector.
+ return HelioVector(self.targetBody, time) - observerPos
+
+
+def BackdatePosition(time, observerBody, targetBody, aberration):
+ """Solve for light travel time correction of apparent position.
+
+ When observing a distant object, for example Jupiter as seen from Earth,
+ the amount of time it takes for light to travel from the object to the
+ observer can significantly affect the object's apparent position.
+
+ This function solves the light travel time correction for the apparent
+ relative position vector of a target body as seen by an observer body
+ at a given observation time.
+
+ For a more generalized light travel correction solver, see #CorrectLightTravel.
+
+ Parameters
+ ----------
+ time : Time
+ The time of observation.
+ observerBody : Body
+ The body to be used as the observation location.
+ targetBody : Body
+ The body to be observed.
+ aberration : bool
+ `True` to correct for aberration, or `False` to leave uncorrected.
+
+ Returns
+ -------
+ Vector
+ The position vector at the solved backdated time.
+ Its `t` field holds the time that light left the observed
+ body to arrive at the observer at the observation time.
+ """
+ if aberration:
+ # With aberration, `BackdatePosition` will calculate `observerPos` at different times.
+ # Therefore, do not waste time calculating it now.
+ # Provide a placeholder value.
+ observerPos = None
+ else:
+ # Without aberration, we need the observer body position at the observation time only.
+ # For efficiency, calculate it once and hold onto it, so `BodyPosition` can keep using it.
+ observerPos = HelioVector(observerBody, time)
+ func = _BodyPosition(observerBody, targetBody, aberration, observerPos)
+ return CorrectLightTravel(func, time)
+
+
def GeoVector(body, time, aberration):
"""Calculates geocentric Cartesian coordinates of a body in the J2000 equatorial system.
@@ -4435,7 +4591,7 @@ def GeoVector(body, time, aberration):
If given an invalid value for `body`, this function will raise an exception.
- Unlike #HelioVector, this function always corrects for light travel time.
+ Unlike #HelioVector, this function corrects for light travel time.
This means the position of the body is "back-dated" by the amount of time it takes
light to travel from that body to an observer on the Earth.
@@ -4464,37 +4620,8 @@ def GeoVector(body, time, aberration):
if body == Body.Earth:
return Vector(0.0, 0.0, 0.0, time)
- if not aberration:
- # No aberration, so calculate Earth's position once, at the time of observation.
- earth = _CalcEarth(time)
-
# Correct for light-travel time, to get position of body as seen from Earth's center.
- ltime = time
- for _ in range(10):
- h = HelioVector(body, ltime)
- if aberration:
- # Include aberration, so make a good first-order approximation
- # by backdating the Earth's position also.
- # This is confusing, but it works for objects within the Solar System
- # because the distance the Earth moves in that small amount of light
- # travel time (a few minutes to a few hours) is well approximated
- # by a line segment that substends the angle seen from the remote
- # body viewing Earth. That angle is pretty close to the aberration
- # angle of the moving Earth viewing the remote body.
- # In other words, both of the following approximate the aberration angle:
- # (transverse distance Earth moves) / (distance to body)
- # (transverse speed of Earth) / (speed of light).
- earth = _CalcEarth(ltime)
-
- geo = Vector(h.x-earth.x, h.y-earth.y, h.z-earth.z, time)
- ltime2 = time.AddDays(-geo.Length() / C_AUDAY)
- dt = abs(ltime2.tt - ltime.tt)
- if dt < 1.0e-9:
- return geo
-
- ltime = ltime2
-
- raise Error('Light-travel time solver did not converge: dt={}'.format(dt))
+ return BackdatePosition(time, Body.Earth, body, aberration)
def _ExportState(terse, time):
diff --git a/generate/template/astronomy.cs b/generate/template/astronomy.cs
index c1b36115..881b34e2 100644
--- a/generate/template/astronomy.cs
+++ b/generate/template/astronomy.cs
@@ -4438,7 +4438,7 @@ $ASTRO_IAU_DATA()
/// `Aberration.Corrected` to correct for aberration, or `Aberration.None` to leave uncorrected.
///
/// The position vector at the solved backdated time.
- /// The `t` field holds the time that light left the observed
+ /// Its `t` field holds the time that light left the observed
/// body to arrive at the observer at the observation time.
///
public static AstroVector BackdatePosition(
diff --git a/generate/template/astronomy.py b/generate/template/astronomy.py
index f96f2546..23d42183 100644
--- a/generate/template/astronomy.py
+++ b/generate/template/astronomy.py
@@ -35,6 +35,7 @@ import math
import datetime
import enum
import re
+import abc
def _cbrt(x):
if x < 0.0:
@@ -2383,6 +2384,161 @@ def HelioDistance(body, time):
return HelioVector(body, time).Length()
+class PositionFunction(abc.ABC):
+ """A function for which to solve a light-travel time problem.
+
+ This abstract class defines the contract for wrapping a
+ position vector as a function of time. A class derived from
+ `PositionFunction` must define a `Position` method that
+ returns a position vector for a given time.
+
+ The function #CorrectLightTravel solves a generalized
+ problem of deducing how far in the past light must have
+ left a target object to be seen by an observer at a
+ specified time. It is passed an instance of `PositionFunction`
+ that expresses a relative position vector function.
+ """
+ def __init__(self):
+ pass
+
+ @abc.abstractmethod
+ def Position(self, time):
+ """Returns a relative position vector for a given time.
+
+ Parameters
+ ----------
+ time : Time
+ The time at which to evaluate a relative position vector.
+
+ Returns
+ -------
+ Vector
+ """
+
+def CorrectLightTravel(func, time):
+ """Solve for light travel time of a vector function.
+
+ When observing a distant object, for example Jupiter as seen from Earth,
+ the amount of time it takes for light to travel from the object to the
+ observer can significantly affect the object's apparent position.
+ This function is a generic solver that figures out how long in the
+ past light must have left the observed object to reach the observer
+ at the specified observation time. It uses #PositionFunction
+ to express an arbitrary position vector as a function of time.
+
+ This function repeatedly calls `func.Position`, passing a series of time
+ estimates in the past. Then `func.Position` must return a relative state vector between
+ the observer and the target. `CorrectLightTravel` keeps calling
+ `func.Position` with more and more refined estimates of the time light must have
+ left the target to arrive at the observer.
+
+ For common use cases, it is simpler to use #BackdatePosition
+ for calculating the light travel time correction of one body observing another body.
+
+ Parameters
+ ----------
+ func : PositionFunction
+ An arbitrary position vector as a function of time.
+
+ time : Time
+ The observation time for which to solve for light travel delay.
+
+ Returns
+ -------
+ Vector
+ The position vector at the solved backdated time.
+ The `t` field holds the time that light left the observed
+ body to arrive at the observer at the observation time.
+ """
+ ltime = time
+ for _ in range(10):
+ pos = func.Position(ltime)
+ ltime2 = time.AddDays(-pos.Length() / C_AUDAY)
+ dt = abs(ltime2.tt - ltime.tt)
+ if dt < 1.0e-9: # 86.4 microseconds
+ return pos
+ ltime = ltime2
+ raise NoConvergeError()
+
+
+class _BodyPosition(PositionFunction):
+ def __init__(self, observerBody, targetBody, aberration, observerPos):
+ super().__init__()
+ self.observerBody = observerBody
+ self.targetBody = targetBody
+ self.aberration = aberration
+ self.observerPos = observerPos
+
+ def Position(self, time):
+ if self.aberration:
+ # The following discussion is worded with the observer body being the Earth,
+ # which is often the case. However, the same reasoning applies to any observer body
+ # without loss of generality.
+ #
+ # To include aberration, make a good first-order approximation
+ # by backdating the Earth's position also.
+ # This is confusing, but it works for objects within the Solar System
+ # because the distance the Earth moves in that small amount of light
+ # travel time (a few minutes to a few hours) is well approximated
+ # by a line segment that substends the angle seen from the remote
+ # body viewing Earth. That angle is pretty close to the aberration
+ # angle of the moving Earth viewing the remote body.
+ # In other words, both of the following approximate the aberration angle:
+ # (transverse distance Earth moves) / (distance to body)
+ # (transverse speed of Earth) / (speed of light).
+ observerPos = HelioVector(self.observerBody, time)
+ else:
+ # No aberration, so use the pre-calculated initial position of
+ # the observer body that is already stored in this object.
+ observerPos = self.observerPos
+ # Subtract the bodies' heliocentric positions to obtain a relative position vector.
+ return HelioVector(self.targetBody, time) - observerPos
+
+
+def BackdatePosition(time, observerBody, targetBody, aberration):
+ """Solve for light travel time correction of apparent position.
+
+ When observing a distant object, for example Jupiter as seen from Earth,
+ the amount of time it takes for light to travel from the object to the
+ observer can significantly affect the object's apparent position.
+
+ This function solves the light travel time correction for the apparent
+ relative position vector of a target body as seen by an observer body
+ at a given observation time.
+
+ For a more generalized light travel correction solver, see #CorrectLightTravel.
+
+ Parameters
+ ----------
+ time : Time
+ The time of observation.
+ observerBody : Body
+ The body to be used as the observation location.
+ targetBody : Body
+ The body to be observed.
+ aberration : bool
+ `True` to correct for aberration, or `False` to leave uncorrected.
+
+ Returns
+ -------
+ Vector
+ The position vector at the solved backdated time.
+ Its `t` field holds the time that light left the observed
+ body to arrive at the observer at the observation time.
+ """
+ if aberration:
+ # With aberration, `BackdatePosition` will calculate `observerPos` at different times.
+ # Therefore, do not waste time calculating it now.
+ # Provide a placeholder value.
+ observerPos = None
+ else:
+ # Without aberration, we need the observer body position at the observation time only.
+ # For efficiency, calculate it once and hold onto it, so `BodyPosition` can keep using it.
+ observerPos = HelioVector(observerBody, time)
+ func = _BodyPosition(observerBody, targetBody, aberration, observerPos)
+ return CorrectLightTravel(func, time)
+
+
def GeoVector(body, time, aberration):
"""Calculates geocentric Cartesian coordinates of a body in the J2000 equatorial system.
@@ -2393,7 +2549,7 @@ def GeoVector(body, time, aberration):
If given an invalid value for `body`, this function will raise an exception.
- Unlike #HelioVector, this function always corrects for light travel time.
+ Unlike #HelioVector, this function corrects for light travel time.
This means the position of the body is "back-dated" by the amount of time it takes
light to travel from that body to an observer on the Earth.
@@ -2422,37 +2578,8 @@ def GeoVector(body, time, aberration):
if body == Body.Earth:
return Vector(0.0, 0.0, 0.0, time)
- if not aberration:
- # No aberration, so calculate Earth's position once, at the time of observation.
- earth = _CalcEarth(time)
-
# Correct for light-travel time, to get position of body as seen from Earth's center.
- ltime = time
- for _ in range(10):
- h = HelioVector(body, ltime)
- if aberration:
- # Include aberration, so make a good first-order approximation
- # by backdating the Earth's position also.
- # This is confusing, but it works for objects within the Solar System
- # because the distance the Earth moves in that small amount of light
- # travel time (a few minutes to a few hours) is well approximated
- # by a line segment that substends the angle seen from the remote
- # body viewing Earth. That angle is pretty close to the aberration
- # angle of the moving Earth viewing the remote body.
- # In other words, both of the following approximate the aberration angle:
- # (transverse distance Earth moves) / (distance to body)
- # (transverse speed of Earth) / (speed of light).
- earth = _CalcEarth(ltime)
-
- geo = Vector(h.x-earth.x, h.y-earth.y, h.z-earth.z, time)
- ltime2 = time.AddDays(-geo.Length() / C_AUDAY)
- dt = abs(ltime2.tt - ltime.tt)
- if dt < 1.0e-9:
- return geo
-
- ltime = ltime2
-
- raise Error('Light-travel time solver did not converge: dt={}'.format(dt))
+ return BackdatePosition(time, Body.Earth, body, aberration)
def _ExportState(terse, time):
diff --git a/generate/template/astronomy.ts b/generate/template/astronomy.ts
index cf681fed..a4be7ae1 100644
--- a/generate/template/astronomy.ts
+++ b/generate/template/astronomy.ts
@@ -3032,7 +3032,7 @@ export function HelioDistance(body: Body, date: FlexibleDateTime): number {
export abstract class PositionFunction {
/**
* @brief Returns a relative position vector for a given time.
- * @param {Body} time
+ * @param {AstroTime} time
* The time at which to evaluate a relative position vector.
*/
abstract Position(time: AstroTime): Vector;
diff --git a/source/csharp/README.md b/source/csharp/README.md
index e3fc98f2..b8eccca7 100644
--- a/source/csharp/README.md
+++ b/source/csharp/README.md
@@ -333,7 +333,7 @@ For a more generalized light travel correction solver, see [`Astronomy.CorrectLi
| [`Body`](#Body) | `targetBody` | The body to be observed. |
| [`Aberration`](#Aberration) | `aberration` | `Aberration.Corrected` to correct for aberration, or `Aberration.None` to leave uncorrected. |
-**Returns:** The position vector at the solved backdated time. The `t` field holds the time that light left the observed body to arrive at the observer at the observation time.
+**Returns:** The position vector at the solved backdated time. Its `t` field holds the time that light left the observed body to arrive at the observer at the observation time.
### Astronomy.BaryState(body, time) ⇒ [`StateVector`](#StateVector)
diff --git a/source/csharp/astronomy.cs b/source/csharp/astronomy.cs
index 3cd197ee..5976d52e 100644
--- a/source/csharp/astronomy.cs
+++ b/source/csharp/astronomy.cs
@@ -5650,7 +5650,7 @@ namespace CosineKitty
/// `Aberration.Corrected` to correct for aberration, or `Aberration.None` to leave uncorrected.
///
/// The position vector at the solved backdated time.
- /// The `t` field holds the time that light left the observed
+ /// Its `t` field holds the time that light left the observed
/// body to arrive at the observer at the observation time.
///
public static AstroVector BackdatePosition(
diff --git a/source/js/astronomy.d.ts b/source/js/astronomy.d.ts
index c6a33e53..3cff64a9 100644
--- a/source/js/astronomy.d.ts
+++ b/source/js/astronomy.d.ts
@@ -965,7 +965,7 @@ export declare function HelioDistance(body: Body, date: FlexibleDateTime): numbe
export declare abstract class PositionFunction {
/**
* @brief Returns a relative position vector for a given time.
- * @param {Body} time
+ * @param {AstroTime} time
* The time at which to evaluate a relative position vector.
*/
abstract Position(time: AstroTime): Vector;
diff --git a/source/js/astronomy.ts b/source/js/astronomy.ts
index 6ba14d9d..342688e2 100644
--- a/source/js/astronomy.ts
+++ b/source/js/astronomy.ts
@@ -4038,7 +4038,7 @@ export function HelioDistance(body: Body, date: FlexibleDateTime): number {
export abstract class PositionFunction {
/**
* @brief Returns a relative position vector for a given time.
- * @param {Body} time
+ * @param {AstroTime} time
* The time at which to evaluate a relative position vector.
*/
abstract Position(time: AstroTime): Vector;
diff --git a/source/python/README.md b/source/python/README.md
index 0b72de96..4f18edea 100644
--- a/source/python/README.md
+++ b/source/python/README.md
@@ -763,6 +763,36 @@ to report information about the center of the Moon passing through the ecliptic
---
+
+### class PositionFunction
+
+**A function for which to solve a light-travel time problem.**
+
+This abstract class defines the contract for wrapping a
+position vector as a function of time. A class derived from
+`PositionFunction` must define a `Position` method that
+returns a position vector for a given time.
+The function [`CorrectLightTravel`](#CorrectLightTravel) solves a generalized
+problem of deducing how far in the past light must have
+left a target object to be seen by an observer at a
+specified time. It is passed an instance of `PositionFunction`
+that expresses a relative position vector function.
+
+#### member functions
+
+
+### PositionFunction.Position(self, time)
+
+**Returns a relative position vector for a given time.**
+
+| Type | Parameter | Description |
+| --- | --- | --- |
+| [`Time`](#Time) | `time` | The time at which to evaluate a relative position vector. |
+
+**Returns**: [`Vector`](#Vector)
+
+---
+
### class RotationMatrix
@@ -1218,6 +1248,33 @@ and the specified body as seen from the center of the Earth.
---
+
+### BackdatePosition(time, observerBody, targetBody, aberration)
+
+**Solve for light travel time correction of apparent position.**
+
+When observing a distant object, for example Jupiter as seen from Earth,
+the amount of time it takes for light to travel from the object to the
+observer can significantly affect the object's apparent position.
+This function solves the light travel time correction for the apparent
+relative position vector of a target body as seen by an observer body
+at a given observation time.
+For a more generalized light travel correction solver, see [`CorrectLightTravel`](#CorrectLightTravel).
+
+| Type | Parameter | Description |
+| --- | --- | --- |
+| [`Time`](#Time) | `time` | The time of observation. |
+| [`Body`](#Body) | `observerBody` | The body to be used as the observation location. |
+| [`Body`](#Body) | `targetBody` | The body to be observed. |
+| `bool` | `aberration` | `True` to correct for aberration, or `False` to leave uncorrected. |
+
+**Returns**: [`Vector`](#Vector)
+The position vector at the solved backdated time.
+Its `t` field holds the time that light left the observed
+body to arrive at the observer at the observation time.
+
+---
+
### BaryState(body, time)
@@ -1296,6 +1353,39 @@ the converted B1875 (ra,dec) for that point.
---
+
+### CorrectLightTravel(func, time)
+
+**Solve for light travel time of a vector function.**
+
+When observing a distant object, for example Jupiter as seen from Earth,
+the amount of time it takes for light to travel from the object to the
+observer can significantly affect the object's apparent position.
+This function is a generic solver that figures out how long in the
+past light must have left the observed object to reach the observer
+at the specified observation time. It uses [`PositionFunction`](#PositionFunction)
+to express an arbitrary position vector as a function of time.
+This function repeatedly calls `func.Position`, passing a series of time
+estimates in the past. Then `func.Position` must return a relative state vector between
+the observer and the target. `CorrectLightTravel` keeps calling
+`func.Position` with more and more refined estimates of the time light must have
+left the target to arrive at the observer.
+For common use cases, it is simpler to use [`BackdatePosition`](#BackdatePosition)
+for calculating the light travel time correction of one body observing another body.
+time : Time
+ The observation time for which to solve for light travel delay.
+
+| Type | Parameter | Description |
+| --- | --- | --- |
+| [`PositionFunction`](#PositionFunction) | `func` | An arbitrary position vector as a function of time. |
+
+**Returns**: [`Vector`](#Vector)
+The position vector at the solved backdated time.
+The `t` field holds the time that light left the observed
+body to arrive at the observer at the observation time.
+
+---
+
### DeltaT_EspenakMeeus(ut)
@@ -1526,7 +1616,7 @@ using the center of the Earth as the origin. The result is expressed as a Carte
vector in the J2000 equatorial system: the coordinates are based on the mean equator
of the Earth at noon UTC on 1 January 2000.
If given an invalid value for `body`, this function will raise an exception.
-Unlike [`HelioVector`](#HelioVector), this function always corrects for light travel time.
+Unlike [`HelioVector`](#HelioVector), this function corrects for light travel time.
This means the position of the body is "back-dated" by the amount of time it takes
light to travel from that body to an observer on the Earth.
Also, the position can optionally be corrected for
diff --git a/source/python/astronomy/astronomy.py b/source/python/astronomy/astronomy.py
index 918ff944..4a3db707 100644
--- a/source/python/astronomy/astronomy.py
+++ b/source/python/astronomy/astronomy.py
@@ -35,6 +35,7 @@ import math
import datetime
import enum
import re
+import abc
def _cbrt(x):
if x < 0.0:
@@ -4425,6 +4426,161 @@ def HelioDistance(body, time):
return HelioVector(body, time).Length()
+class PositionFunction(abc.ABC):
+ """A function for which to solve a light-travel time problem.
+
+ This abstract class defines the contract for wrapping a
+ position vector as a function of time. A class derived from
+ `PositionFunction` must define a `Position` method that
+ returns a position vector for a given time.
+
+ The function #CorrectLightTravel solves a generalized
+ problem of deducing how far in the past light must have
+ left a target object to be seen by an observer at a
+ specified time. It is passed an instance of `PositionFunction`
+ that expresses a relative position vector function.
+ """
+ def __init__(self):
+ pass
+
+ @abc.abstractmethod
+ def Position(self, time):
+ """Returns a relative position vector for a given time.
+
+ Parameters
+ ----------
+ time : Time
+ The time at which to evaluate a relative position vector.
+
+ Returns
+ -------
+ Vector
+ """
+
+def CorrectLightTravel(func, time):
+ """Solve for light travel time of a vector function.
+
+ When observing a distant object, for example Jupiter as seen from Earth,
+ the amount of time it takes for light to travel from the object to the
+ observer can significantly affect the object's apparent position.
+ This function is a generic solver that figures out how long in the
+ past light must have left the observed object to reach the observer
+ at the specified observation time. It uses #PositionFunction
+ to express an arbitrary position vector as a function of time.
+
+ This function repeatedly calls `func.Position`, passing a series of time
+ estimates in the past. Then `func.Position` must return a relative state vector between
+ the observer and the target. `CorrectLightTravel` keeps calling
+ `func.Position` with more and more refined estimates of the time light must have
+ left the target to arrive at the observer.
+
+ For common use cases, it is simpler to use #BackdatePosition
+ for calculating the light travel time correction of one body observing another body.
+
+ Parameters
+ ----------
+ func : PositionFunction
+ An arbitrary position vector as a function of time.
+
+ time : Time
+ The observation time for which to solve for light travel delay.
+
+ Returns
+ -------
+ Vector
+ The position vector at the solved backdated time.
+ The `t` field holds the time that light left the observed
+ body to arrive at the observer at the observation time.
+ """
+ ltime = time
+ for _ in range(10):
+ pos = func.Position(ltime)
+ ltime2 = time.AddDays(-pos.Length() / C_AUDAY)
+ dt = abs(ltime2.tt - ltime.tt)
+ if dt < 1.0e-9: # 86.4 microseconds
+ return pos
+ ltime = ltime2
+ raise NoConvergeError()
+
+
+class _BodyPosition(PositionFunction):
+ def __init__(self, observerBody, targetBody, aberration, observerPos):
+ super().__init__()
+ self.observerBody = observerBody
+ self.targetBody = targetBody
+ self.aberration = aberration
+ self.observerPos = observerPos
+
+ def Position(self, time):
+ if self.aberration:
+ # The following discussion is worded with the observer body being the Earth,
+ # which is often the case. However, the same reasoning applies to any observer body
+ # without loss of generality.
+ #
+ # To include aberration, make a good first-order approximation
+ # by backdating the Earth's position also.
+ # This is confusing, but it works for objects within the Solar System
+ # because the distance the Earth moves in that small amount of light
+ # travel time (a few minutes to a few hours) is well approximated
+ # by a line segment that substends the angle seen from the remote
+ # body viewing Earth. That angle is pretty close to the aberration
+ # angle of the moving Earth viewing the remote body.
+ # In other words, both of the following approximate the aberration angle:
+ # (transverse distance Earth moves) / (distance to body)
+ # (transverse speed of Earth) / (speed of light).
+ observerPos = HelioVector(self.observerBody, time)
+ else:
+ # No aberration, so use the pre-calculated initial position of
+ # the observer body that is already stored in this object.
+ observerPos = self.observerPos
+ # Subtract the bodies' heliocentric positions to obtain a relative position vector.
+ return HelioVector(self.targetBody, time) - observerPos
+
+
+def BackdatePosition(time, observerBody, targetBody, aberration):
+ """Solve for light travel time correction of apparent position.
+
+ When observing a distant object, for example Jupiter as seen from Earth,
+ the amount of time it takes for light to travel from the object to the
+ observer can significantly affect the object's apparent position.
+
+ This function solves the light travel time correction for the apparent
+ relative position vector of a target body as seen by an observer body
+ at a given observation time.
+
+ For a more generalized light travel correction solver, see #CorrectLightTravel.
+
+ Parameters
+ ----------
+ time : Time
+ The time of observation.
+ observerBody : Body
+ The body to be used as the observation location.
+ targetBody : Body
+ The body to be observed.
+ aberration : bool
+ `True` to correct for aberration, or `False` to leave uncorrected.
+
+ Returns
+ -------
+ Vector
+ The position vector at the solved backdated time.
+ Its `t` field holds the time that light left the observed
+ body to arrive at the observer at the observation time.
+ """
+ if aberration:
+ # With aberration, `BackdatePosition` will calculate `observerPos` at different times.
+ # Therefore, do not waste time calculating it now.
+ # Provide a placeholder value.
+ observerPos = None
+ else:
+ # Without aberration, we need the observer body position at the observation time only.
+ # For efficiency, calculate it once and hold onto it, so `BodyPosition` can keep using it.
+ observerPos = HelioVector(observerBody, time)
+ func = _BodyPosition(observerBody, targetBody, aberration, observerPos)
+ return CorrectLightTravel(func, time)
+
+
def GeoVector(body, time, aberration):
"""Calculates geocentric Cartesian coordinates of a body in the J2000 equatorial system.
@@ -4435,7 +4591,7 @@ def GeoVector(body, time, aberration):
If given an invalid value for `body`, this function will raise an exception.
- Unlike #HelioVector, this function always corrects for light travel time.
+ Unlike #HelioVector, this function corrects for light travel time.
This means the position of the body is "back-dated" by the amount of time it takes
light to travel from that body to an observer on the Earth.
@@ -4464,37 +4620,8 @@ def GeoVector(body, time, aberration):
if body == Body.Earth:
return Vector(0.0, 0.0, 0.0, time)
- if not aberration:
- # No aberration, so calculate Earth's position once, at the time of observation.
- earth = _CalcEarth(time)
-
# Correct for light-travel time, to get position of body as seen from Earth's center.
- ltime = time
- for _ in range(10):
- h = HelioVector(body, ltime)
- if aberration:
- # Include aberration, so make a good first-order approximation
- # by backdating the Earth's position also.
- # This is confusing, but it works for objects within the Solar System
- # because the distance the Earth moves in that small amount of light
- # travel time (a few minutes to a few hours) is well approximated
- # by a line segment that substends the angle seen from the remote
- # body viewing Earth. That angle is pretty close to the aberration
- # angle of the moving Earth viewing the remote body.
- # In other words, both of the following approximate the aberration angle:
- # (transverse distance Earth moves) / (distance to body)
- # (transverse speed of Earth) / (speed of light).
- earth = _CalcEarth(ltime)
-
- geo = Vector(h.x-earth.x, h.y-earth.y, h.z-earth.z, time)
- ltime2 = time.AddDays(-geo.Length() / C_AUDAY)
- dt = abs(ltime2.tt - ltime.tt)
- if dt < 1.0e-9:
- return geo
-
- ltime = ltime2
-
- raise Error('Light-travel time solver did not converge: dt={}'.format(dt))
+ return BackdatePosition(time, Body.Earth, body, aberration)
def _ExportState(terse, time):