++ed by:
TYLDUM BKB HAMMER

3 PAUSE users

Tom Wyant
and 1 contributors

NAME

Astro::Coord::ECI - Manipulate geocentric coordinates

SYNOPSIS

 use Astro::Coord::ECI;
 use Astro::Coord::ECI::Sun;
 use Astro::Coord::ECI::TLE;
 use Astro::Coord::ECI::Utils qw{rad2deg};
 # 1600 Pennsylvania Avenue, in radians, radians, and KM
 my ($lat, $lon, $elev) = (0.678911227503559,
     -1.34456123391096, 0.01668);
 # Record the time
 my $time = time ();
 # Set up observer's location
 my $loc = Astro::Coord::ECI->geodetic ($lat, $lon, $elev);
 # Instantiate the Sun.
 my $sun = Astro::Coord::ECI::Sun->universal ($time);
 # Figure out if the Sun is up at the observer's location.
 my ($azimuth, $elevation, $range) = $loc->azel ($sun);
 print "The Sun is ", rad2deg ($elevation),
     " degrees above the horizon.\n";

See the Astro::Coord::ECI::TLE documentation for an example involving satellite pass prediction.

DESCRIPTION

This module was written to provide a base class for a system to predict satellite visibility. Its main task is to convert the Earth-Centered Inertial (ECI) coordinates generated by the NORAD models into other coordinate systems (e.g. latitude, longitude, and altitude above mean sea level), but a other functions have accreted to it. In addition, a few support routines have been exposed for testing, or whatever.

All distances are in kilometers, and all angles are in radians (including right ascension, which is usually measured in hours).

Times are normal Perl times, whether used as universal or dynamical time. Universal time is what is usually meant, unless otherwise stated.

Known subclasses include Astro::Coord::ECI::Moon to predict the position of the Moon, Astro::Coord::ECI::Star to predict the position of a star, or anything else that can be considered fixed on the celestial sphere, Astro::Coord::ECI::Sun to predict the position of the Sun, Astro::Coord::ECI::TLE to predict the position of a satellite given the NORAD orbital parameters, and Astro::Coord::ECI::TLE::Iridium (a subclass of Astro::Coord::ECI::TLE) to predict Iridium flares.

Note that in version 0.022_01 the velocity code got a substantial rework, which is still in progress. I am attempting give useful velocities where they are available, and no velocities at all where they are not. Unfortunately I have yet to locate any worked problems, so the velocity code is, in the most literal sense, untested.

In general, locations specified in Earth-fixed coordinates are considered to share the rotational velocity of the Earth, and locations specified in inertial coordinates are considered to have undefined velocities unless a velocity was specified explicitly or produced by the object's model. This involved a change in the behavior of the eci() method when velocity was not specified but location was. See the next paragraph but one for my excuse.

Caveat user: This class and its subclasses should probably be considered alpha code, meaning that the public interface may not be completely stable. I will try not to change it, but given sufficient reason I will do so. If I do so, I will draw attention to the change in the documentation.

Methods

The following methods should be considered public.

$coord = Astro::Coord::ECI->new ();

This method instantiates a coordinate object. Any arguments are passed to the set() method once the object has been instantiated.

$angle = $coord->angle ($coord2, $coord3);

This method calculates the angle between the $coord2 and $coord3 objects, as seen from $coord. The calculation uses the law of haversines, and does not take atmospheric refraction into account. The return is a number of radians between 0 and pi.

The algorithm comes from "Ask Dr. Math" on Drexel's Math Forum, http://mathforum.org/library/drmath/view/51879.html, which attributes it to the Geographic Information Systems FAQ, http://www.faqs.org/faqs/geography/infosystems-faq/, which in turn attributes it to R. W. Sinnott, "Virtues of the Haversine," Sky and Telescope, volume 68, number 2, 1984, page 159.

Prior to version 0.011_03 the law of cosines was used, but this produced errors on extremely small angles. The haversine law was selected based on Jean Meeus, "Astronomical Algorithms", 2nd edition, chapter 17 "Angular Separation".

$which = $coord->attribute ($name);

This method returns the name of the class that implements the named attribute, or undef if the attribute name is not valid.

($azimuth, $elevation, $range) = $coord->azel ($coord2, $upper);

This method takes another coordinate object, and computes its azimuth, elevation, and range in reference to the object doing the computing. The return is azimuth in radians measured clockwise from North (always positive), elevation above the horizon in radians (negative if below), and range in kilometers.

If the optional 'upper' argument is true, the calculation will be of the upper limb of the object, using the 'diameter' attribute of the $coord2 object.

As a side effect, the time of the $coord object may be set from the $coord2 object.

By default, atmospheric refraction is taken into account in the calculation of elevation, using the correct_for_refraction() method. This better represents the observed position in the sky when the object is above the horizon, but produces a gap in the data when the object passes below the horizon, since I have no refraction equations for rock. See the correct_for_refraction() documentation for details.

If you want to ignore atmospheric refraction (and not have a gap in your data), set the refraction attribute of the $coord object to any value Perl considers false (e.g. 0).

The algorithm for position is the author's, but he confesses to having to refer to T. S. Kelso's "Computers and Satellites" column in "Satellite Times", November/December 1995, titled "Orbital Coordinate Systems, Part II" and available at http://celestrak.com/columns/v02n02/ to get the signs right.

