One of the main goals of this project was to make the ephemerides functions easily available to an end user. Ephemerides basically refers to an object in space whose motion is being tracked and observed. SPICE required you to provide the following parameters to observe a body in space.
Target: The body of interest
Frame: A rotational frame of reference (Default is J2000 [Not to be confused with the J2000 epoch])
Observer: An observing body whose viewpoint is used to chart the vector
Epoch: An epoch in Ephemeris Time
SPICE has an integer-key convention for the kind of bodies that it
has support for. Each body can be referenced via a string or an
integer id. While there isn’t an actual strict range for integer ID
classification, it is mentioned here and can be summed up
in the following
elsif clauses. (In Ruby constant strings
are better off as symbols, so the constructor takes either an integer
ID of a string symbol)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
It was very tempting to involve inheritance and extend a base Body
class onto these potential classes, but I simply did not see the need
for it at this point. The way it is at the moment, the
has a reader attribute type that stores some metadata about the body
for the user’s convenience. Perhaps as coverage of SPICE improves,
this minor thing can be changed later on.
To create a Body object, you instantiate with either a body name or a
body id. Certain bodies such as instruments will require additional
kernels to be loaded. To proceed seamlessly, load a leap seconds
kernel, a planetary constants kernel, and an ephemeris kernel. (All
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
:earth map to the same body in SPICE data. The frame of
reference can also be specified as a named parameter during
instantiation to set a custom default frame for that particular
1 2 3 4 5 6
In SPICE, a
state is a 6 length column vector that stores position
and velocity in 3D cartesian co-ordinates
As a base case, let’s find out the the position of the Earth with respect to itself.
1 2 3 4 5
The origin as seen from itself is still the origin, so this makes
sense. The methods
#state_at take an identical
set of parameters. While there is a bit of redundancy going on,
splitting them makes the API more elegant, but the basic relationship
between these three vectors is the following :-
1 2 3 4
One thing to note is that state/velocity/position vectors will always
be returned as an
NMatrix object, SciRuby’s numerical matrix core,
to allow for calculations via the NMatrix API.
As an example that is used in the code, one line can turn a position vector into distance from origin (here using Euclidean distance):
As a simple imprecise experiment, let’s find out how the speed of light can be “estimated” up with this data.
1 2 3 4 5 6 7 8
The unit of distance here is kilometers, so the speed of light by this measurement is about pretty close to the textbook figure of 3e+8 m/s.
There is also a function to check if a list of bodies are within a
radial proximity from an observing body. We already calculated the
distance of the moon to be about 367,000 km. The function
within_proximity returns a list of all bodies that are within the
specified radial distance from the calling body object.
1 2 3 4 5 6 7 8
Now that we’ve come to the end of the functionality, I would like to
mention that there is another named argument
which is basically an error reduction method to provide a more
accurate result than the default observation. The default
option for aberration correction basically provides the geometric
observations without any corrections for reception or transmission of
photons. For a list of various aberration correction methods
available, have a look at the documentation for spkpos_c to
find out if you need an aberration correction on SPICE data.
1 2 3 4 5 6 7
If you want to look at it another way, no aberration correction would give you the textbook response of rigid geometry, while introducing an aberration correction would give you a somewhat more realistic output accounting for the errors that do happen when these observations are made.
Finally, if you need to generate a continuous time series for a body,
SpiceRub::Time has two functions to aid in that:
1 2 3 4 5 6 7
In this case, I took a start time and an end time that was one day
after and requested 4 linearly spaced epochs. This is basically an
The other function requires you to input a start time and an end time and a step size that keeps getting added to the start time till the end time is reached. As a contrived example, we’ll take two epochs, five days apart and ask for a step size of a day, expecting six elements.
1 2 3 4 5 6 7 8 9
And that’s it for this blog post. I would appreciate any feedback
regarding this as I’ve been juggling the design back and forth very
frequently. There is large potential of expansion of the
particularly creating new classes as when different Bobdy objects
would have a corresponding function. (For example, the function
getfov_c which returns the field of view of an instrument could be
an instance function attached to the
Instrument subclass of
but this is just potential expansions in the future.)