If velocities are available for both bodies involved in the calculation, they will be returned after the position (i.e. you get a six-element array back instead of a three-element array). The velocity of a point specified in Earth-fixed coordinates (e.g. geodetic latitude, longitude, and altitude) is assumed to be the rotational velocity of the Earth at that point. The returned velocities are azimuthal velocity in radians per second (not radians of azimuth, which get smaller as you go farther away from the Equator), elevational velocity in radians per second, and velocity in recession in kilometers per second, in that order.

The algorithm for recessional velocity comes from John A. Magliacane's Predict program, available at http://www.qsl.net/kd2bd/predict.html. The transverse velocity computations are the author's, but use the same basic approach: vector dot product of the velocity vector with a unit vector in the appropriate direction, the latter generated by the appropriate (I hope!) vector cross products.

Note that I have no worked examples against which to validate the velocity calculations. I did compare them to the low-tech velocity computation (velocity = coordinate at time T + 1 second minus coordinate at time T) for an arbitrarily-selected orbit of the International Space Station and found the following maximum differences:

 Azimuthal velocity: 0.0025 degrees/second
 Elevational velocity: 0.0089 degrees/second
 Velocity in recession: 3.92 cm/second (!)

This indicates to me that there is something seriously wrong with my transverse velocities, but I have no clue what it is. Caveat user.

If velocities are available and you have provided a non-zero value for the frequency attribute, you will get the Doppler shift as the seventh element of the returned array. The caveats about velocity in recession apply to the Doppler shift as well.

$coord2 = $coord->clone ();

This method does a deep clone of an object, producing a different but identical object.

It's really just a wrapper for Storable::dclone.

$elevation = $coord->correct_for_refraction ($elevation);

This method corrects the given angular elevation for atmospheric refraction. This is done only if the elevation has a reasonable chance of being visible; that is, if the elevation before correction is not more than two degrees below either the 'horizon' attribute or zero, whichever is lesser. Sorry for the discontinuity thus introduced, but I did not wish to carry the refraction calculation too low because I am uncertain about the behavior of the algorithm, and I do not have a corresponding algorithm for refraction through magma.

This method can also be called as a class method, in which case the correction is applied only if the uncorrected elevation is greater than minus two degrees. It is really only exposed for testing purposes (hence the cumbersome name). The azel() method calls it for you if the refraction attribute is true.

The algorithm for atmospheric refraction comes from Thorfinn Saemundsson's article in "Sky and Telescope", volume 72, page 70 (July 1986) as reported Jean Meeus' "Astronomical Algorithms", 2nd Edition, chapter 16, page 106, and includes the adjustment suggested by Meeus.

$angle = $coord->dip ();

This method calculates the dip angle of the horizon due to the altitude of the body, in radians. It will be negative for a location above the surface of the reference ellipsoid, and positive for a location below the surface.

The algorithm is simple enough to be the author's.

$coord = $coord->dynamical ($time);

This method sets the dynamical time represented by the object.

This method can also be called as a class method, in which case it instantiates the desired object.

The algorithm for converting this to universal time comes from Jean Meeus' "Astronomical Algorithms", 2nd Edition, Chapter 10, pages 78ff.

$time = $coord->dynamical ();

This method returns the dynamical time previously set, or the universal time previously set, converted to dynamical.

The algorithm comes from Jean Meeus' "Astronomical Algorithms", 2nd Edition, Chapter 10, pages 78ff.

$coord = $coord->ecef($x, $y, $z, $xdot, $ydot, $zdot)

This method sets the coordinates represented by the object in terms of "Earth-Centered, Earth-fixed (ECEF) coordinates" in kilometers, with the x axis being latitude 0 longitude 0, the y axis being latitude 0 longitude 90 degrees east, and the z axis being latitude 90 degrees north. The velocities in kilometers per second are optional, and will default to zero. ECEF velocities are considered to be relative to the surface of the Earth; when converting to ECI, the rotational velocity of the Earth will be added in.

Note that prior to version 0.022_01, the documentation said that the default velocity was the rotational velocity of the Earth. This was not correct in ECEF coordinates. The functionality of the code itself in this respect did not change.

The object itself is returned.

This method can also be called as a class method, in which case it instantiates the desired object.

($x, $y, $z, $xdot, $ydot, $zdot) = $coord->ecef();

This method returns the object's "Earth-Centered, Earth-fixed (ECEF) coordinates".

If the original coordinate setting was in an inertial system (e.g. eci, equatorial, or ecliptic) and the absolute difference between the current value of 'equinox_dynamical' and the current dynamical() setting is greater than the value of $Astro::Coord::ECI::EQUINOX_TOLERANCE, the coordinates will be precessed to the current dynamical time before conversion. Yes, this should be done any time the equinox is not the current time, but for satellite prediction precession by a year or so does not seem to make much difference in practice. The default value of $Astro::Coord:ECI::EQUINOX_TOLERANCE is 365 days. Note that if this behavior or the default value of $EQUINOX_TOLERANCE begins to look like a bug, it will be changed, and noted in the documentation.

Caveat: Velocities are also returned, but should not at this point be taken seriously unless they were originally set by the same method that is returning them, since I have not at this point got the velocity transforms worked out.

$coord = $coord->eci ($x, $y, $z, $xdot, $ydot, $zdot, $time)

This method sets the coordinates represented by the object in terms of "Earth-Centered Inertial (ECI) coordinates" in kilometers, time being universal time, the x axis being 0 hours "Right Ascension" 0 degrees "Declination", y being 6 hours "Right Ascension" 0 degrees "Declination", and z being 90 degrees north "Declination". The velocities in kilometers per second are optional. If omitted, the object will be considered not to have a velocity. This is a change introduced with version 0.022_01. Before that, the velocity defaulted to 0.

The time argument is optional if the time represented by the object has already been set (e.g. by the universal() or dynamical() methods).

The object itself is returned.

This method can also be called as a class method, in which case it instantiates the desired object. In this case the time is not optional.

The algorithm for converting from ECI to geocentric coordinates and back is based on the description of ECI coordinates in T. S. Kelso's "Computers and Satellites" column in "Satellite Times", September/October 1995, titled "Orbital Coordinate Systems, Part I" and available at http://celestrak.com/columns/v02n01/.

($x, $y, $z, $xdot, $ydot, $zdot) = $coord->eci($time);

This method returns the "Earth-Centered Inertial (ECI) coordinates" of the object at the given time. The time argument is actually optional if the time represented by the object has already been set.

If you specify a time, the time represented by the object will be set to that time. The net effect of specifying a time is equivalent to

 ($x, $y, $z, $xdot, $ydot, $zdot) = $coord->universal($time)->eci()

If the original coordinate setting was in a non-inertial system (e.g. ECEF or geodetic), the equinox_dynamical attribute will be set to the object's dynamical time.

Velocities will be returned if they were originally provided. This is a change introduced in version 0.022_01. Prior to that version, velocities were always returned.

Caveat: Velocities will be returned if they are available, but this code is still untested.

$coord = $coord->ecliptic ($latitude, $longitude, $range, $time);

This method sets the "Ecliptic" coordinates represented by the object in terms of "Ecliptic latitude" and "Ecliptic longitude" in radians, and the range to the object in kilometers, time being universal time. The object itself is returned.

The time argument is optional if the time represented by the object has already been set (e.g. by the universal() or dynamical() methods).

The latitude should be a number between -PI/2 and PI/2 radians inclusive. The longitude should be a number between -2*PI and 2*PI radians inclusive. The increased range (one would expect -PI to PI) is because in some astronomical usages latitudes outside the range + or - 180 degrees are employed. A warning will be generated if either of these range checks fails.

This method can also be called as a class method, in which case it instantiates the desired object. In this case the time is not optional.

The algorithm for converting from ecliptic latitude and longitude to right ascension and declination comes from Jean Meeus' "Astronomical Algorithms", 2nd Edition, Chapter 13, page 93.

($latitude, $longitude, $range) = $coord->ecliptic ($time);

This method returns the ecliptic latitude and longitude of the object at the given time. The time is optional if the time represented by the object has already been set (e.g. by the universal() or dynamical() methods).

Caveat: velocities are not returned by this method.

$coord->equatorial ($rightasc, $declin, $range, $time);

This method sets the "Equatorial" coordinates represented by the object in terms of "Right Ascension" and "Declination" in radians, and the range to the object in kilometers, time being universal time. The object itself is returned.

The right ascension should be a number between 0 and 2*PI radians inclusive. The declination should be a number between -PI/2 and PI/2 radians inclusive. A warning will be generated if either of these range checks fails.

The time argument is optional if the time represented by the object has already been set (e.g. by the universal() or dynamical() methods).

This method can also be called as a class method, in which case it instantiates the desired object. In this case the time is not optional.

You may optionally pass velocity information in addition to position information. If you do this, the velocity in right ascension (in radians per second), declination (ditto), and range (in kilometers per second in recession) are passed after the position arguments, and before the $time argument if any.

($rightasc, $declin, $range) = $coord->equatorial ($time);

This method returns the "Equatorial" coordinates of the object at the given time. The time argument is optional if the time represented by the object has already been set (e.g. by the universal() or dynamical() methods).

If velocities are available from the object (i.e. if it is an instance of Astro::Coord::ECI::TLE) the return will contain velocity in right ascension, declination, and range, the first two being in radians per second and the third in kilometers per second in recession.

($rightasc, $declin, $range) = $coord->equatorial ($coord2);

This method returns the apparent equatorial coordinates of the object represented by $coord2, as seen from the location represented by $coord.

As a side effect, the time of the $coord object may be set from the $coord2 object.

If the refraction attribute of the $coord object is true, the coordinates will be corrected for atmospheric refraction using the correct_for_refraction() method.

If velocities are available from both objects (i.e. if both objects are Astro::Coord::ECI::TLE objects) the return will contain velocity in right ascension, declination, and range, the first two being in radians per second and the third in kilometers per second in recession.

my ($rasc, $decl, $range, $v_rasc, $v_decl, $v_r) = $coord->equatorial_unreduced($body);

This method computes the unreduced equatorial position of the second ECI object as seen from the first. If the second argument is undefined, computes the equatorial position of the first object as seen from the center of the Earth. Unlike the equatorial() method itself, this method is an accessor only. This method would probably not be exposed except for the anticipation of the usefulness of $range and $v_r in satellite conjunction computations, and the desirability of not taking the time to make the two atan2() calls that are unneeded in this application.

The 'unreduced' in the name of the method is intended to refer to the fact that the $rasc and $decl are not the right ascension and declination themselves, but the arguments to atan2() needed to compute them, and $v_rasc and $v_decl are in km/sec, rather than being divided by the range to get radians per second.

The returned data are:

$rasc is an array reference, which can be converted to the right ascension in radians by mod2pi(atan2($rasc->[0], $rasc->[1])).

$decl is an array reference, which can be converted to the declination in radians by atan2($decl->[0], $decl->[1]).

$range is the range in kilometers.

$v_rasc is the velocity in the right ascensional direction in kilometers per second. It can be converted to radians per second by dividing by $range.

$v_decl is the velocity in the declinational direction in kilometers per second. It can be converted to radians per second by dividing by $range.

$v_r is the velocity in recession in kilometers per second. Negative values indicate that the objects are approaching.

The velocities are only returned if they are available from the input objects.

$coord = $coord->equinox_dynamical ($value);

This method sets the value of the equinox_dynamical attribute, and returns the modified object. If called without an argument, it returns the current value of the equinox_dynamical attribute.

Yes, it is equivalent to $coord->set (equinox_dynamical => $value) and $coord->get ('equinox_dynamical'). But there seems to be a significant performance penalty in the $self->SUPER::set () needed to get this attribute set from a subclass. It is possible that more methods like this will be added, but I do not plan to eliminate the set() interface.

$coord = $coord->geocentric($psiprime, $lambda, $rho);

This method sets the "Geocentric" coordinates represented by the object in terms of "Geocentric latitude" psiprime and "Longitude" lambda in radians, and distance from the center of the Earth rho in kilometers.

The latitude should be a number between -PI/2 and PI/2 radians inclusive. The longitude should be a number between -2*PI and 2*PI radians inclusive. The increased range (one would expect -PI to PI) is because in some astronomical usages latitudes outside the range + or - 180 degrees are employed. A warning will be generated if either of these range checks fails.

This method can also be called as a class method, in which case it instantiates the desired object.

This method should not be used with map coordinates because map latitude is "Geodetic latitude", measured in terms of the tangent of the reference ellipsoid, whereas geocentric coordinates are, essentially, spherical coordinates.

The algorithm for conversion between geocentric and ECEF is the author's.

($psiprime, $lambda, $rho) = $coord->geocentric();

This method returns the "Geocentric latitude", "Longitude", and distance to the center of the Earth.

$coord = $coord->geodetic($psi, $lambda, $h, $ellipsoid);

This method sets the "Geodetic" coordinates represented by the object in terms of its "Geodetic latitude" psi and "Longitude" lambda in radians, and its height h above mean sea level in kilometers.

The latitude should be a number between -PI/2 and PI/2 radians inclusive. The longitude should be a number between -2*PI and 2*PI radians inclusive. The increased range (one would expect -PI to PI) is because in some astronomical usages latitudes outside the range + or - 180 degrees are employed. A warning will be generated if either of these range checks fails.

The ellipsoid argument is the name of a "Reference Ellipsoid" known to the class, and is optional. If passed, it will set the ellipsoid to be used for calculations with this object. If not passed, the default ellipsoid is used.

This method can also be called as a class method, in which case it instantiates the desired object.

The conversion from geodetic to geocentric comes from Jean Meeus' "Astronomical Algorithms", 2nd Edition, Chapter 11, page 82.

This is the method that should be used with map coordinates.

($psi, $lambda, $h) = $coord->geodetic($ellipsoid);

This method returns the geodetic latitude, longitude, and height above mean sea level.

The ellipsoid argument is the name of a "Reference ellipsoid" known to the class, and is optional. If not specified, the most-recently-set ellipsoid will be used.

The conversion from geocentric to geodetic comes from Kazimierz Borkowski's "Accurate Algorithms to Transform Geocentric to Geodetic Coordinates", at http://www.astro.uni.torun.pl/~kb/Papers/geod/Geod-BG.htm. This is best viewed with Internet Explorer because of its use of Microsoft's Symbol font.

$value = $coord->get ($attrib);

This method returns the named attributes of the object. If called in list context, you can give more than one attribute name, and it will return all their values.

If called as a class method, it returns the current default values.

See "Attributes" for a list of the attributes you can get.

$coord = $coord->local_mean_time ($time);

This method sets the local mean time of the object. This is not local standard time, but the universal time plus the longitude of the object expressed in seconds. Another definition is mean solar time plus 12 hours (since the solar day begins at noon). You will get an exception of some sort if the position of the object has not been set, or if the object represents inertial coordinates, or on any subclass whose position depends on the time.

$time = $coord->local_mean_time ()

This method returns the local mean time of the object. It will raise an exception if the time has not been set.

Note that this returns the actual local time, not the GMT equivalent. This means that in formatting for output, you call

 strftime $format, gmtime $coord->local_mean_time ();
$time = $coord->local_time ();

This method returns the local time (defined as solar time plus 12 hours) of the given object. There is no way to set the local time.

This is really just a convenience routine - the calculation is simply the local mean time plus the equation of time at the given time.

Note that this returns the actual local time, not the GMT equivalent. This means that in formatting for output, you call

 strftime $format, gmtime $coord->local_time ();
$value = $coord->mean_angular_velocity();

This method returns the mean angular velocity of the body in radians per second. If the $coord object has a period() method, this method just returns two pi divided by the period. Otherwise it returns the contents of the angularvelocity attribute.

$time = $coord->next_azimuth( $body, $azimuth );

This method returns the next time the given $body passes the given $azimuth as seen from the given $coord, calculated to the nearest second. The start time is the current time setting of the $body object.

($time, $rise) = $coord->next_elevation ($body, $elev, $upper)

This method calculates the next time the given body passes above or below the given elevation (in radians). The $elev argument may be omitted (or passed as undef), and will default to 0. If the $upper argument is true, the calculation will be based on the upper limb of the body (as determined from its angulardiameter attribute); if false, the calculation will be based on the center of the body. The $upper argument defaults to true if the $elev argument is zero or positive, and false if the $elev argument is negative.

The algorithm is successive approximation, and assumes that the body will be at its highest at meridian passage. It also assumes that if the body hasn't passed the given elevation in 183 days it never will. In this case it returns undef in scalar context, or an empty list in list context.

($time, $above) = $coord->next_meridian ($body, $want)

This method calculates the next meridian passage of the given body over (or under) the location specified by the $coord object. The $body object must be a subclass of Astro::Coord::ECI.

The optional $want argument should be specified as true (i.e. 1) if you want the next passage above the observer, or as false (i.e. 0) if you want the next passage below the observer. If this argument is omitted or undefined, you get whichever passage is next.

The start time of the search is the current time setting of the $coord object.

The returns are the time of the meridian passage, and an indicator which is true if the passage is above the observer (i.e. local noon if the $body represents the sun), or false if below (i.e. local midnight if the $body represents the sun). If called in scalar context, you get the time only.

The current time of both $coord and $body object are left at the returned time.

The algorithm is by successive approximation. It will croak if the period of the $body is close to synchronous, and will probably not work well for bodies in highly eccentric orbits. The calculation is to the nearest second, and the time returned is the first even second after the body crosses the meridian.

$coord = $coord->precess ($time);

This method is a convenience wrapper for precess_dynamical(). The functionality is the same except that the time is specified in universal time.

$coord = $coord->precess_dynamical ($time);

This method precesses the coordinates of the object to the given equinox, specified in dynamical time. The starting equinox is the value of the 'equinox_dynamical' attribute, or the current time setting if the 'equinox_dynamical' attribute is any false value (i.e. undef, 0, or ''). A warning will be issued if the value of 'equinox_dynamical' is undef (which is the default setting) and the object represents inertial coordinates. As of version 0.013_02, the time setting of the object is unaffected by this operation.

As a side effect, the value of the 'equinox_dynamical' attribute will be set to the dynamical time corresponding to the argument.

The object itself is returned.

The algorithm comes from Jean Meeus, "Astronomical Algorithms", 2nd Edition, Chapter 21, pages 134ff (a.k.a. "the rigorous method").

Astro::Coord::ECI->reference_ellipsoid($semi, $flat, $name);

This class method can be used to define or redefine reference ellipsoids.

Nothing bad will happen if you call this as an object method, but it still just creates a reference ellipsoid definition -- the object is unaffected.

It is not an error to redefine an existing ellipsoid.

($semi, $flat, $name) = Astro::Coord::ECI->reference_ellipsoid($name)

This class method returns the definition of the named reference ellipsoid. It croaks if there is no such ellipsoid.

You can also call this as an object method, but the functionality is the same.

The following reference ellipsoids are known to the class initially:

 CLARKE-1866 - 1866.
   semimajor => 6378.2064 km, flattening => 1/294.98.
   Source: http://www.colorado.edu/geography/gcraft/notes/datum/elist.html

 GRS67 - Geodetic Reference System 1967.
   semimajor => 6378.160 km, flattening => 1/298.247.
   Source: http://www.colorado.edu/geography/gcraft/notes/datum/elist.html

 GRS80 - Geodetic Reference System 1980.
   semimajor => 6378.137 km, flattening => 1/298.25722210088
     (flattening per U.S. Department of Commerce 1989).
   Source: http://biology.usgs.gov/fgdc.metadata/version2/spref/horiz/geodet/faq.htm

 IAU68 - International Astronomical Union, 1968.
   semimajor => 6378.160 km, flattening => 1/298.25.
   Source: http://maic.jmu.edu/sic/standards/ellipsoid.htm

 IAU76 - International Astronomical Union, 1976.
   semimajor => 6378.14 km, flattening => 1 / 298.257.
   Source: Jean Meeus, "Astronomical Algorithms", 2nd Edition

 NAD83 - North American Datum, 1983.
    semimajor => 6378.137 km, flattening => 1/298.257.
    Source:  http://biology.usgs.gov/fgdc.metadata/version2/spref/horiz/geodet/faq.htm
    (NAD83 uses the GRS80 ellipsoid)

 sphere - Just in case you were wondering how much difference it
   makes (a max of 11 minutes 32.73 seconds of arc, per Jean
   Meeus).
   semimajor => 6378.137 km (from GRS80), flattening => 0.

 WGS72 - World Geodetic System 1972.
   semimajor => 6378.135 km, flattening=> 1/298.26.
   Source: http://biology.usgs.gov/fgdc.metadata/version2/spref/horiz/geodet/faq.htm

 WGS84 - World Geodetic System 1984.
   semimajor => 6378.137 km, flattening => 1/1/298.257223563.
   Source: http://www.colorado.edu/geography/gcraft/notes/datum/elist.html

Reference ellipsoid names are case-sensitive.

The default model is WGS84.

$coord->represents ($class);

This method returns true if the $coord object represents the given class. It is pretty much like isa (), but if called on a container class (i.e. Astro::Coord::ECI::TLE::Set), it returns true based on the class of the members of the set, and dies if the set has no members.

The $class argument is optional. If not specified (or undef), it is pretty much like ref $coord || $coord (i.e. it returns the class name), but with the delegation behavior described in the previous paragraph if the $coord object is a container.

There. This took many more words to explain than it did to implement.

$coord->set (name => value ...);

This method sets various attributes of the object. If called as a class method, it changes the defaults.

For reasons that seemed good at the time, this method returns the object it was called on (i.e. $coord in the above example).

See "Attributes" for a list of the attributes you can set.

$coord->universal ($time)

This method sets the time represented by the object, in universal time (a.k.a. CUT, a.k.a. Zulu, a.k.a. Greenwich).

This method can also be called as a class method, in which case it instantiates the desired object.

$time = $coord->universal ();

This method returns the universal time previously set.

Attributes

This class has the following attributes:

angularvelocity (radians per second)

This attribute represents the angular velocity of the Earth's surface in radians per second. The initial value is 7.292114992e-5, which according to Jean Meeus is the value for 1996.5. He cites the International Earth Rotation Service's Annual Report for 1996 (Published at the Observatoire de Paris, 1997).

Subclasses may place appropriate values here, or provide a period() method.

debug (numeric)

This attribute turns on debugging output. The only supported value of this attribute is 0. That is to say, the author makes no guarantees of what will happen if you set it to some other value, nor does he guarantee that this behavior will not change from release to release.

The default is 0.

diameter (numeric, kilometers)

This attribute exists to support classes/instances which represent astronomical bodies. It represents the diameter of the body, and is used by the azel() method when computing the upper limb of the body. It has nothing to do with the semimajor attribute, which always refers to the Earth, and is used to calculate the latitude and longitude of the body.

The default is 0.

ellipsoid (string)

This attribute represents the name of the reference ellipsoid to use. It must be set to one of the known ellipsoids. If you set this, flattening and semimajor will be set also. See the documentation to the known_ellipsoid() method for the initially-valid names, and how to add more.

The default is 'WGS84'.

equinox_dynamical (numeric, dynamical time)

This attribute represents the time of the "Equinox" to which the coordinate data are referred. Models implemented by subclasses should set this to the "Equinox" to which the model is referred. When setting positions directly the user should also set the desired equinox_dynamical if conversion between inertial and Earth-fixed coordinates is of interest. If this is not set, these conversions will use the current time setting of the object as the "Equinox".

In addition, if you have a position specified in earth-fixed coordinates and convert it to inertial coordinates, this attribute will be set to the dynamical time of the object.

Unlike most times in this package, this attribute is specified in dynamical time. If your equinox is universal time $uni, set this attribute to $uni + dynamical_delta ($uni). The dynamical_delta subroutine is found in Astro::Coord::ECI::Utils.

The default is undef.

flattening (numeric)

This attribute represents the flattening factor of the reference ellipsoid. If you set the ellipsoid attribute, this attribute will be set to the flattening factor for the named ellipsoid. If you set this attribute, the ellipsoid attribute will become undefined.

The default is appropriate to the default ellipsoid.

frequency (numeric, Hertz)

This attribute represents the frequency on which the body transmits, in Hertz. If specified, the azel() method will return the Doppler shift if velocities are available.

The default is undef.

horizon (numeric, radians)

This attribute represents the distance the effective horizon is above the geometric horizon. It was added for the Astro::Coord::ECI::TLE::Iridium class, on the same dubious logic that the twilight attribute was added.

The default is the equivalent of 20 degrees.

id (string)

This is an informational attribute, and its setting (or lack thereof) does not affect the functioning of the class. Certain subclasses will set this when they are instantiated. See the subclass documentation for details.

inertial (boolean, read-only)

This attribute returns true (in the Perl sense) if the object was most-recently set to inertial coordinates (i.e. eci, ecliptic, or equatorial) and false otherwise. If coordinates have not been set, it is undefined (and therefore false).

name (string)

This is an informational attribute, and its setting (or lack thereof) does not affect the functioning of the class. Certain subclasses will set this when they are instantiated. See the subclass documentation for details.

refraction (boolean)

Setting this attribute to a true value includes refraction in the calculation of the azel() method. If set to a false value, atmospheric refraction is ignored.

The default is true (well, 1 actually).

semimajor (numeric, kilometers)

This attribute represents the semimajor axis of the reference ellipsoid. If you set the ellipsoid attribute, this attribute will be set to the semimajor axis for the named ellipsoid. If you set this attribute, the ellipsoid attribute will become undefined.

For subclasses representing bodies other than the Earth, this attribute will be set appropriately.

The default is appropriate to the default ellipsoid.

twilight (numeric, radians)

This attribute represents the elevation of the center of the Sun's disk at the beginning and end of twilight. It should probably be an attribute of the Sun subclass, since it is only used by the almanac () method of that subclass, but it's here so you can blindly set it when computing almanac data.

Some of the usual values are:

 civil twilight: -6 degrees
 nautical twilight: -12 degrees
 astronomical twilight: -18 degrees

The default is -6 degrees (or, actually, the equivalent in radians).

For example, to set nautical twilight you could do

 $eci->set(twilight => deg2rad(-12));

where deg2rad() is imported from Astro::Coord::ECI::Utils.

TERMINOLOGY AND CONVENTIONS

Partly because this module straddles the divide between geography and astronomy, the establishment of terminology and conventions was a thorny and in the end somewhat arbitrary process. Because of this, documentation of salient terms and conventions seemed to be in order.

Altitude

This term refers to the distance of a location above mean sea level.

Altitude input to and output from this module is in kilometers.

Maps use "elevation" for this quantity, and measure it in meters. But we're using "Elevation" for something different, and I needed consistent units.

Azimuth

This term refers to distance around the horizon measured clockwise from North.

Azimuth output from this module is in radians.

Astronomical sources tend to measure from the South, but I chose the geodetic standard, which seems to be usual in satellite tracking software.

Declination

Declination is the angle a point makes with the plane of the equator projected onto the sky. North declination is positive, south declination is negative.

Declination input to and output from this module is in radians.

Dynamical time

A dynamical time is defined theoretically by the motion of astronomical bodies. In practice, it is seen to be related to Atomic Time (a.k.a. TAI) by a constant.

There are actually two dynamical times of interest: TT (Terrestrial Time, a.k.a. TDT for Terrestrial Dynamical Time), which is defined in terms of the geocentric ephemerides of solar system bodies, and TDB (Barycentric Dynamical Time), which is defined in terms of the barycentre (a.k.a "center of mass") of the solar system. The two differ by the relativistic effects of the motions of the bodies in the Solar system, and are generally less than 2 milliseconds different. So unless you are doing high-precision work they can be considered identical, as Jean Meeus does in "Astronomical Algorithms".

For practical purposes, TT = TAI + 32.184 seconds. If I ever get the gumption to do a re-implementation (or alternate implementation) of time in terms of the DateTime object, this will be the definition of dynamical time. Until then, though, formula 10.2 on page 78 of Jean Meeus' "Astronomical Algorithms" second edition, Chapter 10 (Dynamical Time and Universal Time) is used.

Compare and contrast this to "Universal time". This explanation leans heavily on http://star-www.rl.ac.uk/star/docs/sun67.htx/node226.html, which contains a more fulsome but eminently readable explanation.

Earth-Centered, Earth-fixed (ECEF) coordinates

This is a Cartesian coodinate system whose origin is the center of the reference ellipsoid. The X axis passes through 0 degrees "Latitude" and 0 degrees "Longitude". The Y axis passes through 90 degrees east "Latitude" and 0 degrees "Longitude", and the Z axis passes through 90 degrees north "Latitude" (a.k.a the North Pole).

All three axes are input to and output from this module in kilometers.

Also known as "XYZ coordinates", e.g. at http://www.ngs.noaa.gov/cgi-bin/xyz_getxyz.prl

Earth-Centered Inertial (ECI) coordinates

This is the Cartesian coordinate system in which NORAD's models predict the position of orbiting bodies. The X axis passes through 0 hours "Right Ascension" and 0 degrees "Declination". The Y axis passes through 6 hours "Right Ascension" and 0 degrees "Declination". The Z axis passes through +90 degrees "Declination" (a.k.a. the North Pole). By implication, these coordinates are referred to a given "Equinox".

All three axes are input to and output from this module in kilometers.

Ecliptic

The Ecliptic is the plane of the Earth's orbit, projected onto the sky. Ecliptic coordinates are a spherical coordinate system referred to the ecliptic and expressed in terms of "Ecliptic latitude" and "Ecliptic longitude". By implication, Ecliptic coordinates are also referred to a specific "Equinox".

Ecliptic latitude

Ecliptic longitude is the angular distance of a point above the plane of the Earth's orbit.

Ecliptic latitude is input to and output from this module in radians.

Ecliptic longitude

Ecliptic longitude is the angular distance of a point east of the point where the plane of the Earth's orbit intersects the plane of the equator. This point is also known as the vernal "Equinox" and the first point of Ares.

Ecliptic longitude is input to and output from this module in radians.

Elevation

This term refers to an angular distance above the horizon.

Elevation output from this module is in radians.

This is the prevailing meaning of the term in satellite tracking. Astronomers use "altitude" for this quantity, and call the corresponding coordinate system "altazimuth." But we're using "Altitude" for something different.

Equatorial

Equatorial coordinates are a spherical coordinate system referred to the plane of the equator projected onto the sky. Equatorial coordinates are specified in "Right Ascension" and "Declination", and implicitly referred to a given "Equinox"

Equinox

The "Ecliptic", "Equatorial", and "Earth-Centered Inertial (ECI) coordinates" are defined in terms of the location of the intersection of the celestial equator with the "Ecliptic". The actual location of this point changes in time due to precession of the Earth's axis, so each of these coordinate systems is implicitly qualified by ("referred to" appears to be the usual terminology) the relevant time. By a process of association of ideas, this time is referred to as the equinox of the data.

Geocentric

When referring to a coordinate system, this term means that the coordinate system assumes the Earth is spherical.

Geocentric latitude

Geocentric latitude is the angle that the ray from the center of the Earth to the location makes with the plane of the equator. North latitude is positive, south latitude is negative.

Geocentric latitude is input to and output from this module in radians.

Geodetic

When referring to a coordinate system, this term means that the coordinate system assumes the Earth is an ellipsoid of revolution (or an oblate spheroid if you prefer). A number of standard "Reference Ellipsoids" have been adopted over the years.

Geodetic latitude

Geodetic latitude is the latitude found on maps. North latitude is positive, south latitude is negative.

Geodetic latitude is input to and output from this module in radians.

Technically speaking, Geodetic latitude is the complement of the angle the plane of the horizon makes with the plane of the equator. In this software, the plane of the horizon is determined from a "Reference Ellipsoid".

Latitude

See either "Ecliptic latitude", "Geocentric latitude" or "Geodetic latitude". When used without qualification, "Geodetic latitude" is meant.

Longitude

When unqualified, this term refers to the angular distance East or West of the standard meridian. East longitude is positive, West longitude is negative.

Longitude is input to or output from this module in radians.

For "Ecliptic longitude", see that entry.

Jean Meeus reports in "Astronomical Algorithms" that the International Astronomical Union has waffled on the sign convention. I have taken the geographic convention.

Obliquity (of the Ecliptic)

This term refers to the angle the plane of the equator makes with the plane of the Earth's orbit.

Obliquity is output from this module in radians.

Reference Ellipsoid

This term refers to a specific ellipsoid adopted as a model of the shape of the Earth. It is defined in terms of the equatorial radius in kilometers, and a dimensionless flattening factor which is used to derive the polar radius, and defined such that the flattening factor of a sphere is zero.

See the documentation on the reference_ellipsoid() method for a list of reference ellipsoids known to this class.

Right Ascension

This term refers to the angular distance of a point east of the intersection of the plane of the Earth's orbit with the plane of the equator.

Right Ascension is input to and output from this module in radians.

In astronomical literature it is usual to report right ascension in hours, minutes, and seconds, with 60 seconds in a minute, 60 minutes in an hour, and 24 hours in a circle.

Universal time

This term can refer to a number of scales, but the two of interest are UTC (Coordinated Universal Time) and UT1 (Universal Time 1, I presume). The latter is in effect mean solar time at Greenwich, though its technical definition differs in detail from GMT (Greenwich Mean Time). The former is a clock-based time, whose second is the SI second (defined in terms of atomic clocks), but which is kept within 0.9 seconds of UT1 by the introduction of leap seconds. These are introduced (typically at midyear or year end) by prior agreement among the various timekeeping bodies based on observation; there is no formula for computing when a leap second will be needed, because of irregularities in the Earth's rotation.

Jean Meeus' "Astronomical Algorithms", second edition, deals with the relationship between Universal time and "Dynamical time" in Chapter 10 (pages 77ff). His definition of "Universal time" seems to refer to UT1, though he does not use the term.

This software considers Universal time to be equivalent to Perl time. Since we are interested in durations (time since a given epoch, to be specific), this is technically wrong in most cases, since leap seconds are not taken into account. But in the case of the bodies modeled by the Astro::Coord::ECI::TLE object, the epoch is very recent (within a week or so), so the error introduced is small. It is larger for astronomical calculations, where the epoch is typically J2000.0, but the angular motions involved are smaller, so it all evens out. I hope.

Compare and contrast "Dynamical time". This explanation leans heavily on http://star-www.rl.ac.uk/star/docs/sun67.htx/node224.html.

XYZ coordinates

See "Earth-Centered, Earth-fixed (ECEF) coordinates".

ACKNOWLEDGMENTS

The author wishes to acknowledge the following individuals and organizations.

Kazimierz Borkowski, whose "Accurate Algorithms to Transform Geocentric to Geodetic Coordinates", at http://www.astro.uni.torun.pl/~kb/Papers/geod/Geod-BG.htm, was used for transforming geocentric to geodetic coordinates.

Dominik Brodowski (http://www.brodo.de/), whose SGP C-lib (available at http://www.brodo.de/space/sgp/) provided a reference implementation that I could easily run, and pick apart to help get Astro::Coord::ECI::TLE working. Dominik based his work on Dr. Kelso's Pascal implementation.

Felix R. Hoots and Ronald L. Roehric, the authors of "SPACETRACK REPORT NO. 3 - Models for Propagation of NORAD Element Sets," which provided the basis for the Astro::Coord::ECI::TLE module.

T. S. Kelso, who compiled this report and made it available at http://celestrak.com/, whose "Computers and Satellites" columns in "Satellite Times" magazine were invaluable to an understanding and implementation of satellite tracking software, whose support, encouragement, patience, and willingness to provide answers on arcane topics were a Godsend, who kindly granted permission to use his azimuth/elevation algorithm, and whose Pascal implementation of the NORAD tracking algorithms indirectly provided a reference implementation for me to use during development.

John A. Magliacane, whose open-source Predict program, available at http://www.qsl.net/kd2bd/predict.html, gave me a much-needed leg up on the velocity calculations in the azel() method.

Jean Meeus, whose book "Astronomical Algorithms" (second edition) formed the basis for this module and the Astro::Coord::ECI::Sun, Astro::Coord::ECI::Moon, and Astro::Coord::ECI::Star modules, and without whom it is impossible to get far in computational astronomy. Any algorithm not explicitly credited above is probably due to him.

Dr. Meeus' publisher, Willmann-Bell Inc (http://www.willbell.com/), which kindly and patiently answered my intellectual-property questions.

BUGS

Functionality involving velocities is untested, and is quite likely to be wrong.

Bugs can be reported to the author by mail, or through http://rt.cpan.org/.

SEE ALSO

The Astro package by Chris Phillips. This contains three function-based modules: Astro::Coord, which provides various astronomical coordinate conversions, plus the calculation of various ephemeris variables; Astro::Time contains time and unit conversions, and Astro::Misc contains various calculations unrelated to position and time.

The Astro-Coords package by Tim Jenness. This contains various modules to do astronomical calculations, and includes coordinate conversion and calculation of planetary orbits based on orbital elements. Requires SLALIB from http://www.starlink.rl.ac.uk/Software/software_store.htm.

The Astro::Nova module by Steffen Mueller, which wraps (and bundles) the libnova celestial mechanics, astrometry and astrodynamics library found at http://libnova.sourceforge.net/.

AUTHOR

Thomas R. Wyant, III (wyant at cpan dot org)

COPYRIGHT AND LICENSE

Copyright (C) 2005-2010, Thomas R. Wyant, III

This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the full text of the licenses in the directory LICENSES.

This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose.