| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Astro::Coords; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | =head1 NAME | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | Astro::Coords - Class for handling astronomical coordinates | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | use Astro::Coords; | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | $c = new Astro::Coords( name => "My target", | 
| 12 |  |  |  |  |  |  | ra   => '05:22:56', | 
| 13 |  |  |  |  |  |  | dec  => '-26:20:40.4', | 
| 14 |  |  |  |  |  |  | type => 'B1950' | 
| 15 |  |  |  |  |  |  | units=> 'sexagesimal'); | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | $c = new Astro::Coords( long => '05:22:56', | 
| 18 |  |  |  |  |  |  | lat  => '-26:20:40.4', | 
| 19 |  |  |  |  |  |  | type => 'galactic'); | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | $c = new Astro::Coords( planet => 'mars' ); | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | $c = new Astro::Coords( elements => \%elements ); | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | $c = new Astro::Coords( az => 345, el => 45 ); | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | # Associate with an observer location | 
| 28 |  |  |  |  |  |  | $c->telescope( new Astro::Telescope( 'JCMT' )); | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | # ...and a reference epoch for all calculations | 
| 31 |  |  |  |  |  |  | $date = Time::Piece->strptime($string, $format); | 
| 32 |  |  |  |  |  |  | $c->datetime( $date ); | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | # or use DateTime | 
| 35 |  |  |  |  |  |  | $date = DateTime->from_epoch( epoch => $epoch, time_zone => 'UTC' ); | 
| 36 |  |  |  |  |  |  | $c->datetime( $date ); | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | # Return coordinates J2000, for the epoch stored in the datetime | 
| 39 |  |  |  |  |  |  | # object. This will work for all variants. | 
| 40 |  |  |  |  |  |  | ($ra, $dec) = $c->radec(); | 
| 41 |  |  |  |  |  |  | $radians = $ra->radians; | 
| 42 |  |  |  |  |  |  |  | 
| 43 |  |  |  |  |  |  | # or individually | 
| 44 |  |  |  |  |  |  | $ra = $c->ra();  # returns Astro::Coords::Angle::Hour object | 
| 45 |  |  |  |  |  |  | $dec = $c->dec( format => 'deg' ); | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | # Return coordinates J2000, epoch 2000.0 | 
| 48 |  |  |  |  |  |  | $ra = $c->ra2000(); | 
| 49 |  |  |  |  |  |  | $dec = $c->dec2000(); | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | # Return coordinats apparent, reference epoch, from location | 
| 52 |  |  |  |  |  |  | # In sexagesimal format. | 
| 53 |  |  |  |  |  |  | ($ra_app, $dec_app) = $c->apparent; | 
| 54 |  |  |  |  |  |  | $ra_app = $c->ra_app( format => 's'); | 
| 55 |  |  |  |  |  |  | $dec_app = $c->dec_app( format => 's' ); | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | # Azimuth and elevation for reference epoch from observer location | 
| 58 |  |  |  |  |  |  | ($az, $el) = $c->azel; | 
| 59 |  |  |  |  |  |  | my $az = $c->az; | 
| 60 |  |  |  |  |  |  | my $el = $c->el; | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | # obtain summary string of object | 
| 63 |  |  |  |  |  |  | $summary = "$c"; | 
| 64 |  |  |  |  |  |  |  | 
| 65 |  |  |  |  |  |  | # Obtain full summary as an array | 
| 66 |  |  |  |  |  |  | @summary = $c->array; | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | # See if the target is observable for the current time | 
| 69 |  |  |  |  |  |  | # and telescope | 
| 70 |  |  |  |  |  |  | $obs = 1 if $c->isObservable; | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | # Calculate distance to another coordinate (in radians) | 
| 73 |  |  |  |  |  |  | $distance = $c->distance( $c2 ); | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | # Calculate the rise and set time of the source | 
| 76 |  |  |  |  |  |  | $tr = $c->rise_time; | 
| 77 |  |  |  |  |  |  | $ts = $c->set_time; | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | # transit elevation | 
| 80 |  |  |  |  |  |  | $trans = $c->transit_el; | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | # transit time | 
| 83 |  |  |  |  |  |  | $mtime = $c->meridian_time(); | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | Class for manipulating and transforming astronomical coordinates. | 
| 89 |  |  |  |  |  |  | Can handle the following coordinate types: | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | + Equatorial RA/Dec, galactic (including proper motions and parallax) | 
| 92 |  |  |  |  |  |  | + Planets | 
| 93 |  |  |  |  |  |  | + Comets/Asteroids | 
| 94 |  |  |  |  |  |  | + Fixed locations in azimuth and elevations | 
| 95 |  |  |  |  |  |  | + interpolated apparent coordinates | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  | For time dependent calculations a telescope location and reference | 
| 98 |  |  |  |  |  |  | time must be provided. See C<Astro::Telescope> and C<DateTime> for | 
| 99 |  |  |  |  |  |  | details on specifying location and reference epoch. | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | =cut | 
| 102 |  |  |  |  |  |  |  | 
| 103 | 19 |  |  | 19 |  | 893134 | use 5.006; | 
|  | 19 |  |  |  |  | 111 |  | 
| 104 | 19 |  |  | 19 |  | 142 | use strict; | 
|  | 19 |  |  |  |  | 77 |  | 
|  | 19 |  |  |  |  | 463 |  | 
| 105 | 19 |  |  | 19 |  | 102 | use warnings; | 
|  | 19 |  |  |  |  | 57 |  | 
|  | 19 |  |  |  |  | 687 |  | 
| 106 | 19 |  |  | 19 |  | 116 | use warnings::register; | 
|  | 19 |  |  |  |  | 41 |  | 
|  | 19 |  |  |  |  | 3208 |  | 
| 107 | 19 |  |  | 19 |  | 128 | use Carp; | 
|  | 19 |  |  |  |  | 36 |  | 
|  | 19 |  |  |  |  | 1560 |  | 
| 108 | 19 |  |  | 19 |  | 125 | use vars qw/ $DEBUG /; | 
|  | 19 |  |  |  |  | 38 |  | 
|  | 19 |  |  |  |  | 1633 |  | 
| 109 |  |  |  |  |  |  | $DEBUG = 0; | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | our $VERSION = '0.21'; | 
| 112 |  |  |  |  |  |  |  | 
| 113 | 19 |  |  | 19 |  | 11095 | use Math::Trig qw/ acos /; | 
|  | 19 |  |  |  |  | 275992 |  | 
|  | 19 |  |  |  |  | 1504 |  | 
| 114 | 19 |  |  | 19 |  | 7693 | use Astro::PAL (); | 
|  | 19 |  |  |  |  | 43144 |  | 
|  | 19 |  |  |  |  | 513 |  | 
| 115 | 19 |  |  | 19 |  | 8376 | use Astro::Coords::Angle; | 
|  | 19 |  |  |  |  | 46 |  | 
|  | 19 |  |  |  |  | 603 |  | 
| 116 | 19 |  |  | 19 |  | 9248 | use Astro::Coords::Angle::Hour; | 
|  | 19 |  |  |  |  | 60 |  | 
|  | 19 |  |  |  |  | 599 |  | 
| 117 | 19 |  |  | 19 |  | 9713 | use Astro::Coords::Equatorial; | 
|  | 19 |  |  |  |  | 60 |  | 
|  | 19 |  |  |  |  | 558 |  | 
| 118 | 19 |  |  | 19 |  | 8755 | use Astro::Coords::Elements; | 
|  | 19 |  |  |  |  | 58 |  | 
|  | 19 |  |  |  |  | 615 |  | 
| 119 | 19 |  |  | 19 |  | 8350 | use Astro::Coords::Planet; | 
|  | 19 |  |  |  |  | 51 |  | 
|  | 19 |  |  |  |  | 607 |  | 
| 120 | 19 |  |  | 19 |  | 8156 | use Astro::Coords::Interpolated; | 
|  | 19 |  |  |  |  | 52 |  | 
|  | 19 |  |  |  |  | 684 |  | 
| 121 | 19 |  |  | 19 |  | 7863 | use Astro::Coords::Fixed; | 
|  | 19 |  |  |  |  | 46 |  | 
|  | 19 |  |  |  |  | 666 |  | 
| 122 | 19 |  |  | 19 |  | 8013 | use Astro::Coords::Calibration; | 
|  | 19 |  |  |  |  | 49 |  | 
|  | 19 |  |  |  |  | 693 |  | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 19 |  |  | 19 |  | 128 | use Scalar::Util qw/ blessed /; | 
|  | 19 |  |  |  |  | 48 |  | 
|  | 19 |  |  |  |  | 1375 |  | 
| 125 | 19 |  |  | 19 |  | 17011 | use DateTime; | 
|  | 19 |  |  |  |  | 9616872 |  | 
|  | 19 |  |  |  |  | 1253 |  | 
| 126 | 19 |  |  | 19 |  | 207 | use Time::Piece; | 
|  | 19 |  |  |  |  | 43 |  | 
|  | 19 |  |  |  |  | 208 |  | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | # Constants for Sun rise/set and twilight definitions | 
| 129 |  |  |  |  |  |  | # Elevation in radians | 
| 130 |  |  |  |  |  |  | # See http://aa.usno.navy.mil/faq/docs/RST_defs.html | 
| 131 | 19 |  |  | 19 |  | 2097 | use constant SUN_RISE_SET => ( - (50 * 60) * Astro::PAL::DAS2R); # 50 arcmin | 
|  | 19 |  |  |  |  | 57 |  | 
|  | 19 |  |  |  |  | 1755 |  | 
| 132 | 19 |  |  | 19 |  | 155 | use constant CIVIL_TWILIGHT => ( - (6 * 3600) * Astro::PAL::DAS2R); # 6 deg | 
|  | 19 |  |  |  |  | 49 |  | 
|  | 19 |  |  |  |  | 1188 |  | 
| 133 | 19 |  |  | 19 |  | 122 | use constant NAUT_TWILIGHT => ( - (12 * 3600) * Astro::PAL::DAS2R); # 12 deg | 
|  | 19 |  |  |  |  | 42 |  | 
|  | 19 |  |  |  |  | 1038 |  | 
| 134 | 19 |  |  | 19 |  | 129 | use constant AST_TWILIGHT => ( - (18 * 3600) * Astro::PAL::DAS2R); # 18 deg | 
|  | 19 |  |  |  |  | 47 |  | 
|  | 19 |  |  |  |  | 1081 |  | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | # This is a fudge. Not accurate | 
| 137 | 19 |  |  | 19 |  | 149 | use constant MOON_RISE_SET => ( 5 * 60 * Astro::PAL::DAS2R); | 
|  | 19 |  |  |  |  | 81 |  | 
|  | 19 |  |  |  |  | 994 |  | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | # Number of km in one Astronomical Unit | 
| 140 | 19 |  |  | 19 |  | 115 | use constant AU2KM => 149.59787066e6; | 
|  | 19 |  |  |  |  | 43 |  | 
|  | 19 |  |  |  |  | 984 |  | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | # Speed of light ( km/s ) | 
| 143 | 19 |  |  | 19 |  | 132 | use constant CLIGHT => 2.99792458e5; | 
|  | 19 |  |  |  |  | 56 |  | 
|  | 19 |  |  |  |  | 1131 |  | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | # UTC TimeZone | 
| 146 | 19 |  |  | 19 |  | 177 | use constant UTC => new DateTime::TimeZone( name => 'UTC' ); | 
|  | 19 |  |  |  |  | 54 |  | 
|  | 19 |  |  |  |  | 146 |  | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | =head1 METHODS | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | =head2 Constructor | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | =over 4 | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | =item B<new> | 
| 155 |  |  |  |  |  |  |  | 
| 156 |  |  |  |  |  |  | This can be treated as an object factory. The object returned | 
| 157 |  |  |  |  |  |  | by this constructor depends on the arguments supplied to it. | 
| 158 |  |  |  |  |  |  | Coordinates can be provided as orbital elements, a planet name | 
| 159 |  |  |  |  |  |  | or an equatorial (or related) fixed coordinate specification (e.g. | 
| 160 |  |  |  |  |  |  | right ascension and declination). | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | A complete (for some definition of complete) specification for | 
| 163 |  |  |  |  |  |  | the coordinates in question must be provided to the constructor. | 
| 164 |  |  |  |  |  |  | The coordinates given as arguments will be converted to an internal | 
| 165 |  |  |  |  |  |  | format. | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | A planet name can be specified with: | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | $c = new Astro::Coords( planet => "sun" ); | 
| 170 |  |  |  |  |  |  |  | 
| 171 |  |  |  |  |  |  | Orbital elements as: | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | $c = new Astro::Coords( elements => \%elements ); | 
| 174 |  |  |  |  |  |  | $c = new Astro::Coords( elements => \@array ); | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | where C<%elements> must contain the names of the elements | 
| 177 |  |  |  |  |  |  | as used in the PAL routine palPlante, and @array is the | 
| 178 |  |  |  |  |  |  | contents of the array returned by calling the array() method | 
| 179 |  |  |  |  |  |  | on another Elements object. | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | Fixed astronomical oordinate frames can be specified using: | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | $c = new Astro::Coords( ra => | 
| 184 |  |  |  |  |  |  | dec => | 
| 185 |  |  |  |  |  |  | long => | 
| 186 |  |  |  |  |  |  | lat => | 
| 187 |  |  |  |  |  |  | type => | 
| 188 |  |  |  |  |  |  | units => | 
| 189 |  |  |  |  |  |  | ); | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | C<ra> and C<dec> are used for HMSDeg systems (eg type=J2000). Long and | 
| 192 |  |  |  |  |  |  | Lat are used for degdeg systems (eg where type=galactic). C<type> can | 
| 193 |  |  |  |  |  |  | be "galactic", "j2000", "b1950", and "supergalactic". | 
| 194 |  |  |  |  |  |  | The C<units> can be specified as "sexagesimal" (when using colon or | 
| 195 |  |  |  |  |  |  | space-separated strings), "degrees" or "radians". The default is | 
| 196 |  |  |  |  |  |  | determined from context. | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | Fixed (as in fixed on Earth) coordinate frames can be specified | 
| 199 |  |  |  |  |  |  | using: | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | $c = new Astro::Coords( dec => | 
| 202 |  |  |  |  |  |  | ha => | 
| 203 |  |  |  |  |  |  | tel => | 
| 204 |  |  |  |  |  |  | az => | 
| 205 |  |  |  |  |  |  | el => | 
| 206 |  |  |  |  |  |  | units => | 
| 207 |  |  |  |  |  |  | ); | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | where C<az> and C<el> are the Azimuth and Elevation. Hour Angle | 
| 210 |  |  |  |  |  |  | and Declination require a telescope. Units are as defined above. | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | Finally, if no arguments are given the object is assumed | 
| 213 |  |  |  |  |  |  | to be of type C<Astro::Coords::Calibration>. | 
| 214 |  |  |  |  |  |  |  | 
| 215 |  |  |  |  |  |  | Returns C<undef> if an object could not be created. | 
| 216 |  |  |  |  |  |  |  | 
| 217 |  |  |  |  |  |  | =cut | 
| 218 |  |  |  |  |  |  |  | 
| 219 |  |  |  |  |  |  | sub new { | 
| 220 | 1562 |  |  | 1562 | 1 | 1394284 | my $class = shift; | 
| 221 |  |  |  |  |  |  |  | 
| 222 | 1562 |  |  |  |  | 6319 | my %args = @_; | 
| 223 |  |  |  |  |  |  |  | 
| 224 | 1562 |  |  |  |  | 2642 | my $obj; | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | # Always try for a planet object first if $args{planet} is used | 
| 227 |  |  |  |  |  |  | # (it might be that ra/dec are being specified and planet is a target | 
| 228 |  |  |  |  |  |  | # name - this allows all the keys to be specified at once and the | 
| 229 |  |  |  |  |  |  | # object can decide the most likely coordinate object to use | 
| 230 |  |  |  |  |  |  | # This has the distinct disadvantage that planet is always tried | 
| 231 |  |  |  |  |  |  | # even though it is rare. We want to be able to throw anything | 
| 232 |  |  |  |  |  |  | # at this without knowing what we are. | 
| 233 | 1562 | 100 | 66 |  |  | 6242 | if (exists $args{planet} and defined $args{planet}) { | 
| 234 | 511 |  |  |  |  | 2056 | $obj = new Astro::Coords::Planet( $args{planet} ); | 
| 235 |  |  |  |  |  |  | } | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | # planet did not work. Try something else. | 
| 238 | 1562 | 100 |  |  |  | 3881 | unless (defined $obj) { | 
| 239 |  |  |  |  |  |  |  | 
| 240 |  |  |  |  |  |  | # For elements we must not only check for the elements key | 
| 241 |  |  |  |  |  |  | # but also make sure that that key points to a hash containing | 
| 242 |  |  |  |  |  |  | # at least the EPOCH or EPOCHPERIH key | 
| 243 | 1051 | 100 | 33 |  |  | 7177 | if (exists $args{elements} and defined $args{elements} | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 100 | 66 |  |  |  |  | 
|  |  | 50 | 33 |  |  |  |  | 
| 244 |  |  |  |  |  |  | && (UNIVERSAL::isa($args{elements},"HASH") # A hash | 
| 245 |  |  |  |  |  |  | &&  ((exists $args{elements}{EPOCH} | 
| 246 |  |  |  |  |  |  | and defined $args{elements}{EPOCH}) | 
| 247 |  |  |  |  |  |  | ||  (exists $args{elements}{EPOCHPERIH} | 
| 248 |  |  |  |  |  |  | and defined $args{elements}{EPOCHPERIH}))) || | 
| 249 |  |  |  |  |  |  | ( ref($args{elements}) eq 'ARRAY' && # ->array() ref | 
| 250 |  |  |  |  |  |  | $args{elements}->[0] eq 'ELEMENTS') | 
| 251 |  |  |  |  |  |  | ) { | 
| 252 |  |  |  |  |  |  |  | 
| 253 | 5 |  |  |  |  | 47 | $obj = new Astro::Coords::Elements( %args ); | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  | } elsif (exists $args{mjd1}) { | 
| 256 |  |  |  |  |  |  |  | 
| 257 | 4 |  |  |  |  | 43 | $obj = new Astro::Coords::Interpolated( %args ); | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | } elsif (exists $args{type} and defined $args{type}) { | 
| 260 |  |  |  |  |  |  |  | 
| 261 | 1029 |  |  |  |  | 4522 | $obj = new Astro::Coords::Equatorial( %args ); | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | } elsif (exists $args{az} or exists $args{el} or exists $args{ha}) { | 
| 264 |  |  |  |  |  |  |  | 
| 265 | 11 |  |  |  |  | 79 | $obj = new Astro::Coords::Fixed( %args ); | 
| 266 |  |  |  |  |  |  |  | 
| 267 |  |  |  |  |  |  | } elsif ( scalar keys %args == 0 ) { | 
| 268 |  |  |  |  |  |  |  | 
| 269 | 2 |  |  |  |  | 32 | $obj = new Astro::Coords::Calibration(); | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | } else { | 
| 272 |  |  |  |  |  |  | # unable to work out what you are asking for | 
| 273 | 0 |  |  |  |  | 0 | return undef; | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | } | 
| 276 |  |  |  |  |  |  | } | 
| 277 |  |  |  |  |  |  |  | 
| 278 | 1562 |  |  |  |  | 5867 | return $obj; | 
| 279 |  |  |  |  |  |  | } | 
| 280 |  |  |  |  |  |  |  | 
| 281 |  |  |  |  |  |  |  | 
| 282 |  |  |  |  |  |  | =back | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | =head2 Accessor Methods | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | =over 4 | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | =item B<name> | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | Name of the target associated with the coordinates. | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | =cut | 
| 293 |  |  |  |  |  |  |  | 
| 294 |  |  |  |  |  |  | sub name { | 
| 295 | 5057 |  |  | 5057 | 1 | 7125 | my $self = shift; | 
| 296 | 5057 | 100 |  |  |  | 10227 | if (@_) { | 
| 297 | 4 |  |  |  |  | 10 | $self->{name} = shift; | 
| 298 |  |  |  |  |  |  | } | 
| 299 | 5057 |  |  |  |  | 13216 | return $self->{name}; | 
| 300 |  |  |  |  |  |  | } | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | =item B<telescope> | 
| 303 |  |  |  |  |  |  |  | 
| 304 |  |  |  |  |  |  | Telescope object (an instance of Astro::Telescope) to use | 
| 305 |  |  |  |  |  |  | for obtaining the position of the telescope to use for | 
| 306 |  |  |  |  |  |  | the determination of source elevation. | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | $c->telescope( new Astro::Telescope( 'JCMT' )); | 
| 309 |  |  |  |  |  |  | $tel = $c->telescope; | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | This method checks that the argument is of the correct type. | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | =cut | 
| 314 |  |  |  |  |  |  |  | 
| 315 |  |  |  |  |  |  | sub telescope { | 
| 316 | 43994 |  |  | 43994 | 1 | 671970 | my $self = shift; | 
| 317 | 43994 | 100 |  |  |  | 86062 | if (@_) { | 
| 318 | 1534 |  |  |  |  | 2693 | my $tel = shift; | 
| 319 | 1534 | 50 |  |  |  | 5810 | return undef unless UNIVERSAL::isa($tel, "Astro::Telescope"); | 
| 320 | 1534 |  |  |  |  | 3135 | $self->{Telescope} = $tel; | 
| 321 |  |  |  |  |  |  | # Telescope is part of the caching scheme | 
| 322 | 1534 |  |  |  |  | 3389 | $self->_calc_cache_key(); | 
| 323 |  |  |  |  |  |  | } | 
| 324 | 43994 |  |  |  |  | 78424 | return $self->{Telescope}; | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | =item B<datetime> | 
| 329 |  |  |  |  |  |  |  | 
| 330 |  |  |  |  |  |  | Date/Time object to use when determining the source elevation. | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | $c->datetime( new Time::Piece() ); | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | Argument must be an object that has the C<mjd> method. Both | 
| 335 |  |  |  |  |  |  | C<DateTime> and C<Time::Piece> objects are allowed.  A value of | 
| 336 |  |  |  |  |  |  | C<undef> is supported. This will clear the time and force the current | 
| 337 |  |  |  |  |  |  | time to be used on subsequent calls. | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | $c->datetime( undef ); | 
| 340 |  |  |  |  |  |  |  | 
| 341 |  |  |  |  |  |  | If no argument is specified and no Date/Time object had already been | 
| 342 |  |  |  |  |  |  | supplied, or C<usenow> is set to true, an object | 
| 343 |  |  |  |  |  |  | referring to the current time (GMT/UT) is returned. This object may be | 
| 344 |  |  |  |  |  |  | either a C<Time::Piece> object or a C<DateTime> object depending on | 
| 345 |  |  |  |  |  |  | current implementation (but in modern versions it will be a | 
| 346 |  |  |  |  |  |  | C<DateTime> object). If a Date/Time object had already been specified | 
| 347 |  |  |  |  |  |  | then the object returned will be of the same type as the supplied | 
| 348 |  |  |  |  |  |  | object (C<DateTime> or C<Time::Piece>). | 
| 349 |  |  |  |  |  |  | If a new argument is supplied C<usenow> is always set to false. | 
| 350 |  |  |  |  |  |  |  | 
| 351 |  |  |  |  |  |  | A copy of the input argument is created, guaranteeing a UTC representation. | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | Note that due to the possibility of an optimized caching scheme being | 
| 354 |  |  |  |  |  |  | used, you should not adjust this object after it has been cloned. The | 
| 355 |  |  |  |  |  |  | object is assumed to be immutable by the module internals. If you | 
| 356 |  |  |  |  |  |  | really do require that it be adjusted externally, use the | 
| 357 |  |  |  |  |  |  | C<datetime_is_unsafe> method to indicate this to the module. | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | =cut | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | sub datetime { | 
| 362 | 76732 |  |  | 76732 | 1 | 460644 | my $self = shift; | 
| 363 | 76732 | 100 |  |  |  | 141264 | if (@_) { | 
| 364 | 12330 |  |  |  |  | 17084 | my $time = shift; | 
| 365 |  |  |  |  |  |  |  | 
| 366 |  |  |  |  |  |  | # undef is okay | 
| 367 | 12330 | 0 | 66 |  |  | 60320 | croak "datetime: Argument does not have an mjd() method [class=" | 
|  |  | 50 |  |  |  |  |  | 
| 368 |  |  |  |  |  |  | . ( ref($time) ? ref($time) : $time) ."]" | 
| 369 |  |  |  |  |  |  | if (defined $time && !UNIVERSAL::can($time, "mjd")); | 
| 370 | 12330 |  |  |  |  | 26548 | $self->{DateTime} = _clone_time( $time ); | 
| 371 | 12330 |  |  |  |  | 170077 | $self->usenow(0); | 
| 372 |  |  |  |  |  |  | # Changing usenow will have forced a recalculation of the cache key | 
| 373 |  |  |  |  |  |  | # so no update is required here | 
| 374 |  |  |  |  |  |  | # $self->_calc_cache_key; | 
| 375 |  |  |  |  |  |  | } | 
| 376 | 76732 | 100 | 66 |  |  | 199082 | if (defined $self->{DateTime} && ! $self->usenow) { | 
| 377 | 76657 |  |  |  |  | 154185 | return $self->{DateTime}; | 
| 378 |  |  |  |  |  |  | } else { | 
| 379 | 75 |  |  |  |  | 267 | return DateTime->now( time_zone => UTC ); | 
| 380 |  |  |  |  |  |  | } | 
| 381 |  |  |  |  |  |  | } | 
| 382 |  |  |  |  |  |  |  | 
| 383 |  |  |  |  |  |  | =item B<has_datetime> | 
| 384 |  |  |  |  |  |  |  | 
| 385 |  |  |  |  |  |  | Returns true if a specific time is stored in the object, returns | 
| 386 |  |  |  |  |  |  | false if no time is stored. (The value of C<usenow> is | 
| 387 |  |  |  |  |  |  | ignored). | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | This is required because C<datetime> always returns a time. | 
| 390 |  |  |  |  |  |  |  | 
| 391 |  |  |  |  |  |  | =cut | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | sub has_datetime { | 
| 394 | 21184 |  |  | 21184 | 1 | 28571 | my $self = shift; | 
| 395 | 21184 |  |  |  |  | 68415 | return (defined $self->{DateTime}); | 
| 396 |  |  |  |  |  |  | } | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | =item B<usenow> | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | Flag to indicate whether the current time should be used for calculations | 
| 401 |  |  |  |  |  |  | regardless of whether an explicit time object is stored in C<datetime>. | 
| 402 |  |  |  |  |  |  | This is useful when trying to determine the current position of a target | 
| 403 |  |  |  |  |  |  | without affecting previous settings. | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | $c->usenow( 1 ); | 
| 406 |  |  |  |  |  |  | $usenow = $c->usenow; | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | Defaults to false. | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | =cut | 
| 411 |  |  |  |  |  |  |  | 
| 412 |  |  |  |  |  |  | sub usenow { | 
| 413 | 103535 |  |  | 103535 | 1 | 147024 | my $self = shift; | 
| 414 | 103535 | 100 |  |  |  | 179129 | if (@_) { | 
| 415 | 12331 |  |  |  |  | 20547 | $self->{UseNow} = shift; | 
| 416 |  |  |  |  |  |  | # Since this affects caching we need to force a key check if this value | 
| 417 |  |  |  |  |  |  | # is updated | 
| 418 | 12331 |  |  |  |  | 24843 | $self->_calc_cache_key(); | 
| 419 |  |  |  |  |  |  | } | 
| 420 | 103535 |  |  |  |  | 267692 | return $self->{UseNow}; | 
| 421 |  |  |  |  |  |  | } | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | =item B<datetime_is_unsafe> | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | If true, indicates that the DateTime object stored in this object is | 
| 426 |  |  |  |  |  |  | not guranteed to be immutable by the externally user. This effectively | 
| 427 |  |  |  |  |  |  | turns off the internal cache. | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | $c->datetime_is_unsafe(); | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | =cut | 
| 432 |  |  |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | sub datetime_is_unsafe { | 
| 434 | 17849 |  |  | 17849 | 1 | 25376 | my $self = shift; | 
| 435 | 17849 | 100 |  |  |  | 32795 | if (@_) { | 
| 436 | 2202 |  |  |  |  | 3860 | $self->{DATETIME_IS_UNSAFE} = shift; | 
| 437 | 2202 |  |  |  |  | 3966 | $self->_calc_cache_key; | 
| 438 |  |  |  |  |  |  | } | 
| 439 | 17849 |  |  |  |  | 42660 | return $self->{DATETIME_IS_UNSAFE}; | 
| 440 |  |  |  |  |  |  | } | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | =item B<comment> | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  | A textual comment associated with the coordinate (optional). | 
| 445 |  |  |  |  |  |  | Defaults to the empty string. | 
| 446 |  |  |  |  |  |  |  | 
| 447 |  |  |  |  |  |  | $comment = $c->comment; | 
| 448 |  |  |  |  |  |  | $c->comment("An inaccurate coordinate"); | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | Always returns an empty string if undefined. | 
| 451 |  |  |  |  |  |  |  | 
| 452 |  |  |  |  |  |  | =cut | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | sub comment { | 
| 455 | 7 |  |  | 7 | 1 | 11 | my $self = shift; | 
| 456 | 7 | 50 |  |  |  | 14 | if (@_) { | 
| 457 | 0 |  |  |  |  | 0 | $self->{Comment} = shift; | 
| 458 |  |  |  |  |  |  | } | 
| 459 | 7 |  |  |  |  | 11 | my $com = $self->{Comment}; | 
| 460 | 7 | 50 |  |  |  | 19 | $com = '' unless defined $com; | 
| 461 | 7 |  |  |  |  | 11 | return $com; | 
| 462 |  |  |  |  |  |  | } | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | =item B<native> | 
| 465 |  |  |  |  |  |  |  | 
| 466 |  |  |  |  |  |  | Returns the name of the method that should be called to return the | 
| 467 |  |  |  |  |  |  | coordinates in a form as close as possible to those that were supplied | 
| 468 |  |  |  |  |  |  | to the constructor. This method is useful if, say, the object is created | 
| 469 |  |  |  |  |  |  | from Galactic coordinates but internally represented in a different | 
| 470 |  |  |  |  |  |  | coordinate frame. | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | $native_method = $c->native; | 
| 473 |  |  |  |  |  |  |  | 
| 474 |  |  |  |  |  |  | This method can then be called to retrieve the coordinates: | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | ($c1, $c2) = $c->$native_method(); | 
| 477 |  |  |  |  |  |  |  | 
| 478 |  |  |  |  |  |  | Currently, the native form will not exactly match the supplied form | 
| 479 |  |  |  |  |  |  | if a non-standard equinox has been used, or if proper motions and parallax | 
| 480 |  |  |  |  |  |  | are present, but the resulting answer can be used as a guide. | 
| 481 |  |  |  |  |  |  |  | 
| 482 |  |  |  |  |  |  | If no native method is obvious (e.g. for a planet), 'apparent' will | 
| 483 |  |  |  |  |  |  | be returned. | 
| 484 |  |  |  |  |  |  |  | 
| 485 |  |  |  |  |  |  | =cut | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  | sub native { | 
| 488 | 1043 |  |  | 1043 | 1 | 1837 | my $self = shift; | 
| 489 | 1043 | 100 |  |  |  | 2409 | if (@_) { | 
| 490 | 1042 |  |  |  |  | 2614 | $self->{NativeMethod} = shift; | 
| 491 |  |  |  |  |  |  | } | 
| 492 | 1043 | 50 |  |  |  | 2933 | return (defined $self->{NativeMethod} ? $self->{NativeMethod} : 'apparent' ); | 
| 493 |  |  |  |  |  |  | } | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | =back | 
| 497 |  |  |  |  |  |  |  | 
| 498 |  |  |  |  |  |  | =head2 General Methods | 
| 499 |  |  |  |  |  |  |  | 
| 500 |  |  |  |  |  |  | =over 4 | 
| 501 |  |  |  |  |  |  |  | 
| 502 |  |  |  |  |  |  | =item B<azel> | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | Return Azimuth and elevation for the currently stored time and telescope. | 
| 505 |  |  |  |  |  |  | If no telescope is present the equator is used. Returns the Az and El | 
| 506 |  |  |  |  |  |  | as C<Astro::Coords::Angle> objects. | 
| 507 |  |  |  |  |  |  |  | 
| 508 |  |  |  |  |  |  | ($az, $el) = $c->azel(); | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | =cut | 
| 511 |  |  |  |  |  |  |  | 
| 512 |  |  |  |  |  |  | sub azel { | 
| 513 | 6158 |  |  | 6158 | 1 | 8914 | my $self = shift; | 
| 514 |  |  |  |  |  |  |  | 
| 515 | 6158 |  |  |  |  | 11508 | my ($az,$el) = $self->_cache_read( "AZ", "EL" ); | 
| 516 |  |  |  |  |  |  |  | 
| 517 | 6158 | 100 | 66 |  |  | 14935 | if (!defined $az || !defined $el) { | 
| 518 |  |  |  |  |  |  |  | 
| 519 | 6157 |  |  |  |  | 12136 | my $ha = $self->ha; | 
| 520 | 6157 |  |  |  |  | 17993 | my $dec = $self->dec_app; | 
| 521 | 6157 |  |  |  |  | 19509 | my $tel = $self->telescope; | 
| 522 | 6157 | 100 |  |  |  | 20253 | my $lat = ( defined $tel ? $tel->lat : 0.0); | 
| 523 | 6157 |  |  |  |  | 53537 | ($az, $el) = Astro::PAL::palDe2h( $ha, $dec, $lat ); | 
| 524 | 6157 |  |  |  |  | 17594 | $az = new Astro::Coords::Angle( $az, units => 'rad', range => '2PI' ); | 
| 525 | 6157 |  |  |  |  | 14561 | $el = new Astro::Coords::Angle( $el, units => 'rad' ); | 
| 526 |  |  |  |  |  |  |  | 
| 527 | 6157 |  |  |  |  | 14629 | $self->_cache_write( "AZ" => $az, "EL" => $el ); | 
| 528 |  |  |  |  |  |  | } | 
| 529 | 6158 |  |  |  |  | 15823 | return ($az, $el); | 
| 530 |  |  |  |  |  |  | } | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | =item B<ra_app> | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | Apparent RA for the current time. | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | $ra_app = $c->ra_app( format => "s" ); | 
| 538 |  |  |  |  |  |  |  | 
| 539 |  |  |  |  |  |  | See L<"NOTES"> for details on the supported format specifiers | 
| 540 |  |  |  |  |  |  | and default calling convention. | 
| 541 |  |  |  |  |  |  |  | 
| 542 |  |  |  |  |  |  | =cut | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | sub ra_app { | 
| 545 | 11639 |  |  | 11639 | 1 | 19868 | my $self = shift; | 
| 546 | 11639 |  |  |  |  | 20425 | my %opt = @_; | 
| 547 |  |  |  |  |  |  |  | 
| 548 | 11639 |  |  |  |  | 23734 | my $ra = $self->_cache_read( "RA_APP" ); | 
| 549 | 11639 | 100 |  |  |  | 23452 | if (!defined $ra) { | 
| 550 | 7510 |  |  |  |  | 21515 | $ra = ($self->apparent)[0]; | 
| 551 |  |  |  |  |  |  | } | 
| 552 | 11639 |  |  |  |  | 49093 | return $ra->in_format( $opt{format} ); | 
| 553 |  |  |  |  |  |  | } | 
| 554 |  |  |  |  |  |  |  | 
| 555 |  |  |  |  |  |  |  | 
| 556 |  |  |  |  |  |  | =item B<dec_app> | 
| 557 |  |  |  |  |  |  |  | 
| 558 |  |  |  |  |  |  | Apparent Dec for the currently stored time. | 
| 559 |  |  |  |  |  |  |  | 
| 560 |  |  |  |  |  |  | $dec_app = $c->dec_app( format => "s" ); | 
| 561 |  |  |  |  |  |  |  | 
| 562 |  |  |  |  |  |  | See L<"NOTES"> for details on the supported format specifiers | 
| 563 |  |  |  |  |  |  | and default calling convention. | 
| 564 |  |  |  |  |  |  |  | 
| 565 |  |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | =cut | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | sub dec_app { | 
| 569 | 9257 |  |  | 9257 | 1 | 15262 | my $self = shift; | 
| 570 | 9257 |  |  |  |  | 15502 | my %opt = @_; | 
| 571 | 9257 |  |  |  |  | 18605 | my $dec = $self->_cache_read( "DEC_APP" ); | 
| 572 | 9257 | 100 |  |  |  | 18625 | if (!defined $dec) { | 
| 573 | 6037 |  |  |  |  | 15164 | $dec = ($self->apparent)[1]; | 
| 574 |  |  |  |  |  |  | } | 
| 575 | 9257 |  |  |  |  | 36475 | return $dec->in_format( $opt{format} ); | 
| 576 |  |  |  |  |  |  | } | 
| 577 |  |  |  |  |  |  |  | 
| 578 |  |  |  |  |  |  | =item B<ha> | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  | Get the hour angle for the currently stored LST. By default HA is returned | 
| 581 |  |  |  |  |  |  | as an C<Astro::Coords::Angle::Hour> object. | 
| 582 |  |  |  |  |  |  |  | 
| 583 |  |  |  |  |  |  | $ha = $c->ha; | 
| 584 |  |  |  |  |  |  | $ha = $c->ha( format => "h" ); | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | By default the Hour Angle will be normalised to +/- 12h if an explicit | 
| 587 |  |  |  |  |  |  | format is specified. | 
| 588 |  |  |  |  |  |  |  | 
| 589 |  |  |  |  |  |  | See L<"NOTES"> for details on the supported format specifiers | 
| 590 |  |  |  |  |  |  | and default calling convention. | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | =cut | 
| 593 |  |  |  |  |  |  |  | 
| 594 |  |  |  |  |  |  | # normalize key was supported but should its absence imply no normalization? | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | sub ha { | 
| 597 | 6194 |  |  | 6194 | 1 | 8535 | my $self = shift; | 
| 598 | 6194 |  |  |  |  | 9902 | my %opt = @_; | 
| 599 |  |  |  |  |  |  |  | 
| 600 | 6194 |  |  |  |  | 9858 | my $ha = $self->_cache_read( "HA" ); | 
| 601 | 6194 | 100 |  |  |  | 12204 | if (!defined $ha) { | 
| 602 | 6160 |  |  |  |  | 12133 | $ha = $self->_lst - $self->ra_app; | 
| 603 |  |  |  |  |  |  | # Always normalize? | 
| 604 | 6160 |  |  |  |  | 26511 | $ha = new Astro::Coords::Angle::Hour( $ha, units => 'rad', range => 'PI' ); | 
| 605 | 6160 |  |  |  |  | 13418 | $self->_cache_write( "HA" => $ha ); | 
| 606 |  |  |  |  |  |  | } | 
| 607 | 6194 |  |  |  |  | 20351 | return $ha->in_format( $opt{format} ); | 
| 608 |  |  |  |  |  |  | } | 
| 609 |  |  |  |  |  |  |  | 
| 610 |  |  |  |  |  |  | =item B<az> | 
| 611 |  |  |  |  |  |  |  | 
| 612 |  |  |  |  |  |  | Azimuth of the source for the currently stored time at the current | 
| 613 |  |  |  |  |  |  | telescope. See L<"NOTES"> for details on the supported format specifiers | 
| 614 |  |  |  |  |  |  | and default calling convention. | 
| 615 |  |  |  |  |  |  |  | 
| 616 |  |  |  |  |  |  | $az = $c->az(); | 
| 617 |  |  |  |  |  |  |  | 
| 618 |  |  |  |  |  |  | If no telescope is defined the equator is used. | 
| 619 |  |  |  |  |  |  |  | 
| 620 |  |  |  |  |  |  | =cut | 
| 621 |  |  |  |  |  |  |  | 
| 622 |  |  |  |  |  |  | sub az { | 
| 623 | 47 |  |  | 47 | 1 | 171 | my $self = shift; | 
| 624 | 47 |  |  |  |  | 146 | my %opt = @_; | 
| 625 | 47 |  |  |  |  | 145 | my $az = $self->_cache_read( "AZ" ); | 
| 626 | 47 | 100 |  |  |  | 217 | if (!defined $az) { | 
| 627 | 14 |  |  |  |  | 47 | ($az, my $el) = $self->azel(); | 
| 628 |  |  |  |  |  |  | } | 
| 629 | 47 |  |  |  |  | 164 | return $az->in_format( $opt{format} ); | 
| 630 |  |  |  |  |  |  | } | 
| 631 |  |  |  |  |  |  |  | 
| 632 |  |  |  |  |  |  | =item B<el> | 
| 633 |  |  |  |  |  |  |  | 
| 634 |  |  |  |  |  |  | Elevation of the source for the currently stored time at the current | 
| 635 |  |  |  |  |  |  | telescope. See L<"NOTES"> for details on the supported format specifiers | 
| 636 |  |  |  |  |  |  | and default calling convention. | 
| 637 |  |  |  |  |  |  |  | 
| 638 |  |  |  |  |  |  | $el = $c->el(); | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | If no telescope is defined the equator is used. | 
| 641 |  |  |  |  |  |  |  | 
| 642 |  |  |  |  |  |  | =cut | 
| 643 |  |  |  |  |  |  |  | 
| 644 |  |  |  |  |  |  | sub el { | 
| 645 | 6172 |  |  | 6172 | 1 | 12352 | my $self = shift; | 
| 646 | 6172 |  |  |  |  | 10662 | my %opt = @_; | 
| 647 | 6172 |  |  |  |  | 12890 | my $el = $self->_cache_read( "EL" ); | 
| 648 |  |  |  |  |  |  |  | 
| 649 | 6172 | 100 |  |  |  | 13209 | if (!defined $el) { | 
| 650 | 6126 |  |  |  |  | 12511 | (my $az, $el) = $self->azel(); | 
| 651 |  |  |  |  |  |  | } | 
| 652 | 6172 |  |  |  |  | 23519 | return $el->in_format( $opt{format} ); | 
| 653 |  |  |  |  |  |  | } | 
| 654 |  |  |  |  |  |  |  | 
| 655 |  |  |  |  |  |  | =item B<airmass> | 
| 656 |  |  |  |  |  |  |  | 
| 657 |  |  |  |  |  |  | Airmass of the source for the currently stored time at the current | 
| 658 |  |  |  |  |  |  | telescope. | 
| 659 |  |  |  |  |  |  |  | 
| 660 |  |  |  |  |  |  | $am = $c->airmass(); | 
| 661 |  |  |  |  |  |  |  | 
| 662 |  |  |  |  |  |  | Value determined from the current elevation. | 
| 663 |  |  |  |  |  |  |  | 
| 664 |  |  |  |  |  |  | =cut | 
| 665 |  |  |  |  |  |  |  | 
| 666 |  |  |  |  |  |  | sub airmass { | 
| 667 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 668 | 0 |  |  |  |  | 0 | my $el = $self->el; | 
| 669 | 0 |  |  |  |  | 0 | my $zd = Astro::PAL::DPIBY2 - $el; | 
| 670 | 0 |  |  |  |  | 0 | return Astro::PAL::palAirmas( $zd ); | 
| 671 |  |  |  |  |  |  | } | 
| 672 |  |  |  |  |  |  |  | 
| 673 |  |  |  |  |  |  | =item B<radec> | 
| 674 |  |  |  |  |  |  |  | 
| 675 |  |  |  |  |  |  | Return the J2000 Right Ascension and Declination for the target. Unless | 
| 676 |  |  |  |  |  |  | overridden by a subclass, this converts from the apparent RA/Dec to J2000. | 
| 677 |  |  |  |  |  |  | Returns two C<Astro::Coords::Angle> objects. | 
| 678 |  |  |  |  |  |  |  | 
| 679 |  |  |  |  |  |  | ($ra, $dec) = $c->radec(); | 
| 680 |  |  |  |  |  |  |  | 
| 681 |  |  |  |  |  |  | =cut | 
| 682 |  |  |  |  |  |  |  | 
| 683 |  |  |  |  |  |  | sub radec { | 
| 684 | 9 |  |  | 9 | 1 | 17 | my $self = shift; | 
| 685 | 9 |  | 100 |  |  | 67 | my ($sys, $equ) = $self->_parse_equinox( shift || 'J2000' ); | 
| 686 | 9 |  |  |  |  | 50 | my ($ra_app, $dec_app) = $self->apparent; | 
| 687 | 9 |  |  |  |  | 30 | my $mjd = $self->_mjd_tt; | 
| 688 | 9 |  |  |  |  | 26 | my ($rm, $dm); | 
| 689 | 9 | 50 |  |  |  | 33 | if ($sys eq 'FK5') { | 
|  |  | 0 |  |  |  |  |  | 
| 690 |  |  |  |  |  |  | # Julian epoch | 
| 691 | 9 |  |  |  |  | 54 | ($rm, $dm) = Astro::PAL::palAmp($ra_app, $dec_app, $mjd, $equ); | 
| 692 |  |  |  |  |  |  | } elsif ($sys eq 'FK4') { | 
| 693 |  |  |  |  |  |  | # Convert to J2000 and then convert to Besselian epoch | 
| 694 | 0 |  |  |  |  | 0 | ($rm, $dm) = Astro::PAL::palAmp($ra_app, $dec_app, $mjd, 2000.0); | 
| 695 |  |  |  |  |  |  |  | 
| 696 | 0 |  |  |  |  | 0 | ($rm, $dm) = $self->_j2000_to_byyyy( $equ, $rm, $dm); | 
| 697 |  |  |  |  |  |  | } | 
| 698 |  |  |  |  |  |  |  | 
| 699 | 9 |  |  |  |  | 57 | return (new Astro::Coords::Angle::Hour( $rm, units => 'rad', range => '2PI'), | 
| 700 |  |  |  |  |  |  | new Astro::Coords::Angle( $dm, units => 'rad' )); | 
| 701 |  |  |  |  |  |  | } | 
| 702 |  |  |  |  |  |  |  | 
| 703 |  |  |  |  |  |  | =item B<ra> | 
| 704 |  |  |  |  |  |  |  | 
| 705 |  |  |  |  |  |  | Return the J2000 Right ascension for the target. Unless overridden | 
| 706 |  |  |  |  |  |  | by a subclass this converts the apparent RA/Dec to J2000. | 
| 707 |  |  |  |  |  |  |  | 
| 708 |  |  |  |  |  |  | $ra2000 = $c->ra( format => "s" ); | 
| 709 |  |  |  |  |  |  |  | 
| 710 |  |  |  |  |  |  | Calls the C<radec> method. See L<"NOTES"> for details on the supported | 
| 711 |  |  |  |  |  |  | format specifiers and default calling convention. | 
| 712 |  |  |  |  |  |  |  | 
| 713 |  |  |  |  |  |  | =cut | 
| 714 |  |  |  |  |  |  |  | 
| 715 |  |  |  |  |  |  | sub ra { | 
| 716 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 717 | 0 |  |  |  |  | 0 | my %opt = @_; | 
| 718 | 0 |  |  |  |  | 0 | my ($ra,$dec) = $self->radec; | 
| 719 | 0 |  |  |  |  | 0 | return $ra->in_format( $opt{format} ); | 
| 720 |  |  |  |  |  |  | } | 
| 721 |  |  |  |  |  |  |  | 
| 722 |  |  |  |  |  |  | =item B<dec> | 
| 723 |  |  |  |  |  |  |  | 
| 724 |  |  |  |  |  |  | Return the J2000 declination for the target. Unless overridden | 
| 725 |  |  |  |  |  |  | by a subclass this converts the apparent RA/Dec to J2000. | 
| 726 |  |  |  |  |  |  |  | 
| 727 |  |  |  |  |  |  | $dec2000 = $c->dec( format => "s" ); | 
| 728 |  |  |  |  |  |  |  | 
| 729 |  |  |  |  |  |  | Calls the C<radec> method. See L<"NOTES"> for details on the supported | 
| 730 |  |  |  |  |  |  | format specifiers and default calling convention. | 
| 731 |  |  |  |  |  |  |  | 
| 732 |  |  |  |  |  |  | =cut | 
| 733 |  |  |  |  |  |  |  | 
| 734 |  |  |  |  |  |  | sub dec { | 
| 735 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 736 | 0 |  |  |  |  | 0 | my %opt = @_; | 
| 737 | 0 |  |  |  |  | 0 | my ($ra,$dec) = $self->radec; | 
| 738 | 0 |  |  |  |  | 0 | return $dec->in_format( $opt{format} ); | 
| 739 |  |  |  |  |  |  | } | 
| 740 |  |  |  |  |  |  |  | 
| 741 |  |  |  |  |  |  | =item B<glong> | 
| 742 |  |  |  |  |  |  |  | 
| 743 |  |  |  |  |  |  | Return Galactic longitude. See L<"NOTES"> for details on the supported | 
| 744 |  |  |  |  |  |  | format specifiers and default calling convention. | 
| 745 |  |  |  |  |  |  |  | 
| 746 |  |  |  |  |  |  | $glong = $c->glong( format => "s" ); | 
| 747 |  |  |  |  |  |  |  | 
| 748 |  |  |  |  |  |  | =cut | 
| 749 |  |  |  |  |  |  |  | 
| 750 |  |  |  |  |  |  | sub glong { | 
| 751 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 752 | 0 |  |  |  |  | 0 | my %opt = @_; | 
| 753 | 0 |  |  |  |  | 0 | my ($glong,$glat) = $self->glonglat(); | 
| 754 | 0 |  |  |  |  | 0 | return $glong->in_format( $opt{format} ); | 
| 755 |  |  |  |  |  |  | } | 
| 756 |  |  |  |  |  |  |  | 
| 757 |  |  |  |  |  |  | =item B<glat> | 
| 758 |  |  |  |  |  |  |  | 
| 759 |  |  |  |  |  |  | Return Galactic latitude. See L<"NOTES"> for details on the supported | 
| 760 |  |  |  |  |  |  | format specifiers and default calling convention. | 
| 761 |  |  |  |  |  |  |  | 
| 762 |  |  |  |  |  |  |  | 
| 763 |  |  |  |  |  |  | $glat = $c->glat( format => "s" ); | 
| 764 |  |  |  |  |  |  |  | 
| 765 |  |  |  |  |  |  | =cut | 
| 766 |  |  |  |  |  |  |  | 
| 767 |  |  |  |  |  |  | sub glat { | 
| 768 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 769 | 0 |  |  |  |  | 0 | my %opt = @_; | 
| 770 | 0 |  |  |  |  | 0 | my ($glong,$glat) = $self->glonglat(); | 
| 771 | 0 |  |  |  |  | 0 | return $glat->in_format( $opt{format} ); | 
| 772 |  |  |  |  |  |  | } | 
| 773 |  |  |  |  |  |  |  | 
| 774 |  |  |  |  |  |  | =item B<sglong> | 
| 775 |  |  |  |  |  |  |  | 
| 776 |  |  |  |  |  |  | Return SuperGalactic longitude. See L<"NOTES"> for details on the | 
| 777 |  |  |  |  |  |  | supported format specifiers and default calling convention. | 
| 778 |  |  |  |  |  |  |  | 
| 779 |  |  |  |  |  |  | $sglong = $c->sglong( format => "s" ); | 
| 780 |  |  |  |  |  |  |  | 
| 781 |  |  |  |  |  |  | =cut | 
| 782 |  |  |  |  |  |  |  | 
| 783 |  |  |  |  |  |  | sub sglong { | 
| 784 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 785 | 0 |  |  |  |  | 0 | my %opt = @_; | 
| 786 | 0 |  |  |  |  | 0 | my ($sglong,$sglat) = $self->sglonglat(); | 
| 787 | 0 |  |  |  |  | 0 | return $sglong->in_format( $opt{format} ); | 
| 788 |  |  |  |  |  |  | } | 
| 789 |  |  |  |  |  |  |  | 
| 790 |  |  |  |  |  |  | =item B<sglat> | 
| 791 |  |  |  |  |  |  |  | 
| 792 |  |  |  |  |  |  | Return SuperGalactic latitude. See L<"NOTES"> for details on the supported format specifiers and default calling convention. | 
| 793 |  |  |  |  |  |  |  | 
| 794 |  |  |  |  |  |  | $glat = $c->sglat( format => "s" ); | 
| 795 |  |  |  |  |  |  |  | 
| 796 |  |  |  |  |  |  | =cut | 
| 797 |  |  |  |  |  |  |  | 
| 798 |  |  |  |  |  |  | sub sglat { | 
| 799 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 800 | 0 |  |  |  |  | 0 | my %opt = @_; | 
| 801 | 0 |  |  |  |  | 0 | my ($sglong,$sglat) = $self->sglonglat(); | 
| 802 | 0 |  |  |  |  | 0 | return $sglat->in_format( $opt{format} ); | 
| 803 |  |  |  |  |  |  | } | 
| 804 |  |  |  |  |  |  |  | 
| 805 |  |  |  |  |  |  | =item B<ecllong> | 
| 806 |  |  |  |  |  |  |  | 
| 807 |  |  |  |  |  |  | Return Ecliptic longitude. See L<"NOTES"> for details on the supported | 
| 808 |  |  |  |  |  |  | format specifiers and default calling convention. | 
| 809 |  |  |  |  |  |  |  | 
| 810 |  |  |  |  |  |  | $eclong = $c->ecllong( format => "s" ); | 
| 811 |  |  |  |  |  |  |  | 
| 812 |  |  |  |  |  |  | =cut | 
| 813 |  |  |  |  |  |  |  | 
| 814 |  |  |  |  |  |  | sub ecllong { | 
| 815 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 816 | 0 |  |  |  |  | 0 | my %opt = @_; | 
| 817 | 0 |  |  |  |  | 0 | my ($eclong,$eclat) = $self->ecllonglat(); | 
| 818 | 0 |  |  |  |  | 0 | return $eclong->in_format( $opt{format} ); | 
| 819 |  |  |  |  |  |  | } | 
| 820 |  |  |  |  |  |  |  | 
| 821 |  |  |  |  |  |  | =item B<ecllat> | 
| 822 |  |  |  |  |  |  |  | 
| 823 |  |  |  |  |  |  | Return ecliptic latitude. See L<"NOTES"> for details on the supported | 
| 824 |  |  |  |  |  |  | format specifiers and default calling convention. | 
| 825 |  |  |  |  |  |  |  | 
| 826 |  |  |  |  |  |  | $eclat = $c->ecllat( format => "s" ); | 
| 827 |  |  |  |  |  |  |  | 
| 828 |  |  |  |  |  |  | =cut | 
| 829 |  |  |  |  |  |  |  | 
| 830 |  |  |  |  |  |  | sub ecllat { | 
| 831 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 832 | 0 |  |  |  |  | 0 | my %opt = @_; | 
| 833 | 0 |  |  |  |  | 0 | my ($eclong,$eclat) = $self->ecllonglat(); | 
| 834 | 0 |  |  |  |  | 0 | return $eclat->in_format( $opt{format} ); | 
| 835 |  |  |  |  |  |  | } | 
| 836 |  |  |  |  |  |  |  | 
| 837 |  |  |  |  |  |  | =item B<glonglat> | 
| 838 |  |  |  |  |  |  |  | 
| 839 |  |  |  |  |  |  | Calculate Galactic longitude and latitude. Position is calculated for | 
| 840 |  |  |  |  |  |  | the current ra/dec position (as returned by the C<radec> method). | 
| 841 |  |  |  |  |  |  |  | 
| 842 |  |  |  |  |  |  | ($long, $lat) = $c->glonglat; | 
| 843 |  |  |  |  |  |  |  | 
| 844 |  |  |  |  |  |  | Answer is returned as two C<Astro::Coords::Angle> objects. | 
| 845 |  |  |  |  |  |  |  | 
| 846 |  |  |  |  |  |  | =cut | 
| 847 |  |  |  |  |  |  |  | 
| 848 |  |  |  |  |  |  | sub glonglat { | 
| 849 | 6 |  |  | 6 | 1 | 26 | my $self = shift; | 
| 850 | 6 |  |  |  |  | 103 | my ($ra,$dec) = $self->radec; | 
| 851 | 6 |  |  |  |  | 34 | my ($long, $lat) = Astro::PAL::palEqgal( $ra, $dec ); | 
| 852 | 6 |  |  |  |  | 21 | return (new Astro::Coords::Angle($long, units => 'rad', range => '2PI'), | 
| 853 |  |  |  |  |  |  | new Astro::Coords::Angle($lat, units => 'rad')); | 
| 854 |  |  |  |  |  |  | } | 
| 855 |  |  |  |  |  |  |  | 
| 856 |  |  |  |  |  |  | =item B<sglonglat> | 
| 857 |  |  |  |  |  |  |  | 
| 858 |  |  |  |  |  |  | Calculate Super Galactic longitude and latitude. | 
| 859 |  |  |  |  |  |  |  | 
| 860 |  |  |  |  |  |  | ($slong, $slat) = $c->sglonglat; | 
| 861 |  |  |  |  |  |  |  | 
| 862 |  |  |  |  |  |  | Answer is returned as two C<Astro::Coords::Angle> objects. | 
| 863 |  |  |  |  |  |  |  | 
| 864 |  |  |  |  |  |  | =cut | 
| 865 |  |  |  |  |  |  |  | 
| 866 |  |  |  |  |  |  | sub sglonglat { | 
| 867 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 868 | 0 |  |  |  |  | 0 | my ($glong, $glat) = $self->glonglat(); | 
| 869 | 0 |  |  |  |  | 0 | my ($sglong, $sglat) = Astro::PAL::palGalsup( $glong, $glat ); | 
| 870 | 0 |  |  |  |  | 0 | return (new Astro::Coords::Angle($sglong, units => 'rad', range => '2PI'), | 
| 871 |  |  |  |  |  |  | new Astro::Coords::Angle($sglat, units => 'rad')); | 
| 872 |  |  |  |  |  |  | } | 
| 873 |  |  |  |  |  |  |  | 
| 874 |  |  |  |  |  |  | =item B<ecllonglat> | 
| 875 |  |  |  |  |  |  |  | 
| 876 |  |  |  |  |  |  | Calculate the ecliptic longitude and latitude for the epoch stored in | 
| 877 |  |  |  |  |  |  | the object. Position is calculated for the current ra/dec position (as | 
| 878 |  |  |  |  |  |  | returned by the C<radec> method. | 
| 879 |  |  |  |  |  |  |  | 
| 880 |  |  |  |  |  |  | ($long, $lat) = $c->ecllonglat(); | 
| 881 |  |  |  |  |  |  |  | 
| 882 |  |  |  |  |  |  | Answer is returned as two C<Astro::Coords::Angle> objects. | 
| 883 |  |  |  |  |  |  |  | 
| 884 |  |  |  |  |  |  | =cut | 
| 885 |  |  |  |  |  |  |  | 
| 886 |  |  |  |  |  |  | sub ecllonglat { | 
| 887 | 2 |  |  | 2 | 1 | 6 | my $self = shift; | 
| 888 | 2 |  |  |  |  | 9 | my ($ra, $dec) = $self->radec; | 
| 889 | 2 |  |  |  |  | 79 | my ($long, $lat) = Astro::PAL::palEqecl( $ra, $dec, $self->_mjd_tt ); | 
| 890 | 2 |  |  |  |  | 11 | return (new Astro::Coords::Angle($long, units => 'rad', range => '2PI'), | 
| 891 |  |  |  |  |  |  | new Astro::Coords::Angle($lat, units => 'rad')); | 
| 892 |  |  |  |  |  |  | } | 
| 893 |  |  |  |  |  |  |  | 
| 894 |  |  |  |  |  |  | =item B<radec2000> | 
| 895 |  |  |  |  |  |  |  | 
| 896 |  |  |  |  |  |  | Convenience wrapper routine to return the J2000 coordinates for epoch | 
| 897 |  |  |  |  |  |  | 2000.0. This is not the same as calling the C<radec> method with | 
| 898 |  |  |  |  |  |  | equinox J2000.0. | 
| 899 |  |  |  |  |  |  |  | 
| 900 |  |  |  |  |  |  | ($ra2000, $dec2000) = $c->radec2000; | 
| 901 |  |  |  |  |  |  |  | 
| 902 |  |  |  |  |  |  | It is equivalent to setting the epoch in the object to 2000.0 | 
| 903 |  |  |  |  |  |  | (ie midday on 2000 January 1) and then calling C<radec>. | 
| 904 |  |  |  |  |  |  |  | 
| 905 |  |  |  |  |  |  | The answer will be location dependent in most cases. | 
| 906 |  |  |  |  |  |  |  | 
| 907 |  |  |  |  |  |  | Results are returned as two C<Astro::Coords::Angle> objects. | 
| 908 |  |  |  |  |  |  |  | 
| 909 |  |  |  |  |  |  | =cut | 
| 910 |  |  |  |  |  |  |  | 
| 911 |  |  |  |  |  |  | sub radec2000 { | 
| 912 | 3 |  |  | 3 | 1 | 6 | my $self = shift; | 
| 913 |  |  |  |  |  |  |  | 
| 914 |  |  |  |  |  |  | # store current configuration | 
| 915 | 3 | 50 |  |  |  | 17 | my $reftime = ( $self->has_datetime ? $self->datetime : undef); | 
| 916 |  |  |  |  |  |  |  | 
| 917 |  |  |  |  |  |  | # Create new time | 
| 918 | 3 |  |  |  |  | 30 | $self->datetime( DateTime->new( year => 2000, month => 1, | 
| 919 |  |  |  |  |  |  | day => 1, hour => 12) ); | 
| 920 |  |  |  |  |  |  |  | 
| 921 |  |  |  |  |  |  | # Ask for the answer | 
| 922 | 3 |  |  |  |  | 32 | my ($ra, $dec) = $self->radec( 'J2000' ); | 
| 923 |  |  |  |  |  |  |  | 
| 924 |  |  |  |  |  |  | # restore the date state | 
| 925 | 3 |  |  |  |  | 37 | $self->datetime( $reftime ); | 
| 926 |  |  |  |  |  |  |  | 
| 927 | 3 |  |  |  |  | 1433 | return ($ra, $dec); | 
| 928 |  |  |  |  |  |  | } | 
| 929 |  |  |  |  |  |  |  | 
| 930 |  |  |  |  |  |  | =item B<radec1950> | 
| 931 |  |  |  |  |  |  |  | 
| 932 |  |  |  |  |  |  | Convenience wrapper to return the FK4 B1950 coordinates for the | 
| 933 |  |  |  |  |  |  | currently defined epoch. Since the FK4 to FK5 conversion requires an | 
| 934 |  |  |  |  |  |  | epoch, the J2000 coordinates are first calculated for the current | 
| 935 |  |  |  |  |  |  | epoch and the frame conversion is done to epoch B1950. | 
| 936 |  |  |  |  |  |  |  | 
| 937 |  |  |  |  |  |  | This is technically not the same as calling the radec() method with | 
| 938 |  |  |  |  |  |  | equinox B1950 since that would use the current epoch associated with | 
| 939 |  |  |  |  |  |  | the coordinates when converting from FK4 to FK5. | 
| 940 |  |  |  |  |  |  |  | 
| 941 |  |  |  |  |  |  | In the base class these are calculated by precessing the J2000 RA/Dec | 
| 942 |  |  |  |  |  |  | for the current date and time, which are themselves derived from the | 
| 943 |  |  |  |  |  |  | apparent RA/Dec for the current time. | 
| 944 |  |  |  |  |  |  |  | 
| 945 |  |  |  |  |  |  | ($ra, $dec) = $c->radec1950; | 
| 946 |  |  |  |  |  |  |  | 
| 947 |  |  |  |  |  |  | Results are returned as two C<Astro::Coords::Angle> objects. | 
| 948 |  |  |  |  |  |  |  | 
| 949 |  |  |  |  |  |  | =cut | 
| 950 |  |  |  |  |  |  |  | 
| 951 |  |  |  |  |  |  | sub radec1950 { | 
| 952 | 3 |  |  | 3 | 1 | 21 | my $self = shift; | 
| 953 | 3 |  |  |  |  | 10 | my ($ra, $dec) = $self->radec; | 
| 954 |  |  |  |  |  |  |  | 
| 955 |  |  |  |  |  |  | # No E-terms or precession since we are going to B1950 epoch 1950 | 
| 956 | 3 |  |  |  |  | 22 | my ($r1950, $d1950, $dr1950, $dd1950) = Astro::PAL::palFk54z($ra,$dec,1950.0); | 
| 957 | 3 |  |  |  |  | 13 | return (new Astro::Coords::Angle::Hour( $r1950, units => 'rad', range => '2PI'), | 
| 958 |  |  |  |  |  |  | new Astro::Coords::Angle( $d1950, units => 'rad' )); | 
| 959 |  |  |  |  |  |  | } | 
| 960 |  |  |  |  |  |  |  | 
| 961 |  |  |  |  |  |  | =item B<pa> | 
| 962 |  |  |  |  |  |  |  | 
| 963 |  |  |  |  |  |  | Parallactic angle of the source for the currently stored time at the | 
| 964 |  |  |  |  |  |  | current telescope. See L<"NOTES"> for details on the supported format | 
| 965 |  |  |  |  |  |  | specifiers and default calling convention. | 
| 966 |  |  |  |  |  |  |  | 
| 967 |  |  |  |  |  |  | $pa = $c->pa(); | 
| 968 |  |  |  |  |  |  | $padeg = $c->pa( format => 'deg' ); | 
| 969 |  |  |  |  |  |  |  | 
| 970 |  |  |  |  |  |  | If no telescope is defined the equator is used. | 
| 971 |  |  |  |  |  |  |  | 
| 972 |  |  |  |  |  |  | =cut | 
| 973 |  |  |  |  |  |  |  | 
| 974 |  |  |  |  |  |  | sub pa { | 
| 975 | 25 |  |  | 25 | 1 | 38 | my $self = shift; | 
| 976 | 25 |  |  |  |  | 46 | my %opt = @_; | 
| 977 | 25 |  |  |  |  | 57 | my $ha = $self->ha; | 
| 978 | 25 |  |  |  |  | 95 | my $dec = $self->dec_app; | 
| 979 | 25 |  |  |  |  | 73 | my $tel = $self->telescope; | 
| 980 | 25 | 50 |  |  |  | 53 | my $lat = ( defined $tel ? $tel->lat : 0.0); | 
| 981 | 25 |  |  |  |  | 87 | my $pa = Astro::PAL::palPa($ha, $dec, $lat); | 
| 982 | 25 |  |  |  |  | 79 | $pa = new Astro::Coords::Angle( $pa, units => 'rad' ); | 
| 983 | 25 |  |  |  |  | 92 | return $pa->in_format( $opt{format} ); | 
| 984 |  |  |  |  |  |  | } | 
| 985 |  |  |  |  |  |  |  | 
| 986 |  |  |  |  |  |  |  | 
| 987 |  |  |  |  |  |  | =item B<isObservable> | 
| 988 |  |  |  |  |  |  |  | 
| 989 |  |  |  |  |  |  | Determine whether the coordinates are accessible for the current | 
| 990 |  |  |  |  |  |  | time and telescope. | 
| 991 |  |  |  |  |  |  |  | 
| 992 |  |  |  |  |  |  | $isobs = $c->isObservable; | 
| 993 |  |  |  |  |  |  |  | 
| 994 |  |  |  |  |  |  | Returns false if a telescope has not been specified (see | 
| 995 |  |  |  |  |  |  | the C<telescope> method) or if the specified telescope does not | 
| 996 |  |  |  |  |  |  | know its own limits. | 
| 997 |  |  |  |  |  |  |  | 
| 998 |  |  |  |  |  |  | =cut | 
| 999 |  |  |  |  |  |  |  | 
| 1000 |  |  |  |  |  |  | sub isObservable { | 
| 1001 | 15 |  |  | 15 | 1 | 13249 | my $self = shift; | 
| 1002 |  |  |  |  |  |  |  | 
| 1003 |  |  |  |  |  |  | # Get the telescope | 
| 1004 | 15 |  |  |  |  | 38 | my $tel = $self->telescope; | 
| 1005 | 15 | 50 |  |  |  | 61 | return 0 unless defined $tel; | 
| 1006 |  |  |  |  |  |  |  | 
| 1007 |  |  |  |  |  |  | # Get the limits hash | 
| 1008 | 15 |  |  |  |  | 54 | my %limits = $tel->limits; | 
| 1009 |  |  |  |  |  |  |  | 
| 1010 | 15 | 50 |  |  |  | 558 | if (exists $limits{type}) { | 
| 1011 |  |  |  |  |  |  |  | 
| 1012 | 15 | 100 |  |  |  | 60 | if ($limits{type} eq 'AZEL') { | 
|  |  | 50 |  |  |  |  |  | 
| 1013 |  |  |  |  |  |  |  | 
| 1014 |  |  |  |  |  |  | # Get the current elevation of the source | 
| 1015 | 12 |  |  |  |  | 52 | my $el = $self->el; | 
| 1016 |  |  |  |  |  |  |  | 
| 1017 | 12 | 100 | 66 |  |  | 59 | if ($el > $limits{el}{min} and $el < $limits{el}{max}) { | 
| 1018 | 8 |  |  |  |  | 68 | return 1; | 
| 1019 |  |  |  |  |  |  | } else { | 
| 1020 | 4 |  |  |  |  | 24 | return 0; | 
| 1021 |  |  |  |  |  |  | } | 
| 1022 |  |  |  |  |  |  |  | 
| 1023 |  |  |  |  |  |  | } elsif ($limits{type} eq 'HADEC') { | 
| 1024 |  |  |  |  |  |  |  | 
| 1025 |  |  |  |  |  |  | # Get the current HA | 
| 1026 | 3 |  |  |  |  | 11 | my $ha = $self->ha( normalize => 1 ); | 
| 1027 |  |  |  |  |  |  |  | 
| 1028 | 3 | 100 | 66 |  |  | 14 | if ( $ha > $limits{ha}{min} and $ha < $limits{ha}{max}) { | 
| 1029 | 2 |  |  |  |  | 17 | my $dec= $self->dec_app; | 
| 1030 |  |  |  |  |  |  |  | 
| 1031 | 2 | 100 | 66 |  |  | 9 | if ($dec > $limits{dec}{min} and $dec < $limits{dec}{max}) { | 
| 1032 | 1 |  |  |  |  | 10 | return 1; | 
| 1033 |  |  |  |  |  |  | } else { | 
| 1034 | 1 |  |  |  |  | 7 | return 0; | 
| 1035 |  |  |  |  |  |  | } | 
| 1036 |  |  |  |  |  |  |  | 
| 1037 |  |  |  |  |  |  | } else { | 
| 1038 | 1 |  |  |  |  | 7 | return 0; | 
| 1039 |  |  |  |  |  |  | } | 
| 1040 |  |  |  |  |  |  |  | 
| 1041 |  |  |  |  |  |  | } else { | 
| 1042 |  |  |  |  |  |  | # have no idea | 
| 1043 | 0 |  |  |  |  | 0 | return 0; | 
| 1044 |  |  |  |  |  |  | } | 
| 1045 |  |  |  |  |  |  |  | 
| 1046 |  |  |  |  |  |  | } else { | 
| 1047 | 0 |  |  |  |  | 0 | return 0; | 
| 1048 |  |  |  |  |  |  | } | 
| 1049 |  |  |  |  |  |  |  | 
| 1050 |  |  |  |  |  |  | } | 
| 1051 |  |  |  |  |  |  |  | 
| 1052 |  |  |  |  |  |  |  | 
| 1053 |  |  |  |  |  |  | =item B<array> | 
| 1054 |  |  |  |  |  |  |  | 
| 1055 |  |  |  |  |  |  | Return a summary of this object in the form of an array containing | 
| 1056 |  |  |  |  |  |  | the following: | 
| 1057 |  |  |  |  |  |  |  | 
| 1058 |  |  |  |  |  |  | coordinate type (eg PLANET, RADEC, MARS) | 
| 1059 |  |  |  |  |  |  | ra2000          (J2000 RA in radians [for equatorial]) | 
| 1060 |  |  |  |  |  |  | dec2000         (J2000 dec in radians [for equatorial]) | 
| 1061 |  |  |  |  |  |  | elements        (up to 8 orbital elements) | 
| 1062 |  |  |  |  |  |  |  | 
| 1063 |  |  |  |  |  |  | =cut | 
| 1064 |  |  |  |  |  |  |  | 
| 1065 |  |  |  |  |  |  | sub array { | 
| 1066 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 1067 | 0 |  |  |  |  | 0 | croak "The method array() must be subclassed\n"; | 
| 1068 |  |  |  |  |  |  | } | 
| 1069 |  |  |  |  |  |  |  | 
| 1070 |  |  |  |  |  |  | =item B<distance> | 
| 1071 |  |  |  |  |  |  |  | 
| 1072 |  |  |  |  |  |  | Calculate the distance (on the tangent plane) between the current | 
| 1073 |  |  |  |  |  |  | coordinate and a supplied coordinate. | 
| 1074 |  |  |  |  |  |  |  | 
| 1075 |  |  |  |  |  |  | $dist = $c->distance( $c2 ); | 
| 1076 |  |  |  |  |  |  | @dist = $c->distance( $c2 ); | 
| 1077 |  |  |  |  |  |  |  | 
| 1078 |  |  |  |  |  |  | In scalar context the distance is returned as an | 
| 1079 |  |  |  |  |  |  | C<Astro::Coords::Angle> object In list context returns the individual | 
| 1080 |  |  |  |  |  |  | "x" and "y" offsets (as C<Astro::Coords::Angle> objects). | 
| 1081 |  |  |  |  |  |  |  | 
| 1082 |  |  |  |  |  |  | Returns undef if there was an error during the calculation (e.g. because | 
| 1083 |  |  |  |  |  |  | the new coordinate was too far away). | 
| 1084 |  |  |  |  |  |  |  | 
| 1085 |  |  |  |  |  |  | =cut | 
| 1086 |  |  |  |  |  |  |  | 
| 1087 |  |  |  |  |  |  | sub distance { | 
| 1088 | 1 |  |  | 1 | 1 | 11 | my $self = shift; | 
| 1089 | 1 |  |  |  |  | 2 | my $offset = shift; | 
| 1090 |  |  |  |  |  |  |  | 
| 1091 | 1 |  |  |  |  | 3 | my( $ra, $dec ) = $self->radec; | 
| 1092 | 1 |  |  |  |  | 4 | my( $ra_off, $dec_off ) = $offset->radec; | 
| 1093 |  |  |  |  |  |  |  | 
| 1094 | 1 |  |  |  |  | 6 | my ($xi, $eta, $j) = Astro::PAL::palDs2tp($ra_off, $dec_off, | 
| 1095 |  |  |  |  |  |  | $ra, $dec ); | 
| 1096 |  |  |  |  |  |  |  | 
| 1097 | 1 | 50 |  |  |  | 4 | return () unless $j == 0; | 
| 1098 |  |  |  |  |  |  |  | 
| 1099 | 1 | 50 |  |  |  | 4 | if (wantarray) { | 
| 1100 | 0 |  |  |  |  | 0 | return (new Astro::Coords::Angle($xi, units => 'rad'), | 
| 1101 |  |  |  |  |  |  | new Astro::Coords::Angle($eta, units => 'rad')); | 
| 1102 |  |  |  |  |  |  | } else { | 
| 1103 | 1 |  |  |  |  | 7 | my $dist = ($xi**2 + $eta**2)**0.5; | 
| 1104 | 1 |  |  |  |  | 4 | return new Astro::Coords::Angle( $dist, units => 'rad' ); | 
| 1105 |  |  |  |  |  |  | } | 
| 1106 |  |  |  |  |  |  | } | 
| 1107 |  |  |  |  |  |  |  | 
| 1108 |  |  |  |  |  |  |  | 
| 1109 |  |  |  |  |  |  | =item B<status> | 
| 1110 |  |  |  |  |  |  |  | 
| 1111 |  |  |  |  |  |  | Return a status string describing the current coordinates. | 
| 1112 |  |  |  |  |  |  | This consists of the current elevation, azimuth, hour angle | 
| 1113 |  |  |  |  |  |  | and declination. If a telescope is defined the observability | 
| 1114 |  |  |  |  |  |  | of the target is included. | 
| 1115 |  |  |  |  |  |  |  | 
| 1116 |  |  |  |  |  |  | $status = $c->status; | 
| 1117 |  |  |  |  |  |  |  | 
| 1118 |  |  |  |  |  |  | =cut | 
| 1119 |  |  |  |  |  |  |  | 
| 1120 |  |  |  |  |  |  | sub status { | 
| 1121 | 6 |  |  | 6 | 1 | 92452 | my $self = shift; | 
| 1122 | 6 |  |  |  |  | 15 | my $string; | 
| 1123 |  |  |  |  |  |  |  | 
| 1124 | 6 | 100 |  |  |  | 35 | $string .= "Target name:    " . $self->name . "\n" | 
| 1125 |  |  |  |  |  |  | if $self->name; | 
| 1126 |  |  |  |  |  |  |  | 
| 1127 | 6 |  |  |  |  | 35 | $string .= "Coordinate type:" . $self->type ."\n"; | 
| 1128 |  |  |  |  |  |  |  | 
| 1129 | 6 | 50 |  |  |  | 20 | if ($self->type ne 'CAL') { | 
| 1130 |  |  |  |  |  |  |  | 
| 1131 | 6 |  |  |  |  | 23 | my ($az,$el) = $self->azel; | 
| 1132 | 6 |  |  |  |  | 43 | $string .= "Elevation:      " . $el->degrees ." deg\n"; | 
| 1133 | 6 |  |  |  |  | 34 | $string .= "Azimuth  :      " . $az->degrees ." deg\n"; | 
| 1134 | 6 |  |  |  |  | 27 | my $ha = $self->ha->hours; | 
| 1135 | 6 |  |  |  |  | 42 | $string .= "Hour angle:     " . $ha ." hrs\n"; | 
| 1136 | 6 |  |  |  |  | 26 | my ($ra_app, $dec_app) = $self->apparent; | 
| 1137 | 6 |  |  |  |  | 33 | $string .= "Apparent RA :   " . $ra_app->string . "\n"; | 
| 1138 | 6 |  |  |  |  | 26 | $string .= "Apparent dec:   " . $dec_app->string ."\n"; | 
| 1139 |  |  |  |  |  |  |  | 
| 1140 |  |  |  |  |  |  | # Transit time | 
| 1141 | 6 |  |  |  |  | 41 | my $mt = $self->meridian_time; | 
| 1142 | 6 | 50 |  |  |  | 59 | $string .= "Time of next transit: " . | 
| 1143 |  |  |  |  |  |  | (defined $mt ? $mt->datetime : "<never>") ."\n"; | 
| 1144 |  |  |  |  |  |  |  | 
| 1145 | 6 |  |  |  |  | 423 | my $t_el = $self->transit_el(format=>'d'); | 
| 1146 | 6 | 50 |  |  |  | 85 | $string .= "Transit El:     " . (defined $t_el ? "$t_el deg" | 
| 1147 |  |  |  |  |  |  | : "<undefined>") ."\n"; | 
| 1148 | 6 |  |  |  |  | 36 | my $ha_set = $self->ha_set( format => 'hour'); | 
| 1149 | 6 | 50 |  |  |  | 67 | $string .= "Hour Ang. (set):" . (defined $ha_set ? $ha_set : '??')." hrs\n"; | 
| 1150 |  |  |  |  |  |  |  | 
| 1151 | 6 |  |  |  |  | 29 | my $t = $self->rise_time; | 
| 1152 | 6 | 50 |  |  |  | 63 | $string .= "Next Rise time:      " . $t->datetime . "\n" if defined $t; | 
| 1153 | 6 |  |  |  |  | 217 | $t = $self->set_time; | 
| 1154 | 6 | 50 |  |  |  | 50 | $string .= "Next Set time:       " . $t->datetime . "\n" if defined $t; | 
| 1155 |  |  |  |  |  |  |  | 
| 1156 |  |  |  |  |  |  | # This check was here before we added a RA/Dec to the | 
| 1157 |  |  |  |  |  |  | # base class. | 
| 1158 | 6 | 50 |  |  |  | 272 | if ($self->can('radec')) { | 
| 1159 | 6 |  |  |  |  | 29 | my ($ra, $dec) = $self->radec; | 
| 1160 | 6 |  |  |  |  | 24 | $string .= "RA (J2000):     " . $ra->string . "\n"; | 
| 1161 | 6 |  |  |  |  | 27 | $string .= "Dec(J2000):     " . $dec->string . "\n"; | 
| 1162 |  |  |  |  |  |  | } | 
| 1163 |  |  |  |  |  |  | } | 
| 1164 |  |  |  |  |  |  |  | 
| 1165 | 6 | 50 |  |  |  | 25 | if (defined $self->telescope) { | 
| 1166 | 6 | 50 |  |  |  | 18 | my $name = (defined $self->telescope->fullname ? | 
| 1167 |  |  |  |  |  |  | $self->telescope->fullname : $self->telescope->name ); | 
| 1168 | 6 |  |  |  |  | 40 | $string .= "Telescope:      $name\n"; | 
| 1169 | 6 | 100 |  |  |  | 37 | if ($self->isObservable) { | 
| 1170 | 3 |  |  |  |  | 10 | $string .= "The target is currently observable\n"; | 
| 1171 |  |  |  |  |  |  | } else { | 
| 1172 | 3 |  |  |  |  | 9 | $string .= "The target is not currently observable\n"; | 
| 1173 |  |  |  |  |  |  | } | 
| 1174 |  |  |  |  |  |  | } | 
| 1175 |  |  |  |  |  |  |  | 
| 1176 | 6 |  |  |  |  | 27 | $string .= "For time ". $self->datetime->datetime ."\n"; | 
| 1177 | 6 |  |  |  |  | 176 | my $fmt = 's'; | 
| 1178 | 6 |  |  |  |  | 27 | $string .= "LST: ". $self->_lst->hours ."\n"; | 
| 1179 |  |  |  |  |  |  |  | 
| 1180 | 6 |  |  |  |  | 3910 | return $string; | 
| 1181 |  |  |  |  |  |  | } | 
| 1182 |  |  |  |  |  |  |  | 
| 1183 |  |  |  |  |  |  | =item B<calculate> | 
| 1184 |  |  |  |  |  |  |  | 
| 1185 |  |  |  |  |  |  | Calculate target positions for a range of times. | 
| 1186 |  |  |  |  |  |  |  | 
| 1187 |  |  |  |  |  |  | @data = $c->calculate( start => $start, | 
| 1188 |  |  |  |  |  |  | end => $end, | 
| 1189 |  |  |  |  |  |  | inc => $increment, | 
| 1190 |  |  |  |  |  |  | units => 'deg' | 
| 1191 |  |  |  |  |  |  | ); | 
| 1192 |  |  |  |  |  |  |  | 
| 1193 |  |  |  |  |  |  | The start and end times are either C<Time::Piece> or C<DateTime> | 
| 1194 |  |  |  |  |  |  | objects and the increment is either a C<Time::Seconds> object, a | 
| 1195 |  |  |  |  |  |  | C<DateTime::Duration> object (in fact, an object that implements the | 
| 1196 |  |  |  |  |  |  | C<seconds> method) or an integer. If the end time will not necessarily | 
| 1197 |  |  |  |  |  |  | be used explictly if the increment does not divide into the total time | 
| 1198 |  |  |  |  |  |  | gap exactly. None of the returned times will exceed the end time. The | 
| 1199 |  |  |  |  |  |  | increment must be greater than zero but the start and end times can be | 
| 1200 |  |  |  |  |  |  | identical. | 
| 1201 |  |  |  |  |  |  |  | 
| 1202 |  |  |  |  |  |  | Returns an array of hashes. Each hash contains | 
| 1203 |  |  |  |  |  |  |  | 
| 1204 |  |  |  |  |  |  | time [same object class as provided as argument] | 
| 1205 |  |  |  |  |  |  | elevation | 
| 1206 |  |  |  |  |  |  | azimuth | 
| 1207 |  |  |  |  |  |  | parang | 
| 1208 |  |  |  |  |  |  | lst [always in radians] | 
| 1209 |  |  |  |  |  |  |  | 
| 1210 |  |  |  |  |  |  | The angles are in the units specified (radians, degrees or sexagesimal). They | 
| 1211 |  |  |  |  |  |  | will be Angle objects if no units are specified. | 
| 1212 |  |  |  |  |  |  |  | 
| 1213 |  |  |  |  |  |  | Note that this method returns C<DateTime> objects if it was given C<DateTime> | 
| 1214 |  |  |  |  |  |  | objects, else it returns C<Time::Piece> objects. | 
| 1215 |  |  |  |  |  |  |  | 
| 1216 |  |  |  |  |  |  | After running, the original time associated with the object will be retained. | 
| 1217 |  |  |  |  |  |  |  | 
| 1218 |  |  |  |  |  |  | =cut | 
| 1219 |  |  |  |  |  |  |  | 
| 1220 |  |  |  |  |  |  | sub calculate { | 
| 1221 | 1 |  |  | 1 | 1 | 256 | my $self = shift; | 
| 1222 |  |  |  |  |  |  |  | 
| 1223 | 1 |  |  |  |  | 5 | my %opt = @_; | 
| 1224 |  |  |  |  |  |  |  | 
| 1225 | 1 | 50 |  |  |  | 5 | croak "No start time specified" unless exists $opt{start}; | 
| 1226 | 1 | 50 |  |  |  | 3 | croak "No end time specified" unless exists $opt{end}; | 
| 1227 | 1 | 50 |  |  |  | 2 | croak "No time increment specified" unless exists $opt{inc}; | 
| 1228 |  |  |  |  |  |  |  | 
| 1229 |  |  |  |  |  |  | # Get the increment as an integer (DateTime::Duration or Time::Seconds) | 
| 1230 | 1 |  |  |  |  | 41 | my $inc = $opt{inc}; | 
| 1231 | 1 | 50 |  |  |  | 11 | if (UNIVERSAL::can($inc, "seconds")) { | 
| 1232 | 0 |  |  |  |  | 0 | $inc = $inc->seconds; | 
| 1233 |  |  |  |  |  |  | } | 
| 1234 | 1 | 50 |  |  |  | 9 | croak "Increment must be greater than zero" unless $inc > 0; | 
| 1235 |  |  |  |  |  |  |  | 
| 1236 |  |  |  |  |  |  | # Determine date class to use for calculations | 
| 1237 | 1 |  |  |  |  | 6 | my $dateclass = blessed( $opt{start} ); | 
| 1238 | 1 | 50 | 33 |  |  | 7 | croak "Start time must be either Time::Piece or DateTime object" | 
|  |  |  | 33 |  |  |  |  | 
| 1239 |  |  |  |  |  |  | if (!$dateclass || | 
| 1240 |  |  |  |  |  |  | ($dateclass ne "Time::Piece" && $dateclass ne 'DateTime' )); | 
| 1241 |  |  |  |  |  |  |  | 
| 1242 | 1 |  |  |  |  | 3 | my @data; | 
| 1243 |  |  |  |  |  |  |  | 
| 1244 |  |  |  |  |  |  | # Get the current datetime | 
| 1245 |  |  |  |  |  |  | my $original; | 
| 1246 | 1 |  |  |  |  | 5 | my $usenow = $self->usenow; | 
| 1247 | 1 | 50 |  |  |  | 8 | $original = $self->datetime() unless $usenow; | 
| 1248 |  |  |  |  |  |  |  | 
| 1249 |  |  |  |  |  |  | # Get a private copy of the date object for calculations | 
| 1250 |  |  |  |  |  |  | # (copy constructor) | 
| 1251 | 1 |  |  |  |  | 706 | my $current = _clone_time( $opt{start} ); | 
| 1252 |  |  |  |  |  |  |  | 
| 1253 | 1 |  |  |  |  | 73 | while ( $current->epoch <= $opt{end}->epoch ) { | 
| 1254 |  |  |  |  |  |  |  | 
| 1255 |  |  |  |  |  |  | # Hash for storing the data | 
| 1256 | 25 |  |  |  |  | 321 | my %timestep; | 
| 1257 |  |  |  |  |  |  |  | 
| 1258 |  |  |  |  |  |  | # store a copy of the time | 
| 1259 | 25 |  |  |  |  | 57 | $timestep{time} = _clone_time( $current ); | 
| 1260 |  |  |  |  |  |  |  | 
| 1261 |  |  |  |  |  |  | # Set the time in the object | 
| 1262 |  |  |  |  |  |  | # [standard problem with knowing whether we are overriding | 
| 1263 |  |  |  |  |  |  | # another setting] | 
| 1264 | 25 |  |  |  |  | 1578 | $self->datetime( $current ); | 
| 1265 |  |  |  |  |  |  |  | 
| 1266 |  |  |  |  |  |  | # Now calculate the positions | 
| 1267 | 25 |  |  |  |  | 67 | ($timestep{azimuth}, $timestep{elevation}) = $self->azel(); | 
| 1268 | 25 |  |  |  |  | 64 | $timestep{parang} = $self->pa; | 
| 1269 |  |  |  |  |  |  |  | 
| 1270 | 25 | 50 |  |  |  | 84 | if (defined $opt{units} ) { | 
| 1271 | 25 |  |  |  |  | 67 | $timestep{azimuth} = $timestep{azimuth}->in_format( $opt{units} ); | 
| 1272 | 25 |  |  |  |  | 59 | $timestep{elevation} = $timestep{elevation}->in_format( $opt{units} ); | 
| 1273 | 25 |  |  |  |  | 63 | $timestep{parang} = $timestep{parang}->in_format( $opt{units} ); | 
| 1274 |  |  |  |  |  |  | } | 
| 1275 |  |  |  |  |  |  |  | 
| 1276 | 25 |  |  |  |  | 93 | $timestep{lst} = $self->_lst(); | 
| 1277 |  |  |  |  |  |  |  | 
| 1278 |  |  |  |  |  |  | # store the timestep | 
| 1279 | 25 |  |  |  |  | 69 | push(@data, \%timestep); | 
| 1280 |  |  |  |  |  |  |  | 
| 1281 |  |  |  |  |  |  | # increment the time | 
| 1282 | 25 |  |  |  |  | 65 | $current = _inc_time( $current, $inc ); | 
| 1283 |  |  |  |  |  |  |  | 
| 1284 |  |  |  |  |  |  | } | 
| 1285 |  |  |  |  |  |  |  | 
| 1286 |  |  |  |  |  |  | # Restore the time | 
| 1287 | 1 | 50 |  |  |  | 16 | if ($usenow) { | 
| 1288 | 0 |  |  |  |  | 0 | $self->datetime( undef ); | 
| 1289 |  |  |  |  |  |  | } else { | 
| 1290 | 1 |  |  |  |  | 4 | $self->datetime( $original ); | 
| 1291 |  |  |  |  |  |  | } | 
| 1292 | 1 |  |  |  |  | 5 | $self->usenow( $usenow ); | 
| 1293 |  |  |  |  |  |  |  | 
| 1294 | 1 |  |  |  |  | 42 | return @data; | 
| 1295 |  |  |  |  |  |  |  | 
| 1296 |  |  |  |  |  |  | } | 
| 1297 |  |  |  |  |  |  |  | 
| 1298 |  |  |  |  |  |  | =item B<rise_time> | 
| 1299 |  |  |  |  |  |  |  | 
| 1300 |  |  |  |  |  |  | Time at which the target will appear above the horizon. By default the | 
| 1301 |  |  |  |  |  |  | calculation is for the next rise time relative to the current | 
| 1302 |  |  |  |  |  |  | reference time.  If the "event" key is used, this can control which | 
| 1303 |  |  |  |  |  |  | rise time will be returned. For event=1, this indicates the following | 
| 1304 |  |  |  |  |  |  | rise (the default), event=-1 indicates a previous rise and event=0 | 
| 1305 |  |  |  |  |  |  | indicates the nearest source rising to the current reference time. | 
| 1306 |  |  |  |  |  |  |  | 
| 1307 |  |  |  |  |  |  | If the "nearest" key is set in the argument hash, this is synonymous | 
| 1308 |  |  |  |  |  |  | with event=0 and supercedes the event key. | 
| 1309 |  |  |  |  |  |  |  | 
| 1310 |  |  |  |  |  |  | Returns C<undef> if the target is never visible or never sets. An | 
| 1311 |  |  |  |  |  |  | optional argument can be given specifying a different elevation to the | 
| 1312 |  |  |  |  |  |  | horizon (in radians). | 
| 1313 |  |  |  |  |  |  |  | 
| 1314 |  |  |  |  |  |  | $t = $c->set_time(); | 
| 1315 |  |  |  |  |  |  | $t = $c->set_time( horizon => $el ); | 
| 1316 |  |  |  |  |  |  | $t = $c->set_time( nearest => 1 ); | 
| 1317 |  |  |  |  |  |  | $t = $c->set_time( event => -1 ); | 
| 1318 |  |  |  |  |  |  |  | 
| 1319 |  |  |  |  |  |  | Returns a C<Time::Piece> object or a C<DateTime> object depending on the | 
| 1320 |  |  |  |  |  |  | type of object that is returned by the C<datetime> method. | 
| 1321 |  |  |  |  |  |  |  | 
| 1322 |  |  |  |  |  |  | For some occasions the calculation will be performed twice, once for | 
| 1323 |  |  |  |  |  |  | the meridian transit before the reference time and once for the | 
| 1324 |  |  |  |  |  |  | transit after the reference time. | 
| 1325 |  |  |  |  |  |  |  | 
| 1326 |  |  |  |  |  |  | Does not distinguish a source that never rises from a source that | 
| 1327 |  |  |  |  |  |  | never sets. Both will return undef for the rise time. | 
| 1328 |  |  |  |  |  |  |  | 
| 1329 |  |  |  |  |  |  | Next and previous depend on the adjacent transits. The routine will | 
| 1330 |  |  |  |  |  |  | not step forward multiple days looking for a rise time if the source is | 
| 1331 |  |  |  |  |  |  | not going to rise before the next or previous transit. | 
| 1332 |  |  |  |  |  |  |  | 
| 1333 |  |  |  |  |  |  | =cut | 
| 1334 |  |  |  |  |  |  |  | 
| 1335 |  |  |  |  |  |  | sub rise_time { | 
| 1336 | 1529 |  |  | 1529 | 1 | 13871 | my $self = shift; | 
| 1337 | 1529 |  |  |  |  | 4066 | return $self->_rise_set_time( 1, @_); | 
| 1338 |  |  |  |  |  |  | } | 
| 1339 |  |  |  |  |  |  |  | 
| 1340 |  |  |  |  |  |  | =item B<set_time> | 
| 1341 |  |  |  |  |  |  |  | 
| 1342 |  |  |  |  |  |  | Time at which the target will set below the horizon. By default the | 
| 1343 |  |  |  |  |  |  | calculation is the next set time from the current reference time. If | 
| 1344 |  |  |  |  |  |  | the "event" key is used, this can control which set time will be | 
| 1345 |  |  |  |  |  |  | returned. For event=1, this indicates the following set (the default), | 
| 1346 |  |  |  |  |  |  | event=-1 indicates a previous set and event=0 indicates the nearest | 
| 1347 |  |  |  |  |  |  | source setting to the current reference time. | 
| 1348 |  |  |  |  |  |  |  | 
| 1349 |  |  |  |  |  |  | If the "nearest" key is set in the argument hash, this is synonymous | 
| 1350 |  |  |  |  |  |  | with event=0 and supercedes the event key. | 
| 1351 |  |  |  |  |  |  |  | 
| 1352 |  |  |  |  |  |  | Returns C<undef> if the target is never visible or never sets. An | 
| 1353 |  |  |  |  |  |  | optional argument can be given specifying a different elevation to the | 
| 1354 |  |  |  |  |  |  | horizon (in radians). | 
| 1355 |  |  |  |  |  |  |  | 
| 1356 |  |  |  |  |  |  | $t = $c->set_time(); | 
| 1357 |  |  |  |  |  |  | $t = $c->set_time( horizon => $el ); | 
| 1358 |  |  |  |  |  |  | $t = $c->set_time( nearest => 1 ); | 
| 1359 |  |  |  |  |  |  | $t = $c->set_time( event => -1 ); | 
| 1360 |  |  |  |  |  |  |  | 
| 1361 |  |  |  |  |  |  | Returns a C<Time::Piece> object or a C<DateTime> object depending on the | 
| 1362 |  |  |  |  |  |  | type of object that is returned by the C<datetime> method. | 
| 1363 |  |  |  |  |  |  |  | 
| 1364 |  |  |  |  |  |  | For some occasions the calculation will be performed twice, once for | 
| 1365 |  |  |  |  |  |  | the meridian transit before the reference time and once for the | 
| 1366 |  |  |  |  |  |  | transit after the reference time. | 
| 1367 |  |  |  |  |  |  |  | 
| 1368 |  |  |  |  |  |  | Does not distinguish a source that never rises from a source that | 
| 1369 |  |  |  |  |  |  | never sets. Both will return undef for the set time. | 
| 1370 |  |  |  |  |  |  |  | 
| 1371 |  |  |  |  |  |  | Next and previous depend on the adjacent transits. The routine will | 
| 1372 |  |  |  |  |  |  | not step forward multiple days looking for a set time if the source is | 
| 1373 |  |  |  |  |  |  | not going to set following the next or previous transit. | 
| 1374 |  |  |  |  |  |  |  | 
| 1375 |  |  |  |  |  |  | =cut | 
| 1376 |  |  |  |  |  |  |  | 
| 1377 |  |  |  |  |  |  | sub set_time { | 
| 1378 | 1529 |  |  | 1529 | 1 | 16434 | my $self = shift; | 
| 1379 | 1529 |  |  |  |  | 3542 | return $self->_rise_set_time( 0, @_); | 
| 1380 |  |  |  |  |  |  | } | 
| 1381 |  |  |  |  |  |  |  | 
| 1382 |  |  |  |  |  |  |  | 
| 1383 |  |  |  |  |  |  | =item B<ha_set> | 
| 1384 |  |  |  |  |  |  |  | 
| 1385 |  |  |  |  |  |  | Hour angle at which the target will set. Negate this value to obtain | 
| 1386 |  |  |  |  |  |  | the rise time. By default assumes the target sets at an elevation of 0 | 
| 1387 |  |  |  |  |  |  | degrees (except for the Sun and Moon which are special-cased). An | 
| 1388 |  |  |  |  |  |  | optional hash can be given with key of "horizon" specifying a | 
| 1389 |  |  |  |  |  |  | different elevation (in radians). | 
| 1390 |  |  |  |  |  |  |  | 
| 1391 |  |  |  |  |  |  | $ha = $c->ha_set; | 
| 1392 |  |  |  |  |  |  | $ha = $c->ha_set( horizon => $el ); | 
| 1393 |  |  |  |  |  |  |  | 
| 1394 |  |  |  |  |  |  | Returned by default as an C<Astro::Coords::Angle::Hour> object unless | 
| 1395 |  |  |  |  |  |  | an explicit "format" is specified. | 
| 1396 |  |  |  |  |  |  |  | 
| 1397 |  |  |  |  |  |  | $ha = $c->ha_set( horizon => $el, format => 'h'); | 
| 1398 |  |  |  |  |  |  |  | 
| 1399 |  |  |  |  |  |  | There are predefined elevations for events such as | 
| 1400 |  |  |  |  |  |  | Sun rise/set and Twilight (only relevant if your object | 
| 1401 |  |  |  |  |  |  | refers to the Sun). See L<"CONSTANTS"> for more information. | 
| 1402 |  |  |  |  |  |  |  | 
| 1403 |  |  |  |  |  |  | Returns C<undef> if the target never reaches the specified horizon. | 
| 1404 |  |  |  |  |  |  | (maybe it is circumpolar). | 
| 1405 |  |  |  |  |  |  |  | 
| 1406 |  |  |  |  |  |  | For the Sun and moon this calculation will not be very accurate since | 
| 1407 |  |  |  |  |  |  | it depends on the time for which the calculation is to be performed | 
| 1408 |  |  |  |  |  |  | (the time is not used by this routine) and the rise Hour Angle and | 
| 1409 |  |  |  |  |  |  | setting Hour Angle will differ (especially for the moon) . These | 
| 1410 |  |  |  |  |  |  | effects are corrected for by the C<rise_time> and C<set_time> | 
| 1411 |  |  |  |  |  |  | methods. | 
| 1412 |  |  |  |  |  |  |  | 
| 1413 |  |  |  |  |  |  | In some cases for the Moon, an iterative technique is used to calculate | 
| 1414 |  |  |  |  |  |  | the hour angle when the Moon is near transit (the simple geometrical | 
| 1415 |  |  |  |  |  |  | arguments do not correctly calculate the transit elevation). | 
| 1416 |  |  |  |  |  |  |  | 
| 1417 |  |  |  |  |  |  | =cut | 
| 1418 |  |  |  |  |  |  |  | 
| 1419 |  |  |  |  |  |  | sub ha_set { | 
| 1420 | 3064 |  |  | 3064 | 1 | 4407 | my $self = shift; | 
| 1421 |  |  |  |  |  |  |  | 
| 1422 |  |  |  |  |  |  | # Get the reference horizon elevation | 
| 1423 | 3064 |  |  |  |  | 8142 | my %opt = @_; | 
| 1424 |  |  |  |  |  |  |  | 
| 1425 |  |  |  |  |  |  | my $horizon = (defined $opt{horizon} ? $opt{horizon} : | 
| 1426 | 3064 | 100 |  |  |  | 7857 | $self->_default_horizon ); | 
| 1427 |  |  |  |  |  |  |  | 
| 1428 |  |  |  |  |  |  | # Get the telescope position | 
| 1429 | 3064 |  |  |  |  | 6220 | my $tel = $self->telescope; | 
| 1430 |  |  |  |  |  |  |  | 
| 1431 |  |  |  |  |  |  | # Get the longitude (in radians) | 
| 1432 | 3064 | 100 |  |  |  | 9396 | my $lat = (defined $tel ? $tel->lat : 0.0 ); | 
| 1433 |  |  |  |  |  |  |  | 
| 1434 |  |  |  |  |  |  | # Declination | 
| 1435 | 3064 |  |  |  |  | 22071 | my $dec = $self->dec_app; | 
| 1436 |  |  |  |  |  |  |  | 
| 1437 |  |  |  |  |  |  | # Calculate the hour angle for this elevation | 
| 1438 |  |  |  |  |  |  | # See http://www.faqs.org/faqs/astronomy/faq/part3/section-5.html | 
| 1439 | 3064 |  |  |  |  | 18434 | my $cos_ha0 = ( sin($horizon) - sin($lat)*sin( $dec ) ) / | 
| 1440 |  |  |  |  |  |  | ( cos($lat) * cos($dec) ); | 
| 1441 |  |  |  |  |  |  |  | 
| 1442 |  |  |  |  |  |  | # Make sure we have a valid number for the cosine | 
| 1443 | 3064 | 100 | 100 |  |  | 7215 | if (defined $self->name && lc($self->name) eq 'moon' && abs($cos_ha0) > 1) { | 
|  |  |  | 100 |  |  |  |  | 
| 1444 |  |  |  |  |  |  | # for the moon this routine can incorrectly determine | 
| 1445 |  |  |  |  |  |  | # cos HA near transit [in fact it always will be inaccurate | 
| 1446 |  |  |  |  |  |  | # but near transit it won't return any value at all] | 
| 1447 |  |  |  |  |  |  | # Calculate transit elevation and if it is greater than the | 
| 1448 |  |  |  |  |  |  | # requested "horizon" use an iterative technique to find the | 
| 1449 |  |  |  |  |  |  | # set time. | 
| 1450 | 2 | 50 |  |  |  | 11 | if ($self->transit_el > $horizon) { | 
| 1451 | 2 | 50 |  |  |  | 11 | my $oldtime = ( $self->has_datetime ? $self->datetime : undef); | 
| 1452 | 2 |  |  |  |  | 13 | my $mt = $self->meridian_time(); | 
| 1453 | 2 |  |  |  |  | 11 | $self->datetime( $mt ); | 
| 1454 | 2 | 50 |  |  |  | 12 | print "# Calculating iterative set time for HA determination\n" | 
| 1455 |  |  |  |  |  |  | if $DEBUG; | 
| 1456 | 2 |  |  |  |  | 11 | my $convok = $self->_iterative_el( $horizon, -1 ); | 
| 1457 | 2 | 50 |  |  |  | 12 | return undef unless $convok; | 
| 1458 | 2 |  |  |  |  | 8 | my $seconds = $self->datetime->epoch - $mt->epoch; | 
| 1459 | 2 |  |  |  |  | 25 | $cos_ha0 = cos( $seconds * Astro::PAL::DS2R ); | 
| 1460 | 2 |  |  |  |  | 9 | $self->datetime( $oldtime ); | 
| 1461 |  |  |  |  |  |  | } | 
| 1462 |  |  |  |  |  |  | } | 
| 1463 |  |  |  |  |  |  |  | 
| 1464 | 3064 | 100 |  |  |  | 8831 | return undef if abs($cos_ha0) > 1; | 
| 1465 |  |  |  |  |  |  |  | 
| 1466 |  |  |  |  |  |  | # Work out the hour angle for this elevation | 
| 1467 | 2546 |  |  |  |  | 8180 | my $ha0 = acos( $cos_ha0 ); | 
| 1468 |  |  |  |  |  |  |  | 
| 1469 |  |  |  |  |  |  | # If we are the Sun we need to convert this to solar time | 
| 1470 |  |  |  |  |  |  | # time from sidereal time | 
| 1471 | 2546 | 100 | 100 |  |  | 19514 | $ha0 *= 365.2422/366.2422 | 
|  |  |  | 66 |  |  |  |  | 
| 1472 |  |  |  |  |  |  | unless (defined $self->name && | 
| 1473 |  |  |  |  |  |  | lc($self->name) eq 'sun' && $self->isa("Astro::Coords::Planet")); | 
| 1474 |  |  |  |  |  |  |  | 
| 1475 |  |  |  |  |  |  |  | 
| 1476 |  |  |  |  |  |  | #  print "HA 0 is $ha0\n"; | 
| 1477 |  |  |  |  |  |  | #  print "#### in hours: ". ( $ha0 * Astro::PAL::DR2S / 3600)."\n"; | 
| 1478 |  |  |  |  |  |  |  | 
| 1479 |  |  |  |  |  |  | # return the result (converting if necessary) | 
| 1480 |  |  |  |  |  |  | return Astro::Coords::Angle::Hour->new( $ha0, units => 'rad', | 
| 1481 | 2546 |  |  |  |  | 7502 | range => 'PI')->in_format($opt{format}); | 
| 1482 |  |  |  |  |  |  |  | 
| 1483 |  |  |  |  |  |  | } | 
| 1484 |  |  |  |  |  |  |  | 
| 1485 |  |  |  |  |  |  | =item B<meridian_time> | 
| 1486 |  |  |  |  |  |  |  | 
| 1487 |  |  |  |  |  |  | Calculate the meridian time for this target (the time at which | 
| 1488 |  |  |  |  |  |  | the source transits). | 
| 1489 |  |  |  |  |  |  |  | 
| 1490 |  |  |  |  |  |  | MT(UT) = apparent RA - LST(UT=0) | 
| 1491 |  |  |  |  |  |  |  | 
| 1492 |  |  |  |  |  |  | By default the next transit following the current time is calculated and | 
| 1493 |  |  |  |  |  |  | returned as a C<Time::Piece> or C<DateTime> object (depending on what | 
| 1494 |  |  |  |  |  |  | is stored in C<datetime>). | 
| 1495 |  |  |  |  |  |  |  | 
| 1496 |  |  |  |  |  |  | If you want control over which transit should be calculated this can be | 
| 1497 |  |  |  |  |  |  | specified using the "event" hash key: | 
| 1498 |  |  |  |  |  |  |  | 
| 1499 |  |  |  |  |  |  | $mt = $c->meridian_time( event => 1 ); | 
| 1500 |  |  |  |  |  |  | $mt = $c->meridian_time( event => 0 ); | 
| 1501 |  |  |  |  |  |  | $mt = $c->meridian_time( event => -1 ); | 
| 1502 |  |  |  |  |  |  |  | 
| 1503 |  |  |  |  |  |  | A value of "1" indicates the next transit following the current | 
| 1504 |  |  |  |  |  |  | reference time (this is the default behaviour for reasons of backwards | 
| 1505 |  |  |  |  |  |  | compatibility). A value of "-1" indicates that the closest transit | 
| 1506 |  |  |  |  |  |  | event before the reference time should be used. A valud of "0" | 
| 1507 |  |  |  |  |  |  | indicates that the nearest transit event should be returned. If the parameter | 
| 1508 |  |  |  |  |  |  | value is not one of the above, it will default to "1". | 
| 1509 |  |  |  |  |  |  |  | 
| 1510 |  |  |  |  |  |  | A synonym for event=>0 is provided by using the "nearest" key. If | 
| 1511 |  |  |  |  |  |  | present and true, this key overrides "event". If present and false the | 
| 1512 |  |  |  |  |  |  | "event" key is used (defaulting to "1" if event indicates "nearest"=1). | 
| 1513 |  |  |  |  |  |  |  | 
| 1514 |  |  |  |  |  |  | $mt = $c->meridian_time( nearest => 1 ); | 
| 1515 |  |  |  |  |  |  |  | 
| 1516 |  |  |  |  |  |  | =cut | 
| 1517 |  |  |  |  |  |  |  | 
| 1518 |  |  |  |  |  |  | sub meridian_time { | 
| 1519 | 2564 |  |  | 2564 | 1 | 18008 | my $self = shift; | 
| 1520 | 2564 |  |  |  |  | 6374 | my %opt = @_; | 
| 1521 |  |  |  |  |  |  |  | 
| 1522 |  |  |  |  |  |  | # extract the true value of "event" given all the backwards compatibility | 
| 1523 |  |  |  |  |  |  | # problems | 
| 1524 | 2564 |  |  |  |  | 6953 | my $event = $self->_extract_event( %opt ); | 
| 1525 |  |  |  |  |  |  |  | 
| 1526 |  |  |  |  |  |  | # Get the current time (do not modify it since we need to put it back) | 
| 1527 | 2564 | 50 |  |  |  | 5253 | my $oldtime = ( $self->has_datetime ? $self->datetime : undef); | 
| 1528 |  |  |  |  |  |  |  | 
| 1529 |  |  |  |  |  |  | # Need a reference time for the calculation | 
| 1530 | 2564 |  |  |  |  | 5237 | my $reftime = $self->datetime; | 
| 1531 |  |  |  |  |  |  |  | 
| 1532 |  |  |  |  |  |  | # For fast moving objects such as planets, we need to calculate | 
| 1533 |  |  |  |  |  |  | # the transit time iteratively since the apparent RA/Dec will change | 
| 1534 |  |  |  |  |  |  | # slightly during the night so we need to adjust the internal clock | 
| 1535 |  |  |  |  |  |  | # to get it close to the actual transit time. We also need to make sure | 
| 1536 |  |  |  |  |  |  | # that we are starting at the correct reference time so start at the | 
| 1537 |  |  |  |  |  |  | # current time and look forward until we get a transit time > than | 
| 1538 |  |  |  |  |  |  | # our start time | 
| 1539 |  |  |  |  |  |  |  | 
| 1540 |  |  |  |  |  |  | # calculate the required times | 
| 1541 | 2564 |  |  |  |  | 3550 | my $mtime; | 
| 1542 | 2564 | 100 | 100 |  |  | 7607 | if ($event == 1 || $event == -1) { | 
|  |  | 50 |  |  |  |  |  | 
| 1543 |  |  |  |  |  |  | # previous or next | 
| 1544 | 2561 |  |  |  |  | 6754 | $mtime = $self->_calc_mtime( $reftime, $event ); | 
| 1545 |  |  |  |  |  |  |  | 
| 1546 |  |  |  |  |  |  | # Reset the clock | 
| 1547 | 2561 |  |  |  |  | 7047 | $self->datetime( $oldtime ); | 
| 1548 |  |  |  |  |  |  |  | 
| 1549 |  |  |  |  |  |  | } elsif ($event == 0) { | 
| 1550 |  |  |  |  |  |  | # nearest requires both | 
| 1551 | 3 |  |  |  |  | 8 | my $prev = $self->_calc_mtime( $reftime, -1 ); | 
| 1552 | 3 |  |  |  |  | 13 | my $next = $self->_calc_mtime( $reftime,  1 ); | 
| 1553 |  |  |  |  |  |  |  | 
| 1554 |  |  |  |  |  |  | # Reset the clock (before we possibly croak) | 
| 1555 | 3 |  |  |  |  | 22 | $self->datetime( $oldtime ); | 
| 1556 |  |  |  |  |  |  |  | 
| 1557 |  |  |  |  |  |  | # calculate the diff | 
| 1558 | 3 | 50 | 33 |  |  | 46 | if (defined $prev && defined $next) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 1559 | 3 |  |  |  |  | 13 | my $prev_diff = abs( $reftime->epoch - $prev->epoch ); | 
| 1560 | 3 |  |  |  |  | 34 | my $next_diff = abs( $reftime->epoch - $next->epoch ); | 
| 1561 |  |  |  |  |  |  |  | 
| 1562 | 3 | 50 |  |  |  | 31 | if ($prev_diff < $next_diff) { | 
| 1563 | 3 |  |  |  |  | 11 | $mtime = $prev; | 
| 1564 |  |  |  |  |  |  | } else { | 
| 1565 | 0 |  |  |  |  | 0 | $mtime = $next; | 
| 1566 |  |  |  |  |  |  | } | 
| 1567 |  |  |  |  |  |  |  | 
| 1568 |  |  |  |  |  |  | } elsif (defined $prev) { | 
| 1569 | 0 |  |  |  |  | 0 | $mtime = $prev; | 
| 1570 |  |  |  |  |  |  | } elsif (defined $next) { | 
| 1571 | 0 |  |  |  |  | 0 | $mtime = $next; | 
| 1572 |  |  |  |  |  |  | } else { | 
| 1573 | 0 |  |  |  |  | 0 | croak "Should not occur in meridian_time\n"; | 
| 1574 |  |  |  |  |  |  | } | 
| 1575 |  |  |  |  |  |  | } else { | 
| 1576 | 0 |  |  |  |  | 0 | croak "Unrecognized value for event: $event\n"; | 
| 1577 |  |  |  |  |  |  | } | 
| 1578 |  |  |  |  |  |  |  | 
| 1579 | 2564 |  |  |  |  | 7101 | return $mtime; | 
| 1580 |  |  |  |  |  |  | } | 
| 1581 |  |  |  |  |  |  |  | 
| 1582 |  |  |  |  |  |  | sub _calc_mtime { | 
| 1583 | 1075 |  |  | 1075 |  | 1671 | my $self = shift; | 
| 1584 | 1075 |  |  |  |  | 2377 | my ($reftime, $event ) = @_; | 
| 1585 |  |  |  |  |  |  |  | 
| 1586 |  |  |  |  |  |  | # event must be 1 or -1 | 
| 1587 | 1075 | 50 | 66 |  |  | 4352 | if (!defined $event || ($event != 1 && $event != -1)) { | 
|  |  |  | 33 |  |  |  |  | 
| 1588 | 0 |  |  |  |  | 0 | croak "Event must be either +1 or -1"; | 
| 1589 |  |  |  |  |  |  | } | 
| 1590 |  |  |  |  |  |  |  | 
| 1591 |  |  |  |  |  |  | # do we have DateTime objects | 
| 1592 | 1075 |  |  |  |  | 2055 | my $dtime = $self->_isdt(); | 
| 1593 |  |  |  |  |  |  |  | 
| 1594 |  |  |  |  |  |  | # Somewhere to store the previous time so we can make sure things | 
| 1595 |  |  |  |  |  |  | # are iterating nicely | 
| 1596 | 1075 |  |  |  |  | 1772 | my $prevtime; | 
| 1597 |  |  |  |  |  |  |  | 
| 1598 |  |  |  |  |  |  | # The current best guess of the meridian time | 
| 1599 |  |  |  |  |  |  | my $mtime; | 
| 1600 |  |  |  |  |  |  |  | 
| 1601 |  |  |  |  |  |  | # Number of times we want to loop before aborting | 
| 1602 | 1075 |  |  |  |  | 1433 | my $max = 10; | 
| 1603 |  |  |  |  |  |  |  | 
| 1604 |  |  |  |  |  |  | # Tolerance for good convergence | 
| 1605 | 1075 |  |  |  |  | 1530 | my $tol = 1; | 
| 1606 |  |  |  |  |  |  |  | 
| 1607 |  |  |  |  |  |  | # Increment (in hours) to jump forward each loop | 
| 1608 |  |  |  |  |  |  | # Need to make sure we lock onto the correct transit so I'm | 
| 1609 |  |  |  |  |  |  | # wary of jumping forward by exactly 24 hours | 
| 1610 | 1075 |  |  |  |  | 1592 | my $inc = 12 * $event; | 
| 1611 | 1075 | 100 | 66 |  |  | 2877 | $inc /= 2 if (defined $self->name && lc($self->name) eq 'moon'); | 
| 1612 |  |  |  |  |  |  |  | 
| 1613 |  |  |  |  |  |  | # Loop until mtime is greater than the reftime | 
| 1614 |  |  |  |  |  |  | # and (mtime - prevtime) is smaller than a second | 
| 1615 |  |  |  |  |  |  | # and we have not looped more than $max times | 
| 1616 |  |  |  |  |  |  | # There is probably an analytical solution. The problem is that | 
| 1617 |  |  |  |  |  |  | # the apparent RA depends on the current time yet the apparent RA | 
| 1618 |  |  |  |  |  |  | # varies with time | 
| 1619 | 1075 |  |  |  |  | 1948 | my $count = 0; | 
| 1620 | 1075 | 50 |  |  |  | 2211 | print "Looping..............".$reftime->datetime."\n" if $DEBUG; | 
| 1621 | 1075 |  |  |  |  | 2273 | while ( $count <= $max ) { | 
| 1622 | 3978 |  |  |  |  | 22749 | $count++; | 
| 1623 |  |  |  |  |  |  |  | 
| 1624 | 3978 | 100 |  |  |  | 7619 | if (defined $mtime) { | 
| 1625 | 2903 |  |  |  |  | 6128 | $prevtime = _clone_time( $mtime ); | 
| 1626 | 2903 |  |  |  |  | 40319 | $self->datetime( $mtime ); | 
| 1627 |  |  |  |  |  |  | } | 
| 1628 | 3978 |  |  |  |  | 8736 | $mtime = $self->_local_mtcalc(); | 
| 1629 | 3978 | 50 |  |  |  | 3561707 | print "New meridian time: ".$mtime->datetime ."\n" if $DEBUG; | 
| 1630 |  |  |  |  |  |  |  | 
| 1631 |  |  |  |  |  |  | # make sure we are going the correct way | 
| 1632 |  |  |  |  |  |  | # since we are enforced to only find a transit in the direction | 
| 1633 |  |  |  |  |  |  | # governed by "event" | 
| 1634 |  |  |  |  |  |  |  | 
| 1635 |  |  |  |  |  |  | # Calculate the difference in epoch seconds before the current | 
| 1636 |  |  |  |  |  |  | # object reference time and the calculate transit time. | 
| 1637 |  |  |  |  |  |  | # Use ->epoch rather than overload since I'm having problems | 
| 1638 |  |  |  |  |  |  | # with Duration objects | 
| 1639 | 3978 |  |  |  |  | 11266 | my $diff = $reftime->epoch - $mtime->epoch; | 
| 1640 | 3978 |  |  |  |  | 43836 | $diff *= $event; # make the comparison correct sense | 
| 1641 | 3978 | 100 |  |  |  | 9091 | if ($diff > 0) { | 
| 1642 | 1048 | 50 |  |  |  | 2547 | print "Need to offset....[diff = $diff sec]\n" if $DEBUG; | 
| 1643 |  |  |  |  |  |  | # this is an earlier transit time | 
| 1644 |  |  |  |  |  |  | # Need to keep jumping forward until we lock on to a meridian | 
| 1645 |  |  |  |  |  |  | # time that ismore recent than the ref time | 
| 1646 | 1048 | 100 |  |  |  | 2364 | if ($dtime) { | 
| 1647 | 1023 |  |  |  |  | 2847 | $mtime->add( hours => ($count * $inc)); | 
| 1648 |  |  |  |  |  |  | } else { | 
| 1649 | 25 |  |  |  |  | 72 | $mtime = $mtime + ($count * $inc * Time::Seconds::ONE_HOUR); | 
| 1650 |  |  |  |  |  |  | } | 
| 1651 |  |  |  |  |  |  | } | 
| 1652 |  |  |  |  |  |  |  | 
| 1653 |  |  |  |  |  |  | # End loop if the difference between meridian time and calculated | 
| 1654 |  |  |  |  |  |  | # previous time is less than the acceptable tolerance | 
| 1655 | 3978 | 100 | 66 |  |  | 887080 | if (defined $prevtime && defined $mtime) { | 
| 1656 | 2903 | 100 |  |  |  | 6877 | last if (abs($mtime->epoch - $prevtime->epoch) <= $tol); | 
| 1657 |  |  |  |  |  |  | } | 
| 1658 |  |  |  |  |  |  | } | 
| 1659 |  |  |  |  |  |  |  | 
| 1660 |  |  |  |  |  |  | # warn if we did not converge | 
| 1661 | 1075 | 50 |  |  |  | 10388 | warnings::warnif "Meridian time calculation failed to converge" | 
| 1662 |  |  |  |  |  |  | if $count > $max; | 
| 1663 |  |  |  |  |  |  |  | 
| 1664 |  |  |  |  |  |  | # return the time | 
| 1665 | 1075 |  |  |  |  | 3739 | return $mtime; | 
| 1666 |  |  |  |  |  |  | } | 
| 1667 |  |  |  |  |  |  |  | 
| 1668 |  |  |  |  |  |  | # Returns true if | 
| 1669 |  |  |  |  |  |  | #    time - reftime is negative | 
| 1670 |  |  |  |  |  |  |  | 
| 1671 |  |  |  |  |  |  | # Returns RA-LST added on to reference time | 
| 1672 |  |  |  |  |  |  | sub _local_mtcalc { | 
| 1673 | 5470 |  |  | 5470 |  | 7586 | my $self = shift; | 
| 1674 |  |  |  |  |  |  |  | 
| 1675 |  |  |  |  |  |  | # Now calculate the offset from the RA of the source. | 
| 1676 |  |  |  |  |  |  | # Note that RA should be apparent RA and so the time should | 
| 1677 |  |  |  |  |  |  | # match the actual time stored in the object. | 
| 1678 |  |  |  |  |  |  | # Make sure the LST and Apparent RA are -PI to +PI | 
| 1679 |  |  |  |  |  |  | # so that we do not jump whole days | 
| 1680 | 5470 |  |  |  |  | 10916 | my $lst = Astro::PAL::palDrange($self->_lst); | 
| 1681 | 5470 |  |  |  |  | 12952 | my $ra_app = Astro::PAL::palDrange( $self->ra_app ); | 
| 1682 | 5470 |  |  |  |  | 15806 | my $offset = $ra_app - $lst; | 
| 1683 |  |  |  |  |  |  |  | 
| 1684 |  |  |  |  |  |  | # This is in radians. Need to convert it to seconds | 
| 1685 | 5470 |  |  |  |  | 8681 | my $offset_sec = $offset * Astro::PAL::DR2S; | 
| 1686 |  |  |  |  |  |  |  | 
| 1687 |  |  |  |  |  |  | #  print "LST:            $lst\n"; | 
| 1688 |  |  |  |  |  |  | #  print "RA App:         ". $self->ra_app ."\n"; | 
| 1689 |  |  |  |  |  |  | #  print "Offset radians: $offset\n"; | 
| 1690 |  |  |  |  |  |  | #  print "Offset seconds: $offset_sec\n"; | 
| 1691 |  |  |  |  |  |  |  | 
| 1692 |  |  |  |  |  |  | # If we are not the Sun we need to convert this to sidereal | 
| 1693 |  |  |  |  |  |  | # time from solar time | 
| 1694 | 5470 | 100 | 100 |  |  | 12219 | $offset_sec *= 365.2422/366.2422 | 
|  |  |  | 66 |  |  |  |  | 
| 1695 |  |  |  |  |  |  | unless (defined $self->name && | 
| 1696 |  |  |  |  |  |  | lc($self->name) eq 'sun' && $self->isa("Astro::Coords::Planet")); | 
| 1697 |  |  |  |  |  |  |  | 
| 1698 | 5470 |  |  |  |  | 13339 | my $datetime = $self->datetime; | 
| 1699 | 5470 | 100 |  |  |  | 12269 | if ($self->_isdt()) { | 
| 1700 | 5370 |  |  |  |  | 13937 | return $datetime->clone->add( seconds => $offset_sec ); | 
| 1701 |  |  |  |  |  |  | } else { | 
| 1702 | 100 |  |  |  |  | 309 | return ($datetime + $offset_sec); | 
| 1703 |  |  |  |  |  |  | } | 
| 1704 |  |  |  |  |  |  |  | 
| 1705 |  |  |  |  |  |  | #  return $mtime; | 
| 1706 |  |  |  |  |  |  | } | 
| 1707 |  |  |  |  |  |  |  | 
| 1708 |  |  |  |  |  |  | =item B<transit_el> | 
| 1709 |  |  |  |  |  |  |  | 
| 1710 |  |  |  |  |  |  | Elevation at transit. This is just the elevation at Hour Angle = 0.0. | 
| 1711 |  |  |  |  |  |  | (ie at C<meridian_time>). | 
| 1712 |  |  |  |  |  |  |  | 
| 1713 |  |  |  |  |  |  | Format is supported as for the C<el> method. See L<"NOTES"> for | 
| 1714 |  |  |  |  |  |  | details on the supported format specifiers and default calling | 
| 1715 |  |  |  |  |  |  | convention. | 
| 1716 |  |  |  |  |  |  |  | 
| 1717 |  |  |  |  |  |  | $el = $c->transit_el( format => 'deg' ); | 
| 1718 |  |  |  |  |  |  |  | 
| 1719 |  |  |  |  |  |  | =cut | 
| 1720 |  |  |  |  |  |  |  | 
| 1721 |  |  |  |  |  |  | sub transit_el { | 
| 1722 | 8 |  |  | 8 | 1 | 30 | my $self = shift; | 
| 1723 |  |  |  |  |  |  |  | 
| 1724 |  |  |  |  |  |  | # Get meridian time | 
| 1725 | 8 |  |  |  |  | 40 | my $mtime = $self->meridian_time(); | 
| 1726 |  |  |  |  |  |  |  | 
| 1727 |  |  |  |  |  |  | # Cache the current time if required | 
| 1728 |  |  |  |  |  |  | # Note that we can leave $cache as undef if there is no | 
| 1729 |  |  |  |  |  |  | # real time. | 
| 1730 | 8 |  |  |  |  | 24 | my $cache; | 
| 1731 | 8 | 50 |  |  |  | 27 | $cache = $self->datetime if $self->has_datetime; | 
| 1732 |  |  |  |  |  |  |  | 
| 1733 |  |  |  |  |  |  | # set the new time | 
| 1734 | 8 |  |  |  |  | 57 | $self->datetime( $mtime ); | 
| 1735 |  |  |  |  |  |  |  | 
| 1736 |  |  |  |  |  |  | # calculate the elevation | 
| 1737 | 8 |  |  |  |  | 61 | my $el = $self->el( @_ ); | 
| 1738 |  |  |  |  |  |  |  | 
| 1739 |  |  |  |  |  |  | # fix the time back to what it was (including an undef value | 
| 1740 |  |  |  |  |  |  | # if we did not read the cache). | 
| 1741 | 8 |  |  |  |  | 49 | $self->datetime( $cache ); | 
| 1742 |  |  |  |  |  |  |  | 
| 1743 | 8 |  |  |  |  | 50 | return $el; | 
| 1744 |  |  |  |  |  |  | } | 
| 1745 |  |  |  |  |  |  |  | 
| 1746 |  |  |  |  |  |  | =item B<apply_offset> | 
| 1747 |  |  |  |  |  |  |  | 
| 1748 |  |  |  |  |  |  | Applies the offsets of an C<Astro::Coords::Offset> object. | 
| 1749 |  |  |  |  |  |  |  | 
| 1750 |  |  |  |  |  |  | my $coords_offset = $coords->apply_offset($offset); | 
| 1751 |  |  |  |  |  |  |  | 
| 1752 |  |  |  |  |  |  | The current implementation works by calling C<radec2000> or C<glonglat> | 
| 1753 |  |  |  |  |  |  | on the original object and will return a new C<Astro::Coords::Equatorial> | 
| 1754 |  |  |  |  |  |  | object. | 
| 1755 |  |  |  |  |  |  |  | 
| 1756 |  |  |  |  |  |  | =cut | 
| 1757 |  |  |  |  |  |  |  | 
| 1758 |  |  |  |  |  |  | sub apply_offset { | 
| 1759 | 7 |  |  | 7 | 1 | 23 | my $self   = shift; | 
| 1760 | 7 |  |  |  |  | 10 | my $offset = shift; | 
| 1761 |  |  |  |  |  |  |  | 
| 1762 |  |  |  |  |  |  | # Check that the current implementation can handle the situation. | 
| 1763 |  |  |  |  |  |  |  | 
| 1764 | 7 | 50 |  |  |  | 25 | croak 'apply_offet: argument should be an Astro::Coords::Offset object' | 
| 1765 |  |  |  |  |  |  | unless UNIVERSAL::isa($offset, 'Astro::Coords::Offset'); | 
| 1766 |  |  |  |  |  |  |  | 
| 1767 | 7 | 50 |  |  |  | 19 | croak 'apply_offset: can only use TAN projection' | 
| 1768 |  |  |  |  |  |  | unless $offset->projection() eq 'TAN'; | 
| 1769 |  |  |  |  |  |  |  | 
| 1770 | 7 |  |  |  |  | 12 | my $coordsoffset = undef; | 
| 1771 |  |  |  |  |  |  |  | 
| 1772 | 7 | 100 |  |  |  | 16 | if ($offset->system() eq 'J2000') { | 
|  |  | 50 |  |  |  |  |  | 
| 1773 |  |  |  |  |  |  | # Apply offsets to J2000 coordinates and create a new object from them. | 
| 1774 |  |  |  |  |  |  |  | 
| 1775 | 5 |  |  |  |  | 14 | my ($dra, $ddec) = map {$_->radians()} $offset->offsets_rotated(); | 
|  | 10 |  |  |  |  | 21 |  | 
| 1776 |  |  |  |  |  |  |  | 
| 1777 |  |  |  |  |  |  | my ($ra, $dec) = Astro::PAL::palDtp2s($dra, $ddec, | 
| 1778 | 5 |  |  |  |  | 27 | map {$_->radians()} $self->radec2000()); | 
|  | 10 |  |  |  |  | 54 |  | 
| 1779 |  |  |  |  |  |  |  | 
| 1780 | 5 |  |  |  |  | 22 | $coordsoffset = new Astro::Coords(type  => 'J2000', | 
| 1781 |  |  |  |  |  |  | ra    => $ra, | 
| 1782 |  |  |  |  |  |  | dec   => $dec, | 
| 1783 |  |  |  |  |  |  | units => 'radians'); | 
| 1784 |  |  |  |  |  |  | } | 
| 1785 |  |  |  |  |  |  | elsif ($offset->system() eq 'GAL') { | 
| 1786 |  |  |  |  |  |  | # Apply offsets to galactic coordinates and create a new object from them. | 
| 1787 |  |  |  |  |  |  |  | 
| 1788 | 2 |  |  |  |  | 7 | my ($dlon, $dlat) = map {$_->radians()} $offset->offsets_rotated(); | 
|  | 4 |  |  |  |  | 9 |  | 
| 1789 |  |  |  |  |  |  |  | 
| 1790 |  |  |  |  |  |  | my ($lon, $lat) = Astro::PAL::palDtp2s($dlon, $dlat, | 
| 1791 | 2 |  |  |  |  | 13 | map {$_->radians()} $self->glonglat()); | 
|  | 4 |  |  |  |  | 20 |  | 
| 1792 |  |  |  |  |  |  |  | 
| 1793 | 2 |  |  |  |  | 10 | $coordsoffset = new Astro::Coords(type  => 'GAL', | 
| 1794 |  |  |  |  |  |  | long  => $lon, | 
| 1795 |  |  |  |  |  |  | lat   => $lat, | 
| 1796 |  |  |  |  |  |  | units => 'radians'); | 
| 1797 |  |  |  |  |  |  | } | 
| 1798 |  |  |  |  |  |  | else { | 
| 1799 | 0 |  |  |  |  | 0 | croak 'apply_offset: can only use J2000 or GAL system'; | 
| 1800 |  |  |  |  |  |  | } | 
| 1801 |  |  |  |  |  |  |  | 
| 1802 | 7 | 50 |  |  |  | 19 | croak 'apply_offset: failed to create object' unless defined $coordsoffset; | 
| 1803 |  |  |  |  |  |  |  | 
| 1804 |  |  |  |  |  |  | # Copy across additional data. | 
| 1805 |  |  |  |  |  |  |  | 
| 1806 | 7 |  |  |  |  | 18 | foreach my $method (qw/name telescope datetime comment/) { | 
| 1807 | 28 |  |  |  |  | 82 | my $value = $self->$method(); | 
| 1808 | 28 | 100 |  |  |  | 1997 | next unless defined $value; | 
| 1809 | 20 | 100 | 66 |  |  | 58 | next if $method eq 'comment' && $value eq ''; | 
| 1810 | 13 |  |  |  |  | 30 | $coordsoffset->$method($value); | 
| 1811 |  |  |  |  |  |  | } | 
| 1812 |  |  |  |  |  |  |  | 
| 1813 | 7 |  |  |  |  | 52 | return $coordsoffset; | 
| 1814 |  |  |  |  |  |  | } | 
| 1815 |  |  |  |  |  |  |  | 
| 1816 |  |  |  |  |  |  | =back | 
| 1817 |  |  |  |  |  |  |  | 
| 1818 |  |  |  |  |  |  | =head2 Velocities | 
| 1819 |  |  |  |  |  |  |  | 
| 1820 |  |  |  |  |  |  | This sections describes the available methods for determining the velocities | 
| 1821 |  |  |  |  |  |  | of each of the standard velocity frames in the direction of the reference | 
| 1822 |  |  |  |  |  |  | target relative to the current observer position and reference time. | 
| 1823 |  |  |  |  |  |  |  | 
| 1824 |  |  |  |  |  |  | =over 4 | 
| 1825 |  |  |  |  |  |  |  | 
| 1826 |  |  |  |  |  |  | =item B<rv> | 
| 1827 |  |  |  |  |  |  |  | 
| 1828 |  |  |  |  |  |  | Return the radial velocity of the target (not the observer) in km/s. | 
| 1829 |  |  |  |  |  |  | This will be used for parallax corrections (if relevant) and for | 
| 1830 |  |  |  |  |  |  | calculating the doppler correction factor. | 
| 1831 |  |  |  |  |  |  |  | 
| 1832 |  |  |  |  |  |  | $rv = $c->rv(); | 
| 1833 |  |  |  |  |  |  |  | 
| 1834 |  |  |  |  |  |  | If the velocity was originally specified as a redshift it will be | 
| 1835 |  |  |  |  |  |  | returned here as optical velocity (and may not be a physical value). | 
| 1836 |  |  |  |  |  |  |  | 
| 1837 |  |  |  |  |  |  | If no radial velocity has been specified, returns 0 km/s. | 
| 1838 |  |  |  |  |  |  |  | 
| 1839 |  |  |  |  |  |  | =cut | 
| 1840 |  |  |  |  |  |  |  | 
| 1841 |  |  |  |  |  |  | sub rv { | 
| 1842 | 10 |  |  | 10 | 1 | 17 | my $self = shift; | 
| 1843 | 10 | 100 |  |  |  | 40 | return (defined $self->{RadialVelocity} ? $self->{RadialVelocity} : 0 ); | 
| 1844 |  |  |  |  |  |  | } | 
| 1845 |  |  |  |  |  |  |  | 
| 1846 |  |  |  |  |  |  | # internal set routine | 
| 1847 |  |  |  |  |  |  | sub _set_rv { | 
| 1848 | 3 |  |  | 3 |  | 6 | my $self = shift; | 
| 1849 | 3 |  |  |  |  | 7 | $self->{RadialVelocity} = shift; | 
| 1850 |  |  |  |  |  |  | } | 
| 1851 |  |  |  |  |  |  |  | 
| 1852 |  |  |  |  |  |  | =item B<redshift> | 
| 1853 |  |  |  |  |  |  |  | 
| 1854 |  |  |  |  |  |  | Redshift is defined as the optical velocity as a fraction of the speed of light: | 
| 1855 |  |  |  |  |  |  |  | 
| 1856 |  |  |  |  |  |  | v(opt) = c z | 
| 1857 |  |  |  |  |  |  |  | 
| 1858 |  |  |  |  |  |  | Returns the reshift if the velocity definition is optical. If the | 
| 1859 |  |  |  |  |  |  | velocity definition is radio, redshift can only be calculated for | 
| 1860 |  |  |  |  |  |  | small radio velocities.  An attempt is made to calculate redshift from | 
| 1861 |  |  |  |  |  |  | radio velocity using | 
| 1862 |  |  |  |  |  |  |  | 
| 1863 |  |  |  |  |  |  | v(opt) = v(radio) / ( 1 - v(radio) / c ) | 
| 1864 |  |  |  |  |  |  |  | 
| 1865 |  |  |  |  |  |  | but only if v(radio)/c is small. Else returns undef. | 
| 1866 |  |  |  |  |  |  |  | 
| 1867 |  |  |  |  |  |  | =cut | 
| 1868 |  |  |  |  |  |  |  | 
| 1869 |  |  |  |  |  |  | sub redshift { | 
| 1870 | 2 |  |  | 2 | 1 | 8 | my $self = shift; | 
| 1871 | 2 |  |  |  |  | 5 | my $vd = $self->vdefn; | 
| 1872 | 2 | 50 | 33 |  |  | 16 | if ($vd eq 'REDSHIFT' || $vd eq 'OPTICAL') { | 
|  |  | 0 |  |  |  |  |  | 
| 1873 | 2 |  |  |  |  | 8 | return ( $self->rv / CLIGHT ); | 
| 1874 |  |  |  |  |  |  | } elsif ($vd eq 'RELATIVISTIC') { | 
| 1875 |  |  |  |  |  |  | # need to add | 
| 1876 | 0 |  |  |  |  | 0 | return undef; | 
| 1877 |  |  |  |  |  |  | } else { | 
| 1878 | 0 |  |  |  |  | 0 | my $rv = $self->rv; | 
| 1879 |  |  |  |  |  |  | # 1% of light speed | 
| 1880 | 0 | 0 |  |  |  | 0 | if ( $rv > ( 0.01 * CLIGHT) ) { | 
| 1881 | 0 |  |  |  |  | 0 | my $vopt = $rv / ( 1 - ( $rv / CLIGHT ) ); | 
| 1882 | 0 |  |  |  |  | 0 | return ( $vopt / CLIGHT ); | 
| 1883 |  |  |  |  |  |  | } else { | 
| 1884 | 0 |  |  |  |  | 0 | return undef; | 
| 1885 |  |  |  |  |  |  | } | 
| 1886 |  |  |  |  |  |  | } | 
| 1887 |  |  |  |  |  |  | } | 
| 1888 |  |  |  |  |  |  |  | 
| 1889 |  |  |  |  |  |  | # internal set routine | 
| 1890 |  |  |  |  |  |  | sub _set_redshift { | 
| 1891 | 1 |  |  | 1 |  | 3 | my $self = shift; | 
| 1892 | 1 |  |  |  |  | 2 | my $z = shift; | 
| 1893 | 1 | 50 |  |  |  | 2 | $z = 0 unless defined $z; | 
| 1894 | 1 |  |  |  |  | 12 | $self->_set_rv( CLIGHT * $z ); | 
| 1895 | 1 |  |  |  |  | 5 | $self->_set_vdefn( 'REDSHIFT' ); | 
| 1896 | 1 |  |  |  |  | 5 | $self->_set_vframe( 'HEL' ); | 
| 1897 |  |  |  |  |  |  | } | 
| 1898 |  |  |  |  |  |  |  | 
| 1899 |  |  |  |  |  |  | =item B<vdefn> | 
| 1900 |  |  |  |  |  |  |  | 
| 1901 |  |  |  |  |  |  | The velocity definition used to specify the target radial velocity. | 
| 1902 |  |  |  |  |  |  | This is a readonly parameter set at object creation (depending on | 
| 1903 |  |  |  |  |  |  | subclass) and can be one of RADIO, OPTICAL, RELATIVISTIC or REDSHIFT | 
| 1904 |  |  |  |  |  |  | (which is really optical but specified in a different way). | 
| 1905 |  |  |  |  |  |  |  | 
| 1906 |  |  |  |  |  |  | $vdefn = $c->vdefn(); | 
| 1907 |  |  |  |  |  |  |  | 
| 1908 |  |  |  |  |  |  | Required for calculating the doppler correction. Defaults to 'OPTICAL'. | 
| 1909 |  |  |  |  |  |  |  | 
| 1910 |  |  |  |  |  |  | =cut | 
| 1911 |  |  |  |  |  |  |  | 
| 1912 |  |  |  |  |  |  | sub vdefn { | 
| 1913 | 9 |  |  | 9 | 1 | 18 | my $self = shift; | 
| 1914 | 9 | 50 |  |  |  | 30 | return (defined $self->{VelocityDefinition} ? $self->{VelocityDefinition} : 'OPTICAL' ); | 
| 1915 |  |  |  |  |  |  | } | 
| 1916 |  |  |  |  |  |  |  | 
| 1917 |  |  |  |  |  |  | # internal set routine | 
| 1918 |  |  |  |  |  |  | sub _set_vdefn { | 
| 1919 | 2 |  |  | 2 |  | 2 | my $self = shift; | 
| 1920 | 2 |  |  |  |  | 4 | my $defn = shift; | 
| 1921 |  |  |  |  |  |  | # undef resets to default | 
| 1922 | 2 | 50 |  |  |  | 5 | if (defined $defn) { | 
| 1923 | 2 |  |  |  |  | 18 | $defn = $self->_normalise_vdefn( $defn ); | 
| 1924 |  |  |  |  |  |  | } | 
| 1925 | 2 |  |  |  |  | 7 | $self->{VelocityDefinition} = $defn; | 
| 1926 |  |  |  |  |  |  | } | 
| 1927 |  |  |  |  |  |  |  | 
| 1928 |  |  |  |  |  |  | =item B<vframe> | 
| 1929 |  |  |  |  |  |  |  | 
| 1930 |  |  |  |  |  |  | The velocity frame used to specify the radial velocity. This attribute | 
| 1931 |  |  |  |  |  |  | is readonly and set during object construction. Abbreviations are used | 
| 1932 |  |  |  |  |  |  | for the first 3 characters of the standard frames (4 to distinguish | 
| 1933 |  |  |  |  |  |  | LSRK from LSRD): | 
| 1934 |  |  |  |  |  |  |  | 
| 1935 |  |  |  |  |  |  | HEL  - Heliocentric (the Sun) | 
| 1936 |  |  |  |  |  |  | BAR  - Barycentric (the Solar System barycentre) | 
| 1937 |  |  |  |  |  |  | GEO  - Geocentric (Centre of the Earth) | 
| 1938 |  |  |  |  |  |  | TOP  - Topocentric (Surface of the Earth) | 
| 1939 |  |  |  |  |  |  | LSR  - Kinematical Local Standard of Rest | 
| 1940 |  |  |  |  |  |  | LSRK - As for LSR | 
| 1941 |  |  |  |  |  |  | LSRD - Dynamical Local Standard of Rest | 
| 1942 |  |  |  |  |  |  |  | 
| 1943 |  |  |  |  |  |  | The usual definition for star catalogues is Heliocentric. Default is | 
| 1944 |  |  |  |  |  |  | Heliocentric. | 
| 1945 |  |  |  |  |  |  |  | 
| 1946 |  |  |  |  |  |  | =cut | 
| 1947 |  |  |  |  |  |  |  | 
| 1948 |  |  |  |  |  |  | sub vframe { | 
| 1949 | 11 |  |  | 11 | 1 | 21 | my $self = shift; | 
| 1950 | 11 | 100 |  |  |  | 46 | return (defined $self->{VelocityFrame} ? $self->{VelocityFrame} : 'HEL' ); | 
| 1951 |  |  |  |  |  |  | } | 
| 1952 |  |  |  |  |  |  |  | 
| 1953 |  |  |  |  |  |  | # internal set routine | 
| 1954 |  |  |  |  |  |  | sub _set_vframe { | 
| 1955 | 2 |  |  | 2 |  | 2 | my $self = shift; | 
| 1956 | 2 |  |  |  |  | 4 | my $frame = shift; | 
| 1957 | 2 | 50 |  |  |  | 5 | if (defined $frame) { | 
| 1958 |  |  |  |  |  |  | # undef resets to default | 
| 1959 | 2 |  |  |  |  | 4 | $frame = $self->_normalise_vframe( $frame ); | 
| 1960 |  |  |  |  |  |  | } | 
| 1961 | 2 |  |  |  |  | 6 | $self->{VelocityFrame} = $frame; | 
| 1962 |  |  |  |  |  |  | } | 
| 1963 |  |  |  |  |  |  |  | 
| 1964 |  |  |  |  |  |  | =item B<obsvel> | 
| 1965 |  |  |  |  |  |  |  | 
| 1966 |  |  |  |  |  |  | Calculates the observed velocity of the target as seen from the | 
| 1967 |  |  |  |  |  |  | observer's location. Includes both the observer velocity and target | 
| 1968 |  |  |  |  |  |  | velocity. | 
| 1969 |  |  |  |  |  |  |  | 
| 1970 |  |  |  |  |  |  | $rv = $c->obsvel; | 
| 1971 |  |  |  |  |  |  |  | 
| 1972 |  |  |  |  |  |  | Note that the source velocity and observer velocity are simply added | 
| 1973 |  |  |  |  |  |  | without any regard for relativistic effects for high redshift sources. | 
| 1974 |  |  |  |  |  |  |  | 
| 1975 |  |  |  |  |  |  | =cut | 
| 1976 |  |  |  |  |  |  |  | 
| 1977 |  |  |  |  |  |  | sub obsvel { | 
| 1978 | 3 |  |  | 3 | 1 | 7 | my $self = shift; | 
| 1979 | 3 |  |  |  |  | 8 | my $vdefn = $self->vdefn; | 
| 1980 | 3 |  |  |  |  | 6 | my $vframe = $self->vframe; | 
| 1981 | 3 |  |  |  |  | 6 | my $rv = $self->rv; | 
| 1982 |  |  |  |  |  |  |  | 
| 1983 |  |  |  |  |  |  | # Now we need to calculate the observer velocity in the | 
| 1984 |  |  |  |  |  |  | # target frame | 
| 1985 | 3 |  |  |  |  | 8 | my $vobs = $self->vdiff( '', 'TOP' ); | 
| 1986 |  |  |  |  |  |  |  | 
| 1987 |  |  |  |  |  |  | # Total velocity between observer and target | 
| 1988 | 3 |  |  |  |  | 6 | my $vtotal = $vobs + $rv; | 
| 1989 |  |  |  |  |  |  |  | 
| 1990 | 3 |  |  |  |  | 9 | return $vtotal; | 
| 1991 |  |  |  |  |  |  | } | 
| 1992 |  |  |  |  |  |  |  | 
| 1993 |  |  |  |  |  |  | =item B<doppler> | 
| 1994 |  |  |  |  |  |  |  | 
| 1995 |  |  |  |  |  |  | Calculates the doppler factor required to correct a rest frequency to | 
| 1996 |  |  |  |  |  |  | an observed frequency. This correction is calculated for the observer | 
| 1997 |  |  |  |  |  |  | location and specified date and uses the velocity definition provided | 
| 1998 |  |  |  |  |  |  | to the object constructor. Both the observer radial velocity, and the | 
| 1999 |  |  |  |  |  |  | target radial velocity are taken into account (see the C<obsvel> | 
| 2000 |  |  |  |  |  |  | method). | 
| 2001 |  |  |  |  |  |  |  | 
| 2002 |  |  |  |  |  |  | $dopp = $c->doppler; | 
| 2003 |  |  |  |  |  |  |  | 
| 2004 |  |  |  |  |  |  | Default definitions and frames will be used if none were specified. | 
| 2005 |  |  |  |  |  |  |  | 
| 2006 |  |  |  |  |  |  | The doppler factors (defined as  frequency/rest frequency or | 
| 2007 |  |  |  |  |  |  | rest wavelength / wavelength) are calculated as follows: | 
| 2008 |  |  |  |  |  |  |  | 
| 2009 |  |  |  |  |  |  | RADIO:    1 - v / c | 
| 2010 |  |  |  |  |  |  |  | 
| 2011 |  |  |  |  |  |  | OPTICAL   1 - v / ( v + c ) | 
| 2012 |  |  |  |  |  |  |  | 
| 2013 |  |  |  |  |  |  | REDSHIFT  ( 1 / ( 1 + z ) ) * ( 1 - v(hel) / ( v(hel) + c ) ) | 
| 2014 |  |  |  |  |  |  |  | 
| 2015 |  |  |  |  |  |  | ie in order to observe a line in the astronomical target, multiply the | 
| 2016 |  |  |  |  |  |  | rest frequency by the doppler correction to select the correct frequency | 
| 2017 |  |  |  |  |  |  | at the telescope to tune the receiver. | 
| 2018 |  |  |  |  |  |  |  | 
| 2019 |  |  |  |  |  |  | For high velocity optical sources ( v(opt) << c ) and those sources | 
| 2020 |  |  |  |  |  |  | specified using redshift, the doppler correction is properly | 
| 2021 |  |  |  |  |  |  | calculated by first correcting the rest frequency to a redshifted | 
| 2022 |  |  |  |  |  |  | frequency (dividing by 1 + z) and then separately correcting for the | 
| 2023 |  |  |  |  |  |  | telescope motion relative to the new redshift corrected heliocentric | 
| 2024 |  |  |  |  |  |  | rest frequency. The REDSHIFT equation, above, is used in this case and | 
| 2025 |  |  |  |  |  |  | is used if the source radial velocity is > 0.01 c. ie the Doppler | 
| 2026 |  |  |  |  |  |  | correction is calculated for a source at 0 km/s Heliocentric and | 
| 2027 |  |  |  |  |  |  | combined with the redshift correction. | 
| 2028 |  |  |  |  |  |  |  | 
| 2029 |  |  |  |  |  |  | The Doppler correction is invalid for large radio velocities. | 
| 2030 |  |  |  |  |  |  |  | 
| 2031 |  |  |  |  |  |  | =cut | 
| 2032 |  |  |  |  |  |  |  | 
| 2033 |  |  |  |  |  |  | sub doppler { | 
| 2034 | 2 |  |  | 2 | 1 | 4 | my $self = shift; | 
| 2035 | 2 |  |  |  |  | 10 | my $vdefn = $self->vdefn; | 
| 2036 | 2 |  |  |  |  | 9 | my $obsvel = $self->obsvel; | 
| 2037 |  |  |  |  |  |  |  | 
| 2038 |  |  |  |  |  |  | # Doppler correction depends on definition | 
| 2039 | 2 |  |  |  |  | 3 | my $doppler; | 
| 2040 | 2 | 100 | 33 |  |  | 15 | if ( $vdefn eq 'RADIO' ) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 2041 | 1 |  |  |  |  | 10 | $doppler = 1 - ( $obsvel / CLIGHT ); | 
| 2042 |  |  |  |  |  |  | } elsif ( $vdefn eq 'OPTICAL' || $vdefn eq 'REDSHIFT' ) { | 
| 2043 | 1 | 50 |  |  |  | 3 | if ( $obsvel > (0.01 * CLIGHT)) { | 
| 2044 |  |  |  |  |  |  | # Relativistic velocity | 
| 2045 |  |  |  |  |  |  | # First calculate the redshift correction | 
| 2046 | 1 |  |  |  |  | 4 | my $zcorr = 1 / ( 1 + $self->redshift ); | 
| 2047 |  |  |  |  |  |  |  | 
| 2048 |  |  |  |  |  |  | # Now the observer doppler correction to Heliocentric frame | 
| 2049 | 1 |  |  |  |  | 3 | my $vhel = $self->vhelio; | 
| 2050 | 1 |  |  |  |  | 4 | my $obscorr = 1 - ( $vhel / ( CLIGHT * $vhel) ); | 
| 2051 |  |  |  |  |  |  |  | 
| 2052 | 1 |  |  |  |  | 2 | $doppler = $zcorr * $obscorr; | 
| 2053 |  |  |  |  |  |  |  | 
| 2054 |  |  |  |  |  |  | } else { | 
| 2055 |  |  |  |  |  |  | # small radial velocity, use standard doppler formula | 
| 2056 | 0 |  |  |  |  | 0 | $doppler = 1 - ( $obsvel / ( CLIGHT + $obsvel ) ); | 
| 2057 |  |  |  |  |  |  | } | 
| 2058 |  |  |  |  |  |  | } elsif ( $vdefn eq 'RELATIVISTIC' ) { | 
| 2059 |  |  |  |  |  |  | # do we need to use the same correction as for OPTICAL and REDSHIFT? | 
| 2060 |  |  |  |  |  |  | # presumably | 
| 2061 | 0 |  |  |  |  | 0 | $doppler = sqrt( ( CLIGHT - $obsvel ) / ( CLIGHT + $obsvel ) ); | 
| 2062 |  |  |  |  |  |  | } else { | 
| 2063 | 0 |  |  |  |  | 0 | croak "Can not calculate doppler correction for unsupported definition $vdefn\n"; | 
| 2064 |  |  |  |  |  |  | } | 
| 2065 | 2 |  |  |  |  | 238 | return $doppler; | 
| 2066 |  |  |  |  |  |  | } | 
| 2067 |  |  |  |  |  |  |  | 
| 2068 |  |  |  |  |  |  | =item B<vdiff> | 
| 2069 |  |  |  |  |  |  |  | 
| 2070 |  |  |  |  |  |  | Simple wrapper around the individual velocity methods (C<vhelio>, C<vlsrk> etc) | 
| 2071 |  |  |  |  |  |  | to report the difference in velocity between two arbitrary frames. | 
| 2072 |  |  |  |  |  |  |  | 
| 2073 |  |  |  |  |  |  | $vd = $c->vdiff( 'HELIOCENTRIC', 'TOPOCENTRIC' ); | 
| 2074 |  |  |  |  |  |  | $vd = $c->vdiff( 'HEL', 'LSRK' ); | 
| 2075 |  |  |  |  |  |  |  | 
| 2076 |  |  |  |  |  |  | Note that the velocity methods all report their velocity relative to the | 
| 2077 |  |  |  |  |  |  | observer (ie topocentric correction), equivalent to specifiying 'TOP' | 
| 2078 |  |  |  |  |  |  | as the second argument to vdiff. | 
| 2079 |  |  |  |  |  |  |  | 
| 2080 |  |  |  |  |  |  | The two arguments are mandatory but if either are 'undef' they are converted | 
| 2081 |  |  |  |  |  |  | to the target velocity frame (see C<vdefn> method). | 
| 2082 |  |  |  |  |  |  |  | 
| 2083 |  |  |  |  |  |  | The second example is simply equivalent to | 
| 2084 |  |  |  |  |  |  |  | 
| 2085 |  |  |  |  |  |  | $vd = $c->vhelio - $c->vlsrk; | 
| 2086 |  |  |  |  |  |  |  | 
| 2087 |  |  |  |  |  |  | but the usefulness of this method really comes into play when defaulting to | 
| 2088 |  |  |  |  |  |  | the target frame since it removes the need for logic in the main program. | 
| 2089 |  |  |  |  |  |  |  | 
| 2090 |  |  |  |  |  |  | $vd = $c->vdiff( 'HEL', '' ); | 
| 2091 |  |  |  |  |  |  |  | 
| 2092 |  |  |  |  |  |  | =cut | 
| 2093 |  |  |  |  |  |  |  | 
| 2094 |  |  |  |  |  |  | sub vdiff { | 
| 2095 | 4 |  |  | 4 | 1 | 7 | my $self = shift; | 
| 2096 | 4 |  | 66 |  |  | 12 | my $f1 = ( shift || $self->vframe ); | 
| 2097 | 4 |  | 33 |  |  | 11 | my $f2 = ( shift || $self->vframe ); | 
| 2098 |  |  |  |  |  |  |  | 
| 2099 |  |  |  |  |  |  | # convert the arguments to standardised frames | 
| 2100 | 4 |  |  |  |  | 12 | $f1 = $self->_normalise_vframe( $f1 ); | 
| 2101 | 4 |  |  |  |  | 20 | $f2 = $self->_normalise_vframe( $f2 ); | 
| 2102 |  |  |  |  |  |  |  | 
| 2103 | 4 | 50 |  |  |  | 26 | return 0 if $f1 eq $f2; | 
| 2104 |  |  |  |  |  |  |  | 
| 2105 |  |  |  |  |  |  | # put all the supported answers in a hash relative to TOP | 
| 2106 | 4 |  |  |  |  | 6 | my %vel; | 
| 2107 | 4 |  |  |  |  | 8 | $vel{TOP} = 0; | 
| 2108 | 4 |  |  |  |  | 9 | $vel{GEO} = $self->verot(); | 
| 2109 | 4 |  |  |  |  | 18 | $vel{HEL} = $self->vhelio; | 
| 2110 | 4 |  |  |  |  | 9 | $vel{BAR} = $self->vbary; | 
| 2111 | 4 |  |  |  |  | 20 | $vel{LSRK} = $self->vlsrk; | 
| 2112 | 4 |  |  |  |  | 11 | $vel{LSRD} = $self->vlsrd; | 
| 2113 | 4 |  |  |  |  | 14 | $vel{GAL}  = $self->vgalc; | 
| 2114 | 4 |  |  |  |  | 33 | $vel{LG}   = $self->vlg; | 
| 2115 |  |  |  |  |  |  |  | 
| 2116 |  |  |  |  |  |  | # now the difference is easy | 
| 2117 | 4 |  |  |  |  | 17 | return ( $vel{$f1} - $vel{$f2} ); | 
| 2118 |  |  |  |  |  |  | } | 
| 2119 |  |  |  |  |  |  |  | 
| 2120 |  |  |  |  |  |  | =item B<verot> | 
| 2121 |  |  |  |  |  |  |  | 
| 2122 |  |  |  |  |  |  | The velocity component of the Earth's rotation in the direction of the | 
| 2123 |  |  |  |  |  |  | target (in km/s). | 
| 2124 |  |  |  |  |  |  |  | 
| 2125 |  |  |  |  |  |  | $vrot = $c->verot(); | 
| 2126 |  |  |  |  |  |  |  | 
| 2127 |  |  |  |  |  |  | Current time will be assumed if none is set. If no observer location | 
| 2128 |  |  |  |  |  |  | is specified, the equator at 0 deg lat will be used. | 
| 2129 |  |  |  |  |  |  |  | 
| 2130 |  |  |  |  |  |  | =cut | 
| 2131 |  |  |  |  |  |  |  | 
| 2132 |  |  |  |  |  |  | sub verot { | 
| 2133 | 41 |  |  | 41 | 1 | 432 | my $self = shift; | 
| 2134 |  |  |  |  |  |  |  | 
| 2135 |  |  |  |  |  |  | # Local Sidereal Time | 
| 2136 | 41 |  |  |  |  | 80 | my $lst = $self->_lst; | 
| 2137 |  |  |  |  |  |  |  | 
| 2138 |  |  |  |  |  |  | # Observer location | 
| 2139 | 41 |  |  |  |  | 88 | my $tel = $self->telescope; | 
| 2140 | 41 | 100 |  |  |  | 108 | my $lat = (defined $tel ? $tel->lat : 0 ); | 
| 2141 |  |  |  |  |  |  |  | 
| 2142 |  |  |  |  |  |  | # apparent ra dec | 
| 2143 | 41 |  |  |  |  | 204 | my ($ra, $dec) = $self->apparent(); | 
| 2144 |  |  |  |  |  |  |  | 
| 2145 | 41 |  |  |  |  | 144 | return Astro::PAL::palRverot( $lat, $ra, $dec, $lst ); | 
| 2146 |  |  |  |  |  |  | } | 
| 2147 |  |  |  |  |  |  |  | 
| 2148 |  |  |  |  |  |  | =item B<vorb> | 
| 2149 |  |  |  |  |  |  |  | 
| 2150 |  |  |  |  |  |  | Velocity component of the Earth's orbit in the direction of the target | 
| 2151 |  |  |  |  |  |  | (in km/s) for the current date and time. | 
| 2152 |  |  |  |  |  |  |  | 
| 2153 |  |  |  |  |  |  | $vorb = $c->vorb; | 
| 2154 |  |  |  |  |  |  |  | 
| 2155 |  |  |  |  |  |  | By default calculates the velocity component relative to the Sun. | 
| 2156 |  |  |  |  |  |  | If an optional parameter is true, the calculation will be relative to | 
| 2157 |  |  |  |  |  |  | the solary system barycentre. | 
| 2158 |  |  |  |  |  |  |  | 
| 2159 |  |  |  |  |  |  | $vorb = $c->vorb( $usebary ); | 
| 2160 |  |  |  |  |  |  |  | 
| 2161 |  |  |  |  |  |  | =cut | 
| 2162 |  |  |  |  |  |  |  | 
| 2163 |  |  |  |  |  |  | sub vorb { | 
| 2164 | 36 |  |  | 36 | 1 | 62 | my $self = shift; | 
| 2165 | 36 |  |  |  |  | 65 | my $usebary = shift; | 
| 2166 |  |  |  |  |  |  |  | 
| 2167 |  |  |  |  |  |  | # Earth velocity (and position) | 
| 2168 | 36 |  |  |  |  | 89 | my ($vbr,$pbr,$vhr,$phr) = Astro::PAL::palEvp($self->_mjd_tt(), 2000.0); | 
| 2169 |  |  |  |  |  |  |  | 
| 2170 |  |  |  |  |  |  | # Barycentric or heliocentric velocity? | 
| 2171 | 36 | 100 |  |  |  | 143 | my @v = ( $usebary ? @$vbr : @$vhr ); | 
| 2172 |  |  |  |  |  |  |  | 
| 2173 |  |  |  |  |  |  | # Convert spherical source coords to cartesian | 
| 2174 | 36 |  |  |  |  | 114 | my ($ra, $dec) = $self->radec; | 
| 2175 | 36 |  |  |  |  | 133 | my @cart = Astro::PAL::palDcs2c($ra,$dec); | 
| 2176 |  |  |  |  |  |  |  | 
| 2177 |  |  |  |  |  |  | # Velocity due to Earth's orbit is scalar product of the star position | 
| 2178 |  |  |  |  |  |  | # with the Earth's velocity | 
| 2179 | 36 |  |  |  |  | 243 | my $vorb = - Astro::PAL::palDvdv(\@cart,\@v)* AU2KM; | 
| 2180 | 36 |  |  |  |  | 342 | return $vorb; | 
| 2181 |  |  |  |  |  |  | } | 
| 2182 |  |  |  |  |  |  |  | 
| 2183 |  |  |  |  |  |  | =item B<vhelio> | 
| 2184 |  |  |  |  |  |  |  | 
| 2185 |  |  |  |  |  |  | Velocity of the observer with respect to the Sun in the direction of | 
| 2186 |  |  |  |  |  |  | the target (ie the heliocentric frame).  This is simply the sum of the | 
| 2187 |  |  |  |  |  |  | component due to the Earth's orbit and the component due to the | 
| 2188 |  |  |  |  |  |  | Earth's rotation. | 
| 2189 |  |  |  |  |  |  |  | 
| 2190 |  |  |  |  |  |  | $vhel = $c->vhelio; | 
| 2191 |  |  |  |  |  |  |  | 
| 2192 |  |  |  |  |  |  | =cut | 
| 2193 |  |  |  |  |  |  |  | 
| 2194 |  |  |  |  |  |  | sub vhelio { | 
| 2195 | 31 |  |  | 31 | 1 | 59 | my $self = shift; | 
| 2196 | 31 |  |  |  |  | 58 | return ($self->verot + $self->vorb); | 
| 2197 |  |  |  |  |  |  | } | 
| 2198 |  |  |  |  |  |  |  | 
| 2199 |  |  |  |  |  |  | =item B<vbary> | 
| 2200 |  |  |  |  |  |  |  | 
| 2201 |  |  |  |  |  |  | Velocity of the observer with respect to the Solar System Barycentre in | 
| 2202 |  |  |  |  |  |  | the direction of the target (ie the barycentric frame).  This is | 
| 2203 |  |  |  |  |  |  | simply the sum of the component due to the Earth's orbit and the | 
| 2204 |  |  |  |  |  |  | component due to the Earth's rotation. | 
| 2205 |  |  |  |  |  |  |  | 
| 2206 |  |  |  |  |  |  | $vhel = $c->vbary; | 
| 2207 |  |  |  |  |  |  |  | 
| 2208 |  |  |  |  |  |  | =cut | 
| 2209 |  |  |  |  |  |  |  | 
| 2210 |  |  |  |  |  |  | sub vbary { | 
| 2211 | 5 |  |  | 5 | 1 | 10 | my $self = shift; | 
| 2212 | 5 |  |  |  |  | 10 | return ($self->verot + $self->vorb(1)); | 
| 2213 |  |  |  |  |  |  | } | 
| 2214 |  |  |  |  |  |  |  | 
| 2215 |  |  |  |  |  |  | =item B<vlsrk> | 
| 2216 |  |  |  |  |  |  |  | 
| 2217 |  |  |  |  |  |  | Velocity of the observer with respect to the kinematical Local Standard | 
| 2218 |  |  |  |  |  |  | of Rest in the direction of the target. | 
| 2219 |  |  |  |  |  |  |  | 
| 2220 |  |  |  |  |  |  | $vlsrk = $c->vlsrk(); | 
| 2221 |  |  |  |  |  |  |  | 
| 2222 |  |  |  |  |  |  | =cut | 
| 2223 |  |  |  |  |  |  |  | 
| 2224 |  |  |  |  |  |  | sub vlsrk { | 
| 2225 | 7 |  |  | 7 | 1 | 24 | my $self = shift; | 
| 2226 | 7 |  |  |  |  | 21 | my ($ra, $dec) = $self->radec; | 
| 2227 | 7 |  |  |  |  | 24 | return (Astro::PAL::palRvlsrk( $ra, $dec ) + $self->vhelio); | 
| 2228 |  |  |  |  |  |  | } | 
| 2229 |  |  |  |  |  |  |  | 
| 2230 |  |  |  |  |  |  | =item B<vlsrd> | 
| 2231 |  |  |  |  |  |  |  | 
| 2232 |  |  |  |  |  |  | Velocity of the observer with respect to the dynamical Local Standard | 
| 2233 |  |  |  |  |  |  | of Rest in the direction of the target. | 
| 2234 |  |  |  |  |  |  |  | 
| 2235 |  |  |  |  |  |  | $vlsrd = $c->vlsrd(); | 
| 2236 |  |  |  |  |  |  |  | 
| 2237 |  |  |  |  |  |  | =cut | 
| 2238 |  |  |  |  |  |  |  | 
| 2239 |  |  |  |  |  |  | sub vlsrd { | 
| 2240 | 11 |  |  | 11 | 1 | 19 | my $self = shift; | 
| 2241 | 11 |  |  |  |  | 25 | my ($ra, $dec) = $self->radec; | 
| 2242 | 11 |  |  |  |  | 36 | return (Astro::PAL::palRvlsrd( $ra, $dec ) + $self->vhelio); | 
| 2243 |  |  |  |  |  |  | } | 
| 2244 |  |  |  |  |  |  |  | 
| 2245 |  |  |  |  |  |  | =item B<vgalc> | 
| 2246 |  |  |  |  |  |  |  | 
| 2247 |  |  |  |  |  |  | Velocity of the observer with respect to the centre of the Galaxy | 
| 2248 |  |  |  |  |  |  | in the direction of the target. | 
| 2249 |  |  |  |  |  |  |  | 
| 2250 |  |  |  |  |  |  | $vlsrd = $c->vgalc(); | 
| 2251 |  |  |  |  |  |  |  | 
| 2252 |  |  |  |  |  |  | =cut | 
| 2253 |  |  |  |  |  |  |  | 
| 2254 |  |  |  |  |  |  | sub vgalc { | 
| 2255 | 5 |  |  | 5 | 1 | 15 | my $self = shift; | 
| 2256 | 5 |  |  |  |  | 12 | my ($ra, $dec) = $self->radec; | 
| 2257 | 5 |  |  |  |  | 17 | return (Astro::PAL::palRvgalc( $ra, $dec ) + $self->vlsrd); | 
| 2258 |  |  |  |  |  |  | } | 
| 2259 |  |  |  |  |  |  |  | 
| 2260 |  |  |  |  |  |  | =item B<vlg> | 
| 2261 |  |  |  |  |  |  |  | 
| 2262 |  |  |  |  |  |  | Velocity of the observer with respect to the Local Group in the | 
| 2263 |  |  |  |  |  |  | direction of the target. | 
| 2264 |  |  |  |  |  |  |  | 
| 2265 |  |  |  |  |  |  | $vlsrd = $c->vlg(); | 
| 2266 |  |  |  |  |  |  |  | 
| 2267 |  |  |  |  |  |  | =cut | 
| 2268 |  |  |  |  |  |  |  | 
| 2269 |  |  |  |  |  |  | sub vlg { | 
| 2270 | 5 |  |  | 5 | 1 | 11 | my $self = shift; | 
| 2271 | 5 |  |  |  |  | 12 | my ($ra, $dec) = $self->radec; | 
| 2272 | 5 |  |  |  |  | 26 | return (Astro::PAL::palRvlg( $ra, $dec ) + $self->vhelio); | 
| 2273 |  |  |  |  |  |  | } | 
| 2274 |  |  |  |  |  |  |  | 
| 2275 |  |  |  |  |  |  | =back | 
| 2276 |  |  |  |  |  |  |  | 
| 2277 |  |  |  |  |  |  | =begin __PRIVATE_METHODS__ | 
| 2278 |  |  |  |  |  |  |  | 
| 2279 |  |  |  |  |  |  | =head2 Private Methods | 
| 2280 |  |  |  |  |  |  |  | 
| 2281 |  |  |  |  |  |  | The following methods are not part of the public interface and can be | 
| 2282 |  |  |  |  |  |  | modified or removed for any release of this module. | 
| 2283 |  |  |  |  |  |  |  | 
| 2284 |  |  |  |  |  |  | =over 4 | 
| 2285 |  |  |  |  |  |  |  | 
| 2286 |  |  |  |  |  |  | =item B<_lst> | 
| 2287 |  |  |  |  |  |  |  | 
| 2288 |  |  |  |  |  |  | Calculate the LST for the current date/time and | 
| 2289 |  |  |  |  |  |  | telescope and return it (in radians). | 
| 2290 |  |  |  |  |  |  |  | 
| 2291 |  |  |  |  |  |  | If no date/time is specified the current time will be used. | 
| 2292 |  |  |  |  |  |  | If no telescope is defined the LST will be from Greenwich. | 
| 2293 |  |  |  |  |  |  |  | 
| 2294 |  |  |  |  |  |  | This is labelled as an internal routine since it is not clear whether | 
| 2295 |  |  |  |  |  |  | the method to determine LST should be here or simply placed into | 
| 2296 |  |  |  |  |  |  | C<DateTime>. In practice this simply calls the | 
| 2297 |  |  |  |  |  |  | C<Astro::PAL::ut2lst> function with the correct args (and therefore | 
| 2298 |  |  |  |  |  |  | does not need the MJD). It will need the longitude though so we | 
| 2299 |  |  |  |  |  |  | calculate it here. | 
| 2300 |  |  |  |  |  |  |  | 
| 2301 |  |  |  |  |  |  | No correction for DUT1 is applied. | 
| 2302 |  |  |  |  |  |  |  | 
| 2303 |  |  |  |  |  |  | =cut | 
| 2304 |  |  |  |  |  |  |  | 
| 2305 |  |  |  |  |  |  | sub _lst { | 
| 2306 | 11716 |  |  | 11716 |  | 17852 | my $self = shift; | 
| 2307 |  |  |  |  |  |  |  | 
| 2308 | 11716 |  |  |  |  | 21870 | my $cachedlst = $self->_cache_read_global_lst(); | 
| 2309 | 11716 | 100 |  |  |  | 30984 | return $cachedlst if defined $cachedlst; | 
| 2310 |  |  |  |  |  |  |  | 
| 2311 | 8758 |  |  |  |  | 16163 | my $time = $self->datetime; | 
| 2312 | 8758 |  |  |  |  | 23982 | my $tel = $self->telescope; | 
| 2313 |  |  |  |  |  |  |  | 
| 2314 |  |  |  |  |  |  | # Get the longitude (in radians) | 
| 2315 | 8758 | 100 |  |  |  | 27688 | my $long = (defined $tel ? $tel->long : 0.0 ); | 
| 2316 |  |  |  |  |  |  |  | 
| 2317 |  |  |  |  |  |  | # Seconds can be floating point. | 
| 2318 | 8758 |  |  |  |  | 67826 | my $sec = $time->sec; | 
| 2319 | 8758 | 100 |  |  |  | 59440 | if ($time->can("nanosecond")) { | 
| 2320 | 8649 |  |  |  |  | 18670 | $sec += 1E-9 * $time->nanosecond; | 
| 2321 |  |  |  |  |  |  | } | 
| 2322 |  |  |  |  |  |  |  | 
| 2323 |  |  |  |  |  |  | # Return the first arg | 
| 2324 |  |  |  |  |  |  | # Note that we guarantee a UT time representation | 
| 2325 | 8758 |  |  |  |  | 55303 | my $lst = (Astro::PAL::ut2lst( $time->year, $time->mon, | 
| 2326 |  |  |  |  |  |  | $time->mday, $time->hour, | 
| 2327 |  |  |  |  |  |  | $time->min, $sec, $long))[0]; | 
| 2328 |  |  |  |  |  |  |  | 
| 2329 | 8758 |  |  |  |  | 2140188 | my $lstobject = new Astro::Coords::Angle::Hour( $lst, units => 'rad', range => '2PI'); | 
| 2330 |  |  |  |  |  |  |  | 
| 2331 | 8758 |  |  |  |  | 25185 | $self->_cache_write_global_lst($lstobject); | 
| 2332 |  |  |  |  |  |  |  | 
| 2333 | 8758 |  |  |  |  | 25701 | return $lstobject; | 
| 2334 |  |  |  |  |  |  | } | 
| 2335 |  |  |  |  |  |  |  | 
| 2336 |  |  |  |  |  |  | =item B<_mjd_tt> | 
| 2337 |  |  |  |  |  |  |  | 
| 2338 |  |  |  |  |  |  | Internal routine to retrieve the MJD in TT (Terrestrial time) rather than UTC time. | 
| 2339 |  |  |  |  |  |  |  | 
| 2340 |  |  |  |  |  |  | =cut | 
| 2341 |  |  |  |  |  |  |  | 
| 2342 |  |  |  |  |  |  | sub _mjd_tt { | 
| 2343 | 13641 |  |  | 13641 |  | 19995 | my $self = shift; | 
| 2344 | 13641 |  |  |  |  | 30337 | my $mjd = $self->datetime->mjd; | 
| 2345 | 13641 |  |  |  |  | 178404 | my $offset = Astro::PAL::palDtt( $mjd ); | 
| 2346 | 13641 |  |  |  |  | 22331 | $mjd += ($offset / (86_400)); | 
| 2347 | 13641 |  |  |  |  | 99471 | return $mjd; | 
| 2348 |  |  |  |  |  |  | } | 
| 2349 |  |  |  |  |  |  |  | 
| 2350 |  |  |  |  |  |  | =item B<_clone_time> | 
| 2351 |  |  |  |  |  |  |  | 
| 2352 |  |  |  |  |  |  | Internal routine to copy a Time::Piece or DateTime object | 
| 2353 |  |  |  |  |  |  | into a new object for internal storage. | 
| 2354 |  |  |  |  |  |  |  | 
| 2355 |  |  |  |  |  |  | $clone = _clone_time( $orig ); | 
| 2356 |  |  |  |  |  |  |  | 
| 2357 |  |  |  |  |  |  | =cut | 
| 2358 |  |  |  |  |  |  |  | 
| 2359 |  |  |  |  |  |  | sub _clone_time { | 
| 2360 | 15259 |  |  | 15259 |  | 21912 | my $input = shift; | 
| 2361 | 15259 | 100 |  |  |  | 29434 | return unless defined $input; | 
| 2362 |  |  |  |  |  |  |  | 
| 2363 | 15256 | 100 |  |  |  | 73780 | if (UNIVERSAL::isa($input, "Time::Piece")) { | 
|  |  | 50 |  |  |  |  |  | 
| 2364 | 319 |  |  |  |  | 783 | return Time::Piece::gmtime( $input->epoch ); | 
| 2365 |  |  |  |  |  |  | } elsif (UNIVERSAL::isa($input, "DateTime")) { | 
| 2366 |  |  |  |  |  |  | # Use proper clone method | 
| 2367 | 14937 |  |  |  |  | 37092 | return $input->clone; | 
| 2368 |  |  |  |  |  |  | } | 
| 2369 | 0 |  |  |  |  | 0 | return; | 
| 2370 |  |  |  |  |  |  | } | 
| 2371 |  |  |  |  |  |  |  | 
| 2372 |  |  |  |  |  |  | =item B<_inc_time> | 
| 2373 |  |  |  |  |  |  |  | 
| 2374 |  |  |  |  |  |  | Internal routine to increment the supplied time by the supplied | 
| 2375 |  |  |  |  |  |  | number of seconds (either as an integer or the native duration class). | 
| 2376 |  |  |  |  |  |  |  | 
| 2377 |  |  |  |  |  |  | $date = _inc_time( $date, $seconds ); | 
| 2378 |  |  |  |  |  |  |  | 
| 2379 |  |  |  |  |  |  | Does return the date object since not all date objects can be modified inplace. | 
| 2380 |  |  |  |  |  |  | Does not guarantee to return a new cloned object though. | 
| 2381 |  |  |  |  |  |  |  | 
| 2382 |  |  |  |  |  |  | =cut | 
| 2383 |  |  |  |  |  |  |  | 
| 2384 |  |  |  |  |  |  | sub _inc_time { | 
| 2385 | 25 |  |  | 25 |  | 44 | my $date = shift; | 
| 2386 | 25 |  |  |  |  | 40 | my $delta = shift; | 
| 2387 |  |  |  |  |  |  |  | 
| 2388 |  |  |  |  |  |  | # convert to integer seconds | 
| 2389 | 25 | 50 |  |  |  | 112 | $delta = $delta->seconds() if UNIVERSAL::can( $delta, "seconds" ); | 
| 2390 |  |  |  |  |  |  |  | 
| 2391 |  |  |  |  |  |  | # Check for Time::Piece, else assume DateTime | 
| 2392 | 25 | 50 |  |  |  | 122 | if (UNIVERSAL::isa( $date, "Time::Piece" ) ) { | 
| 2393 |  |  |  |  |  |  | # can not add time in place | 
| 2394 | 25 |  |  |  |  | 94 | $date += $delta; | 
| 2395 |  |  |  |  |  |  | } else { | 
| 2396 |  |  |  |  |  |  | # increment the time (this happens in place) | 
| 2397 | 0 | 0 |  |  |  | 0 | if (abs($delta) > 1) { | 
| 2398 | 0 |  |  |  |  | 0 | $date->add( seconds => "$delta" ); | 
| 2399 |  |  |  |  |  |  | } else { | 
| 2400 | 0 |  |  |  |  | 0 | $date->add( nanoseconds => ( $delta * 1E9 ) ); | 
| 2401 |  |  |  |  |  |  | } | 
| 2402 |  |  |  |  |  |  | } | 
| 2403 |  |  |  |  |  |  |  | 
| 2404 | 25 |  |  |  |  | 1849 | return $date; | 
| 2405 |  |  |  |  |  |  | } | 
| 2406 |  |  |  |  |  |  |  | 
| 2407 |  |  |  |  |  |  | =item B<_cmp_time> | 
| 2408 |  |  |  |  |  |  |  | 
| 2409 |  |  |  |  |  |  | Internal routine to Compare two times (assuming the same class) | 
| 2410 |  |  |  |  |  |  |  | 
| 2411 |  |  |  |  |  |  | $cmp = _cmp_time( $a, $b ); | 
| 2412 |  |  |  |  |  |  |  | 
| 2413 |  |  |  |  |  |  | Returns 1 if $a > $b (epoch) | 
| 2414 |  |  |  |  |  |  | -1 if $a < $b (epoch) | 
| 2415 |  |  |  |  |  |  | 0 if $a == $b (epoch) | 
| 2416 |  |  |  |  |  |  |  | 
| 2417 |  |  |  |  |  |  | Currently assumes epoch is enough for comparison and so works | 
| 2418 |  |  |  |  |  |  | for both DateTime and Time::Piece objects. | 
| 2419 |  |  |  |  |  |  |  | 
| 2420 |  |  |  |  |  |  | =cut | 
| 2421 |  |  |  |  |  |  |  | 
| 2422 |  |  |  |  |  |  | sub _cmp_time { | 
| 2423 | 0 |  |  | 0 |  | 0 | my $t1 = shift; | 
| 2424 | 0 |  |  |  |  | 0 | my $t2 = shift; | 
| 2425 | 0 | 0 |  |  |  | 0 | my $e1 = (defined $t1 ? $t1->epoch : 0); | 
| 2426 | 0 | 0 |  |  |  | 0 | my $e2 = (defined $t2 ? $t2->epoch : 0); | 
| 2427 | 0 |  |  |  |  | 0 | return $e1 <=> $e2; | 
| 2428 |  |  |  |  |  |  | } | 
| 2429 |  |  |  |  |  |  |  | 
| 2430 |  |  |  |  |  |  | =item B<_default_horizon> | 
| 2431 |  |  |  |  |  |  |  | 
| 2432 |  |  |  |  |  |  | Returns the default horizon to use for rise/set calculations. | 
| 2433 |  |  |  |  |  |  | Normally, a value is supplied to the relevant routines. | 
| 2434 |  |  |  |  |  |  |  | 
| 2435 |  |  |  |  |  |  | In the base class, returns 0. Can be overridden by subclasses (in particular | 
| 2436 |  |  |  |  |  |  | the moon and sun). | 
| 2437 |  |  |  |  |  |  |  | 
| 2438 |  |  |  |  |  |  | =cut | 
| 2439 |  |  |  |  |  |  |  | 
| 2440 |  |  |  |  |  |  | sub _default_horizon { | 
| 2441 | 10 |  |  | 10 |  | 18 | return 0; | 
| 2442 |  |  |  |  |  |  | } | 
| 2443 |  |  |  |  |  |  |  | 
| 2444 |  |  |  |  |  |  | =item B<_rise_set_time> | 
| 2445 |  |  |  |  |  |  |  | 
| 2446 |  |  |  |  |  |  | Return either the rise time or set time associated with a specific horizon. | 
| 2447 |  |  |  |  |  |  |  | 
| 2448 |  |  |  |  |  |  | $time = $c->_rise_set_time( $rise, %opt ); | 
| 2449 |  |  |  |  |  |  |  | 
| 2450 |  |  |  |  |  |  | Where the options hash controls the horizon and whether the event | 
| 2451 |  |  |  |  |  |  | should follow the reference time, come before the reference time or be | 
| 2452 |  |  |  |  |  |  | the nearest event. | 
| 2453 |  |  |  |  |  |  |  | 
| 2454 |  |  |  |  |  |  | Supported keys for event control are processed by the _extract_event | 
| 2455 |  |  |  |  |  |  | private method. | 
| 2456 |  |  |  |  |  |  |  | 
| 2457 |  |  |  |  |  |  | $rise should be true if the source is rising, and false if the source | 
| 2458 |  |  |  |  |  |  | is setting. | 
| 2459 |  |  |  |  |  |  |  | 
| 2460 |  |  |  |  |  |  | =cut | 
| 2461 |  |  |  |  |  |  |  | 
| 2462 |  |  |  |  |  |  | sub _rise_set_time { | 
| 2463 | 3058 |  |  | 3058 |  | 4731 | my $self = shift; | 
| 2464 | 3058 |  |  |  |  | 4333 | my $rise = shift; | 
| 2465 | 3058 |  |  |  |  | 10079 | my %opt = @_; | 
| 2466 | 3058 |  |  |  |  | 7574 | my $period = $self->_sidereal_period(); | 
| 2467 |  |  |  |  |  |  |  | 
| 2468 |  |  |  |  |  |  | # Calculate the HA required for setting | 
| 2469 | 3058 |  |  |  |  | 8998 | my $ha_set = $self->ha_set( %opt, format=> 'radians' ); | 
| 2470 | 3058 | 100 |  |  |  | 10889 | return if ! defined $ha_set; | 
| 2471 |  |  |  |  |  |  |  | 
| 2472 |  |  |  |  |  |  | # extract the event information | 
| 2473 | 2540 |  |  |  |  | 7860 | my $event = $self->_extract_event( %opt ); | 
| 2474 |  |  |  |  |  |  | croak "Unrecognized event specifier in set_time" | 
| 2475 | 2540 | 50 |  |  |  | 5652 | unless grep {$_ == $event} (-1, 0, 1); | 
|  | 7620 |  |  |  |  | 17396 |  | 
| 2476 |  |  |  |  |  |  |  | 
| 2477 |  |  |  |  |  |  | # and convert to seconds | 
| 2478 | 2540 |  |  |  |  | 4405 | $ha_set *= Astro::PAL::DR2S; | 
| 2479 |  |  |  |  |  |  |  | 
| 2480 |  |  |  |  |  |  | # and thence to a duration if required | 
| 2481 | 2540 | 100 |  |  |  | 5377 | if ($self->_isdt()) { | 
| 2482 | 2526 |  |  |  |  | 8176 | $ha_set = new DateTime::Duration( seconds => $ha_set ); | 
| 2483 | 2526 |  |  |  |  | 279405 | $period = new DateTime::Duration( seconds => $period ); | 
| 2484 |  |  |  |  |  |  | } | 
| 2485 |  |  |  |  |  |  |  | 
| 2486 |  |  |  |  |  |  | # Determine whether we have to remember the cache | 
| 2487 | 2540 |  |  |  |  | 235304 | my $havetime = $self->has_datetime; | 
| 2488 |  |  |  |  |  |  |  | 
| 2489 |  |  |  |  |  |  | # Need the requested horizon | 
| 2490 |  |  |  |  |  |  | my $refel = (defined $opt{horizon} ? $opt{horizon} : | 
| 2491 | 2540 | 100 |  |  |  | 7544 | $self->_default_horizon ); | 
| 2492 |  |  |  |  |  |  |  | 
| 2493 |  |  |  |  |  |  | # somewhere to store the times | 
| 2494 | 2540 |  |  |  |  | 3666 | my @event; | 
| 2495 | 2540 |  | 100 |  |  | 7430 | my $direction = $event || 1; | 
| 2496 | 2540 |  |  |  |  | 3298 | my $event_time; | 
| 2497 |  |  |  |  |  |  |  | 
| 2498 |  |  |  |  |  |  | # Do not bother with iterative method for comparisons if the | 
| 2499 |  |  |  |  |  |  | # values differ by more than this amount.  (Ideally this would | 
| 2500 |  |  |  |  |  |  | # be tuned by source depending on how fast it moves.) | 
| 2501 | 2540 |  |  |  |  | 3705 | my $safety_seconds = 3600; | 
| 2502 |  |  |  |  |  |  |  | 
| 2503 |  |  |  |  |  |  | # To be able to defer interative calculations until necessary, | 
| 2504 |  |  |  |  |  |  | # we need a data structure which can store the time and whether | 
| 2505 |  |  |  |  |  |  | # it has been iterated.  For convenience we can add the epoch and | 
| 2506 |  |  |  |  |  |  | # end up with an arrayref like: | 
| 2507 |  |  |  |  |  |  | # | 
| 2508 |  |  |  |  |  |  | #   $event = [$datetime_or_timepiece, $epoch_seconds, $iterated] | 
| 2509 |  |  |  |  |  |  | # | 
| 2510 |  |  |  |  |  |  | # So save the reference time in this format: | 
| 2511 |  |  |  |  |  |  | # | 
| 2512 |  |  |  |  |  |  | # Get the current time (do not modify it since we need to put it back) | 
| 2513 | 2540 |  |  |  |  | 4824 | my $reftime = $self->datetime; | 
| 2514 | 2540 |  |  |  |  | 7437 | $reftime = [$reftime, $reftime->epoch(), 1]; | 
| 2515 |  |  |  |  |  |  |  | 
| 2516 |  |  |  |  |  |  | # And define 2 convient subroutines for these arrayrefs: | 
| 2517 |  |  |  |  |  |  | # | 
| 2518 |  |  |  |  |  |  | # Subroutine to perform the iteration on the event and | 
| 2519 |  |  |  |  |  |  | # check for convergence. | 
| 2520 |  |  |  |  |  |  | my $iterate = sub { | 
| 2521 | 2645 |  |  | 2645 |  | 3914 | my $estimate = shift; | 
| 2522 | 2645 | 50 |  |  |  | 5255 | return if $estimate->[2]; | 
| 2523 | 2645 |  |  |  |  | 7799 | $self->datetime( $estimate->[0] ); | 
| 2524 | 2645 | 100 |  |  |  | 9274 | if ($self->_iterative_el( $refel, ($rise ? 1 : -1) )) { | 
|  |  | 100 |  |  |  |  |  | 
| 2525 | 2644 |  |  |  |  | 5618 | my $dt = $self->datetime(); | 
| 2526 | 2644 |  |  |  |  | 12965 | $estimate->[0] = $dt; | 
| 2527 | 2644 |  |  |  |  | 7248 | $estimate->[1] = $dt->epoch(); | 
| 2528 | 2644 |  |  |  |  | 15089 | $estimate->[2] = 1; | 
| 2529 |  |  |  |  |  |  | } else { | 
| 2530 | 1 | 50 |  |  |  | 6 | print "**** Failed to converge\n" if $DEBUG; | 
| 2531 | 1 |  |  |  |  | 13 | $estimate->[0] = undef; | 
| 2532 | 1 |  |  |  |  | 3 | $estimate->[1] = undef; | 
| 2533 | 1 |  |  |  |  | 4 | $estimate->[2] = 1; | 
| 2534 |  |  |  |  |  |  | } | 
| 2535 | 2540 |  |  |  |  | 24976 | }; | 
| 2536 |  |  |  |  |  |  |  | 
| 2537 |  |  |  |  |  |  | # Subroutine to compare two event arrayrefs.  Iterate them | 
| 2538 |  |  |  |  |  |  | # if they are within $safety_seconds. | 
| 2539 |  |  |  |  |  |  | my $compare = sub { | 
| 2540 | 4848 |  |  | 4848 |  | 8632 | my ($a, $b) = @_; | 
| 2541 | 4848 | 50 | 33 |  |  | 18066 | return undef unless (defined $a->[0] and defined $b->[0]); | 
| 2542 | 4848 | 100 | 66 |  |  | 17296 | return $a->[1] <=> $b->[1] if (abs($b->[1] - $a->[1]) > $safety_seconds) | 
|  |  |  | 100 |  |  |  |  | 
| 2543 |  |  |  |  |  |  | or ($b->[2] && $a->[2]); | 
| 2544 | 143 | 50 |  |  |  | 628 | $iterate->($a) unless $a->[2]; | 
| 2545 | 143 | 50 |  |  |  | 731 | $iterate->($b) unless $b->[2]; | 
| 2546 | 143 | 50 | 33 |  |  | 835 | return undef unless (defined $a->[0] and defined $b->[0]); | 
| 2547 | 143 |  |  |  |  | 390 | return $a->[1] <=> $b->[1]; | 
| 2548 | 2540 |  |  |  |  | 8591 | }; | 
| 2549 |  |  |  |  |  |  |  | 
| 2550 |  |  |  |  |  |  | # Calculate the set or rise time for the meridian time in the direction | 
| 2551 |  |  |  |  |  |  | # in which we want to go, and then step the meridian time by one "day" | 
| 2552 |  |  |  |  |  |  | # so that we can work out which set time to return. There is probably | 
| 2553 |  |  |  |  |  |  | # an analytic short cut that can be used to decide whether the | 
| 2554 |  |  |  |  |  |  | # correct set time will be related to the previous or next meridian | 
| 2555 |  |  |  |  |  |  | # time. | 
| 2556 |  |  |  |  |  |  |  | 
| 2557 |  |  |  |  |  |  | # Calculate the transit time | 
| 2558 | 2540 | 0 |  |  |  | 6005 | print "Calculating " . ($direction == -1 ? "previous" : "next") . | 
|  |  | 50 |  |  |  |  |  | 
| 2559 |  |  |  |  |  |  | " meridian time\n" if $DEBUG; | 
| 2560 |  |  |  |  |  |  |  | 
| 2561 | 2540 |  |  |  |  | 6011 | my $mt = $self->meridian_time( event => $direction ); | 
| 2562 |  |  |  |  |  |  |  | 
| 2563 |  |  |  |  |  |  | # First guestimate for the event | 
| 2564 | 2540 | 100 |  |  |  | 5255 | if ($rise) { | 
| 2565 | 1270 |  |  |  |  | 4331 | $event_time = $mt - $ha_set; | 
| 2566 |  |  |  |  |  |  | } else { | 
| 2567 | 1270 |  |  |  |  | 4467 | $event_time = $mt + $ha_set; | 
| 2568 |  |  |  |  |  |  | } | 
| 2569 |  |  |  |  |  |  |  | 
| 2570 | 2540 |  |  |  |  | 2217237 | $event[0] = [$event_time, $event_time->epoch(), 0]; | 
| 2571 |  |  |  |  |  |  |  | 
| 2572 | 2540 | 0 |  |  |  | 24228 | print "Determined approximate " . ($rise ? "rise" : "set") . " time of ". | 
|  |  | 50 |  |  |  |  |  | 
| 2573 |  |  |  |  |  |  | $event_time . "\n" if $DEBUG; | 
| 2574 |  |  |  |  |  |  |  | 
| 2575 |  |  |  |  |  |  | # Change direction if we wanted the nearest, | 
| 2576 |  |  |  |  |  |  | # or there was no real event the way we tried, | 
| 2577 |  |  |  |  |  |  | # or we went the right way. | 
| 2578 | 2540 | 100 |  |  |  | 5984 | if (! $event) { | 
| 2579 | 856 |  |  |  |  | 1915 | $direction = - $direction | 
| 2580 |  |  |  |  |  |  | } else { | 
| 2581 | 1684 |  |  |  |  | 4432 | my $cmp = $compare->($event[0], $reftime); | 
| 2582 | 1684 | 100 | 66 |  |  | 7976 | $direction = - $direction if (! defined $cmp) | 
| 2583 |  |  |  |  |  |  | or ($cmp * $event > 0); | 
| 2584 |  |  |  |  |  |  | } | 
| 2585 |  |  |  |  |  |  |  | 
| 2586 | 2540 | 0 |  |  |  | 5299 | print 'Calculating  meridian time stepped ' . | 
|  |  | 50 |  |  |  |  |  | 
| 2587 |  |  |  |  |  |  | ($direction == -1 ? 'back' : 'forward') . "\n" if $DEBUG; | 
| 2588 |  |  |  |  |  |  |  | 
| 2589 | 2540 | 100 |  |  |  | 4801 | if ($direction > 0) { | 
| 2590 | 856 |  |  |  |  | 2615 | $event_time = $event_time + $period; | 
| 2591 |  |  |  |  |  |  | } else { | 
| 2592 | 1684 |  |  |  |  | 4836 | $event_time = $event_time - $period; | 
| 2593 |  |  |  |  |  |  | } | 
| 2594 |  |  |  |  |  |  |  | 
| 2595 | 2540 |  |  |  |  | 2275380 | $event[1] = [$event_time, $event_time->epoch(), 0]; | 
| 2596 |  |  |  |  |  |  |  | 
| 2597 |  |  |  |  |  |  | # store the event time | 
| 2598 | 2540 | 100 |  |  |  | 24720 | if ($direction < 0) { | 
| 2599 | 1684 |  |  |  |  | 3179 | @event = reverse @event; | 
| 2600 |  |  |  |  |  |  | } | 
| 2601 |  |  |  |  |  |  |  | 
| 2602 | 2540 | 0 |  |  |  | 5362 | print "Determined approximate " . ($rise ? "rise" : "set") . " time of ". | 
|  |  | 50 |  |  |  |  |  | 
| 2603 |  |  |  |  |  |  | $event_time . "\n" if $DEBUG; | 
| 2604 |  |  |  |  |  |  |  | 
| 2605 |  |  |  |  |  |  | # choose a value | 
| 2606 | 2540 |  |  |  |  | 3945 | $event_time = undef; | 
| 2607 |  |  |  |  |  |  |  | 
| 2608 | 2540 | 100 |  |  |  | 5292 | unless ($event) { | 
| 2609 |  |  |  |  |  |  | # Need to find the "nearest" event. | 
| 2610 |  |  |  |  |  |  | # First check neither is already undefined. | 
| 2611 | 856 | 50 | 33 |  |  | 4459 | unless (defined $event[0]->[0] and defined $event[1]->[0]) { | 
| 2612 | 0 | 0 |  |  |  | 0 | if (defined $event[0]->[0]) { | 
|  |  | 0 |  |  |  |  |  | 
| 2613 | 0 |  |  |  |  | 0 | $iterate->($event[0]); | 
| 2614 | 0 |  |  |  |  | 0 | $event_time = $event[0]->[0]; | 
| 2615 |  |  |  |  |  |  | } | 
| 2616 |  |  |  |  |  |  | elsif (defined $event[1]->[0]) { | 
| 2617 | 0 |  |  |  |  | 0 | $iterate->($event[1]); | 
| 2618 | 0 |  |  |  |  | 0 | $event_time = $event[1]->[0]; | 
| 2619 |  |  |  |  |  |  | } | 
| 2620 |  |  |  |  |  |  | } else { | 
| 2621 |  |  |  |  |  |  | # Otherwise we can safely compute the differences. | 
| 2622 | 856 |  |  |  |  | 1752 | my @diff = map {abs($_->[1] - $reftime->[1])} @event; | 
|  | 1712 |  |  |  |  | 4670 |  | 
| 2623 | 856 | 100 |  |  |  | 2570 | if (abs($diff[0] - $diff[1]) < $safety_seconds) { | 
| 2624 |  |  |  |  |  |  | # Too close to call based on the estimated times, so iterate both. | 
| 2625 | 35 |  |  |  |  | 139 | $iterate->($_) foreach grep {not $_->[2]} @event; | 
|  | 70 |  |  |  |  | 268 |  | 
| 2626 |  |  |  |  |  |  |  | 
| 2627 | 35 | 50 |  |  |  | 284 | if (defined $event[0]->[0]) { | 
|  |  | 0 |  |  |  |  |  | 
| 2628 | 35 | 50 |  |  |  | 142 | if (defined $event[1]->[0]) { | 
| 2629 |  |  |  |  |  |  | # Both events were real, need to compare again. | 
| 2630 | 35 |  |  |  |  | 120 | @diff = map {abs($_->[1] - $reftime->[1])} @event; | 
|  | 70 |  |  |  |  | 237 |  | 
| 2631 | 35 | 100 |  |  |  | 155 | $event_time = $diff[0] < $diff[1] | 
| 2632 |  |  |  |  |  |  | ? $event[0]->[0] | 
| 2633 |  |  |  |  |  |  | : $event[1]->[0]; | 
| 2634 |  |  |  |  |  |  | } else { | 
| 2635 |  |  |  |  |  |  | # Only the first event was real, so return it. | 
| 2636 | 0 |  |  |  |  | 0 | $event_time = $event[0]->[0]; | 
| 2637 |  |  |  |  |  |  | } | 
| 2638 |  |  |  |  |  |  | } | 
| 2639 |  |  |  |  |  |  | elsif (defined $event[1]->[0]) { | 
| 2640 |  |  |  |  |  |  | # Only the second event was real, so return it. | 
| 2641 | 0 |  |  |  |  | 0 | $event_time = $event[1]->[0]; | 
| 2642 |  |  |  |  |  |  | } | 
| 2643 |  |  |  |  |  |  | } | 
| 2644 |  |  |  |  |  |  | else { | 
| 2645 |  |  |  |  |  |  | # Differences were sufficiently close to decide immediately. | 
| 2646 | 821 | 100 |  |  |  | 1919 | my $closer = $diff[0] < $diff[1] ? 0 : 1; | 
| 2647 | 821 |  |  |  |  | 2483 | $iterate->($event[$closer]); | 
| 2648 |  |  |  |  |  |  |  | 
| 2649 |  |  |  |  |  |  | # However we need to check if it wasn't real, and try the other one. | 
| 2650 | 821 | 50 |  |  |  | 2780 | unless (defined $event[$closer]->[0]) { | 
| 2651 | 0 |  |  |  |  | 0 | $closer = 1 - $closer; | 
| 2652 | 0 |  |  |  |  | 0 | $iterate->($event[$closer]); | 
| 2653 |  |  |  |  |  |  | } | 
| 2654 |  |  |  |  |  |  |  | 
| 2655 |  |  |  |  |  |  | # If the other one wasn't real either, we want | 
| 2656 |  |  |  |  |  |  | # to return undef anyway, so no need to check at this point. | 
| 2657 | 821 |  |  |  |  | 1853 | $event_time = $event[$closer]->[0]; | 
| 2658 |  |  |  |  |  |  | } | 
| 2659 |  |  |  |  |  |  | } | 
| 2660 |  |  |  |  |  |  | } else { | 
| 2661 |  |  |  |  |  |  | # For "before", we want the nearest time that is earlier than the | 
| 2662 |  |  |  |  |  |  | # reference time, so reverse the list to start from the end | 
| 2663 |  |  |  |  |  |  | # the end and jump out when we find an event that is before the | 
| 2664 |  |  |  |  |  |  | # reference time.  For "after" we do not reverse the list. | 
| 2665 | 1684 | 100 |  |  |  | 4491 | @event = reverse @event if $event == -1; | 
| 2666 |  |  |  |  |  |  |  | 
| 2667 | 1684 |  |  |  |  | 3706 | for my $t (@event) { | 
| 2668 | 3164 | 50 |  |  |  | 6891 | next unless defined $t->[0]; | 
| 2669 | 3164 |  |  |  |  | 5546 | my $cmp = $compare->($t, $reftime); | 
| 2670 | 3164 | 50 |  |  |  | 6460 | next unless defined $cmp; | 
| 2671 | 3164 | 100 | 66 |  |  | 9260 | if ($cmp == $event or $cmp == 0) { | 
| 2672 | 1684 | 100 |  |  |  | 5711 | $iterate->($t) unless $t->[2]; | 
| 2673 | 1684 |  |  |  |  | 3145 | $event_time = $t->[0]; | 
| 2674 | 1684 |  |  |  |  | 3246 | last; | 
| 2675 |  |  |  |  |  |  | } | 
| 2676 |  |  |  |  |  |  | } | 
| 2677 |  |  |  |  |  |  | } | 
| 2678 |  |  |  |  |  |  |  | 
| 2679 |  |  |  |  |  |  | # and restore the reference date to reset the clock | 
| 2680 | 2540 | 50 |  |  |  | 8357 | $self->datetime( ( $havetime ? $reftime->[0] : undef ) ); | 
| 2681 |  |  |  |  |  |  |  | 
| 2682 | 2540 |  |  |  |  | 56153 | return $event_time; | 
| 2683 |  |  |  |  |  |  | } | 
| 2684 |  |  |  |  |  |  |  | 
| 2685 |  |  |  |  |  |  |  | 
| 2686 |  |  |  |  |  |  | =item B<_iterative_el> | 
| 2687 |  |  |  |  |  |  |  | 
| 2688 |  |  |  |  |  |  | Use an iterative technique to calculate the time the object passes through | 
| 2689 |  |  |  |  |  |  | a specified elevation. This routine is used for non-sidereal objects (especially | 
| 2690 |  |  |  |  |  |  | the moon and fast asteroids) where a simple calculation assuming a sidereal | 
| 2691 |  |  |  |  |  |  | object may lead to inaccuracies of a few minutes (maybe even 10s of minutes). | 
| 2692 |  |  |  |  |  |  | It is called by both C<set_time> and C<rise_time> to converge on an accurate | 
| 2693 |  |  |  |  |  |  | time of elevation. | 
| 2694 |  |  |  |  |  |  |  | 
| 2695 |  |  |  |  |  |  | $status = $self->_iterative_el( $refel, $grad ); | 
| 2696 |  |  |  |  |  |  |  | 
| 2697 |  |  |  |  |  |  | The required elevation must be supplied (in radians). The second | 
| 2698 |  |  |  |  |  |  | argument indicates whether we are looking for a solution with a | 
| 2699 |  |  |  |  |  |  | positive (source is rising) or negative (source is setting) | 
| 2700 |  |  |  |  |  |  | gradient. +1 indicates a rising source, -1 indicates a setting source. | 
| 2701 |  |  |  |  |  |  |  | 
| 2702 |  |  |  |  |  |  | On entry, the C<datetime> method must return a time that is to be used | 
| 2703 |  |  |  |  |  |  | as the starting point for convergence (the closer the better) On exit, | 
| 2704 |  |  |  |  |  |  | the C<datetime> method will return the calculated time for that | 
| 2705 |  |  |  |  |  |  | elevation. | 
| 2706 |  |  |  |  |  |  |  | 
| 2707 |  |  |  |  |  |  | The algorithm used for this routine is very simple. Try not to call it | 
| 2708 |  |  |  |  |  |  | repeatedly. | 
| 2709 |  |  |  |  |  |  |  | 
| 2710 |  |  |  |  |  |  | Returns undef if the routine did not converge. | 
| 2711 |  |  |  |  |  |  |  | 
| 2712 |  |  |  |  |  |  | WARNING: this method is overriden by one in Astro::Coords::Equatorial | 
| 2713 |  |  |  |  |  |  | if there is no proper motion of parallax.  The overriding method assumes | 
| 2714 |  |  |  |  |  |  | that the starting point has been accurately calculated and therefore | 
| 2715 |  |  |  |  |  |  | does nothing.  As a result this method should not be called without | 
| 2716 |  |  |  |  |  |  | a very good starting point in the case of equatorial coordinates. | 
| 2717 |  |  |  |  |  |  |  | 
| 2718 |  |  |  |  |  |  | =cut | 
| 2719 |  |  |  |  |  |  |  | 
| 2720 |  |  |  |  |  |  | sub _iterative_el { | 
| 2721 | 1101 |  |  | 1101 |  | 1929 | my $self = shift; | 
| 2722 | 1101 |  |  |  |  | 1859 | my $refel = shift; | 
| 2723 | 1101 |  |  |  |  | 1551 | my $grad = shift; | 
| 2724 |  |  |  |  |  |  |  | 
| 2725 |  |  |  |  |  |  | # See what type of date object we are dealing with | 
| 2726 | 1101 |  |  |  |  | 2309 | my $use_dt = $self->_isdt(); | 
| 2727 |  |  |  |  |  |  |  | 
| 2728 |  |  |  |  |  |  | # We are tweaking DateTime objects without the cache knowing about | 
| 2729 |  |  |  |  |  |  | # it (because it is faster than lots of object cloning) so we must | 
| 2730 |  |  |  |  |  |  | # turn off caching | 
| 2731 | 1101 |  |  |  |  | 2218 | my $old_unsafe = $self->datetime_is_unsafe; | 
| 2732 | 1101 |  |  |  |  | 2453 | $self->datetime_is_unsafe( 1 ); | 
| 2733 |  |  |  |  |  |  |  | 
| 2734 |  |  |  |  |  |  | # Calculate current elevation | 
| 2735 | 1101 |  |  |  |  | 2555 | my $el = $self->el; | 
| 2736 |  |  |  |  |  |  |  | 
| 2737 |  |  |  |  |  |  | # Tolerance (in arcseconds) | 
| 2738 | 1101 |  |  |  |  | 2568 | my $tol = 5 * Astro::PAL::DAS2R; | 
| 2739 |  |  |  |  |  |  |  | 
| 2740 |  |  |  |  |  |  | # smallest support increment | 
| 2741 | 1101 |  |  |  |  | 1669 | my $smallinc = 0.2; | 
| 2742 |  |  |  |  |  |  |  | 
| 2743 |  |  |  |  |  |  | # Get the estimated time for this elevation | 
| 2744 | 1101 |  |  |  |  | 2272 | my $time = $self->datetime; | 
| 2745 | 1101 |  |  |  |  | 3340 | my $epoch = $time->epoch(); | 
| 2746 |  |  |  |  |  |  |  | 
| 2747 |  |  |  |  |  |  | # now compare the requested elevation with the actual elevation for the | 
| 2748 |  |  |  |  |  |  | # previously calculated rise time | 
| 2749 | 1101 | 100 |  |  |  | 7503 | if (abs($el - $refel) > $tol ) { | 
| 2750 | 1054 | 50 |  |  |  | 2421 | if ($DEBUG) { | 
| 2751 | 0 |  |  |  |  | 0 | print "# ================================ -> ".$self->name."\n"; | 
| 2752 | 0 |  |  |  |  | 0 | print "# Requested elevation: " . (Astro::PAL::DR2D * $refel) ."\n"; | 
| 2753 | 0 |  |  |  |  | 0 | print "# Elevation out of range: ". $self->el(format => 'deg')."\n"; | 
| 2754 | 0 | 0 |  |  |  | 0 | print "# For " . ($grad > 0 ? "rise" : "set")." time: ". $time->datetime ."\n"; | 
| 2755 |  |  |  |  |  |  | } | 
| 2756 |  |  |  |  |  |  |  | 
| 2757 |  |  |  |  |  |  | # use 1 minute for all except the moon | 
| 2758 | 1054 |  |  |  |  | 1589 | my $inc = 60; # seconds | 
| 2759 | 1054 | 100 | 100 |  |  | 2507 | $inc *= 10 if (defined $self->name && lc($self->name) eq 'moon'); | 
| 2760 | 1054 |  |  |  |  | 2112 | my $maxinc = $inc * 10; | 
| 2761 |  |  |  |  |  |  |  | 
| 2762 | 1054 | 100 |  |  |  | 2377 | my $sign = (($el <=> $refel) != $grad ? 1 : -1); # incrementing or decrementing time | 
| 2763 | 1054 |  |  |  |  | 4720 | my $prevel; # previous elevation | 
| 2764 |  |  |  |  |  |  | my $prevepoch; # 'epoch' value of previous time | 
| 2765 |  |  |  |  |  |  |  | 
| 2766 |  |  |  |  |  |  | # Indicate whether we have straddled the correct answer | 
| 2767 | 1054 |  |  |  |  | 0 | my $has_been_high; | 
| 2768 | 1054 |  |  |  |  | 0 | my $has_been_low; | 
| 2769 | 1054 |  |  |  |  | 0 | my $has_straddled; | 
| 2770 |  |  |  |  |  |  |  | 
| 2771 |  |  |  |  |  |  | # This is a very simple convergence algorithm. | 
| 2772 |  |  |  |  |  |  | # Newton-Raphson is used until we straddle the value, given that the function | 
| 2773 |  |  |  |  |  |  | # is almost linear for most elevations. | 
| 2774 | 1054 |  |  |  |  | 2244 | while (abs($el-$refel) > $tol) { | 
| 2775 | 3435 | 100 |  |  |  | 7576 | if (defined $prevel) { | 
| 2776 |  |  |  |  |  |  |  | 
| 2777 |  |  |  |  |  |  | # should check sign of gradient to make sure we are not | 
| 2778 |  |  |  |  |  |  | # running away to an incorrect gradient. Note that this function | 
| 2779 |  |  |  |  |  |  | # can be highly curved if we are near ante transit. | 
| 2780 |  |  |  |  |  |  |  | 
| 2781 |  |  |  |  |  |  | # There are two constraints that have to be used to control | 
| 2782 |  |  |  |  |  |  | # the convergence | 
| 2783 |  |  |  |  |  |  | #  - make sure we are not diverging with each iteration | 
| 2784 |  |  |  |  |  |  | #  - make sure we are not bouncing around missing the final | 
| 2785 |  |  |  |  |  |  | #    value due to inaccuracies in the time resolution (this is | 
| 2786 |  |  |  |  |  |  | #    important for fast moving objects like the sun). Currently | 
| 2787 |  |  |  |  |  |  | #    we are not really doing anything useful by tweaking by less | 
| 2788 |  |  |  |  |  |  | #    than a second | 
| 2789 |  |  |  |  |  |  |  | 
| 2790 |  |  |  |  |  |  | # The problem is that we can not stop on bracketing the correct | 
| 2791 |  |  |  |  |  |  | # value if we are never going to reach the value itself because | 
| 2792 |  |  |  |  |  |  | # it never rises or never sets | 
| 2793 |  |  |  |  |  |  |  | 
| 2794 |  |  |  |  |  |  | #          \   / | 
| 2795 |  |  |  |  |  |  | #           \ / | 
| 2796 |  |  |  |  |  |  | #            -   <--  minimum elevation | 
| 2797 |  |  |  |  |  |  | #                <--  required elevation | 
| 2798 |  |  |  |  |  |  |  | 
| 2799 |  |  |  |  |  |  | # in the above case each step will diverge but will never | 
| 2800 |  |  |  |  |  |  | # straddle | 
| 2801 |  |  |  |  |  |  |  | 
| 2802 |  |  |  |  |  |  | # The solution is to first iterate by using a divergence | 
| 2803 |  |  |  |  |  |  | # criterion. Then, if we determine that we have straddled the | 
| 2804 |  |  |  |  |  |  | # correct result, we switch to a stopping criterion that | 
| 2805 |  |  |  |  |  |  | # uses the time resolution if we never fully converge (the | 
| 2806 |  |  |  |  |  |  | # important thing is to return a time not an elevation) | 
| 2807 |  |  |  |  |  |  |  | 
| 2808 | 2381 |  |  |  |  | 5101 | my $diff_to_prev = $prevel - $refel; | 
| 2809 | 2381 |  |  |  |  | 5106 | my $diff_to_curr = $el - $refel; | 
| 2810 |  |  |  |  |  |  |  | 
| 2811 |  |  |  |  |  |  | # set the straddle parameters | 
| 2812 | 2381 | 100 |  |  |  | 5675 | if ($diff_to_curr < 0) { | 
| 2813 | 909 |  |  |  |  | 1411 | $has_been_low = 1; | 
| 2814 |  |  |  |  |  |  | } else { | 
| 2815 | 1472 |  |  |  |  | 2717 | $has_been_high = 1; | 
| 2816 |  |  |  |  |  |  | } | 
| 2817 |  |  |  |  |  |  |  | 
| 2818 |  |  |  |  |  |  | # if we have straddled, stop on increment being small | 
| 2819 |  |  |  |  |  |  |  | 
| 2820 |  |  |  |  |  |  | # now determine whether we should reverse | 
| 2821 |  |  |  |  |  |  | # if we know we are bouncing around the correct value | 
| 2822 |  |  |  |  |  |  | # we can reverse as soon as the previous el and the current el | 
| 2823 |  |  |  |  |  |  | # are either side of the correct answer | 
| 2824 | 2381 |  |  |  |  | 3196 | my $reverse; | 
| 2825 | 2381 | 100 |  |  |  | 4121 | if ($has_straddled) { | 
| 2826 |  |  |  |  |  |  | # we can abort if we are below the time resolution | 
| 2827 | 52 | 100 |  |  |  | 164 | last if $inc < $smallinc; | 
| 2828 |  |  |  |  |  |  |  | 
| 2829 |  |  |  |  |  |  | # reverse | 
| 2830 | 50 | 100 |  |  |  | 147 | $reverse = 1 if ($diff_to_prev / $diff_to_curr < 0); | 
| 2831 |  |  |  |  |  |  | } else { | 
| 2832 |  |  |  |  |  |  | # abort if the inc is too small | 
| 2833 | 2329 | 100 |  |  |  | 5002 | if ($inc < $smallinc) { | 
| 2834 | 1 |  |  |  |  | 134 | $self->datetime_is_unsafe( $old_unsafe ); | 
| 2835 | 1 |  |  |  |  | 13 | return undef; | 
| 2836 |  |  |  |  |  |  | } | 
| 2837 |  |  |  |  |  |  |  | 
| 2838 | 2328 |  |  |  |  | 3619 | my $deltat = $epoch - $prevepoch; | 
| 2839 | 2328 |  |  |  |  | 4686 | my $deltael = $el - $prevel; | 
| 2840 | 2328 | 100 | 66 |  |  | 9315 | if (abs($deltat) > 0 and abs($deltael) > 0) { | 
| 2841 |  |  |  |  |  |  | # in the linear approximation | 
| 2842 |  |  |  |  |  |  | # we know the gradient | 
| 2843 | 2324 |  |  |  |  | 4088 | my $gradient = $deltael / $deltat; | 
| 2844 | 2324 |  |  |  |  | 3777 | my $newinc = - ($diff_to_curr) / $gradient; | 
| 2845 | 2324 |  |  |  |  | 3808 | my $newsign = $newinc <=> 0; | 
| 2846 | 2324 |  |  |  |  | 3234 | $newinc = abs($newinc); | 
| 2847 |  |  |  |  |  |  |  | 
| 2848 | 2324 | 100 |  |  |  | 4143 | if ($newinc < $maxinc) { | 
| 2849 | 1938 |  |  |  |  | 2676 | $sign = $newsign; | 
| 2850 | 1938 |  |  |  |  | 2623 | $inc = $newinc; | 
| 2851 | 1938 | 50 |  |  |  | 4188 | $inc = $smallinc if $inc < $smallinc; | 
| 2852 |  |  |  |  |  |  | } else { | 
| 2853 |  |  |  |  |  |  | # The gradient approach might be running away, | 
| 2854 |  |  |  |  |  |  | # so duplicate the non-gradient behaviour instead. | 
| 2855 | 386 | 100 |  |  |  | 1086 | $reverse = 1 if abs($diff_to_prev) < abs($diff_to_curr); | 
| 2856 |  |  |  |  |  |  | } | 
| 2857 |  |  |  |  |  |  | } else { | 
| 2858 |  |  |  |  |  |  | # if we have not straddled, we reverse if the previous el | 
| 2859 |  |  |  |  |  |  | # is closer than this el | 
| 2860 | 4 | 100 |  |  |  | 20 | $reverse = 1 if abs($diff_to_prev) < abs($diff_to_curr); | 
| 2861 |  |  |  |  |  |  | } | 
| 2862 |  |  |  |  |  |  | } | 
| 2863 |  |  |  |  |  |  |  | 
| 2864 |  |  |  |  |  |  | # Defer straddled test so we have one gradient-based step past straddling. | 
| 2865 | 2378 |  | 100 |  |  | 5625 | $has_straddled = $has_been_low && $has_been_high; | 
| 2866 |  |  |  |  |  |  |  | 
| 2867 |  |  |  |  |  |  | # reverse | 
| 2868 | 2378 | 100 |  |  |  | 4558 | if ( $reverse ) { | 
| 2869 |  |  |  |  |  |  |  | 
| 2870 |  |  |  |  |  |  | # the gap between the previous measurement and the reference | 
| 2871 |  |  |  |  |  |  | # is smaller than the current gap. We seem to be diverging. | 
| 2872 |  |  |  |  |  |  | # Change direction | 
| 2873 | 241 |  |  |  |  | 364 | $sign *= -1; | 
| 2874 |  |  |  |  |  |  | # and use half the step size | 
| 2875 | 241 |  |  |  |  | 387 | $inc /= 3; | 
| 2876 |  |  |  |  |  |  | } | 
| 2877 |  |  |  |  |  |  | } | 
| 2878 |  |  |  |  |  |  |  | 
| 2879 |  |  |  |  |  |  | # Now calculate a new time | 
| 2880 | 3432 |  |  |  |  | 5706 | my $delta = $sign * $inc; | 
| 2881 | 3432 | 100 |  |  |  | 5819 | if (!$use_dt) { | 
| 2882 | 36 |  |  |  |  | 95 | $time = $time + $delta; | 
| 2883 |  |  |  |  |  |  | # we have created a new object so need to store it for next time | 
| 2884 |  |  |  |  |  |  | # round | 
| 2885 | 36 |  |  |  |  | 2178 | $self->datetime( $time ); | 
| 2886 |  |  |  |  |  |  | } else { | 
| 2887 |  |  |  |  |  |  | # increment the time (this happens in place so we do not need to | 
| 2888 |  |  |  |  |  |  | # register the change with the datetime method | 
| 2889 | 3396 | 100 |  |  |  | 6213 | if (abs($delta) > 1) { | 
| 2890 | 2835 |  |  |  |  | 15090 | $time->add( seconds => "$delta" ); | 
| 2891 |  |  |  |  |  |  | } else { | 
| 2892 | 561 |  |  |  |  | 1884 | $time->add( nanoseconds => ( $delta * 1E9 ) ); | 
| 2893 |  |  |  |  |  |  | } | 
| 2894 |  |  |  |  |  |  |  | 
| 2895 |  |  |  |  |  |  | } | 
| 2896 |  |  |  |  |  |  | # recalculate the elevation, storing the previous as reference | 
| 2897 | 3432 |  |  |  |  | 3168542 | $prevel = $el; | 
| 2898 | 3432 |  |  |  |  | 11813 | $el = $self->el; | 
| 2899 | 3432 | 50 |  |  |  | 10142 | print "# New elevation: ". $self->el(format=>'deg')." \t@ ".$time->datetime." with delta $delta sec\n" | 
| 2900 |  |  |  |  |  |  | if $DEBUG; | 
| 2901 | 3432 |  |  |  |  | 5480 | $prevepoch = $epoch; | 
| 2902 | 3432 |  |  |  |  | 9767 | $epoch = $time->epoch(); | 
| 2903 |  |  |  |  |  |  | } | 
| 2904 |  |  |  |  |  |  |  | 
| 2905 |  |  |  |  |  |  | } | 
| 2906 |  |  |  |  |  |  |  | 
| 2907 |  |  |  |  |  |  | # reset the unsafeness level | 
| 2908 | 1100 |  |  |  |  | 3643 | $self->datetime_is_unsafe( $old_unsafe ); | 
| 2909 |  |  |  |  |  |  |  | 
| 2910 | 1100 |  |  |  |  | 3711 | return 1; | 
| 2911 |  |  |  |  |  |  | } | 
| 2912 |  |  |  |  |  |  |  | 
| 2913 |  |  |  |  |  |  | =item B<_isdt> | 
| 2914 |  |  |  |  |  |  |  | 
| 2915 |  |  |  |  |  |  | Internal method. Returns true if the C<datetime> method contains a | 
| 2916 |  |  |  |  |  |  | DateTime object. Returns false otherwise (assumed to be | 
| 2917 |  |  |  |  |  |  | Time::Piece). If an optional argument is supplied that argument is | 
| 2918 |  |  |  |  |  |  | tested instead. | 
| 2919 |  |  |  |  |  |  |  | 
| 2920 |  |  |  |  |  |  | $isdt = $self->_isdt(); | 
| 2921 |  |  |  |  |  |  | $isdt = $self->_isdt( $dt ); | 
| 2922 |  |  |  |  |  |  |  | 
| 2923 |  |  |  |  |  |  | =cut | 
| 2924 |  |  |  |  |  |  |  | 
| 2925 |  |  |  |  |  |  | sub _isdt { | 
| 2926 | 11678 |  |  | 11678 |  | 16334 | my $self = shift; | 
| 2927 | 11678 |  |  |  |  | 15700 | my $test = shift; | 
| 2928 |  |  |  |  |  |  |  | 
| 2929 | 11678 | 50 |  |  |  | 28467 | $test = $self->datetime unless defined $test; | 
| 2930 |  |  |  |  |  |  |  | 
| 2931 |  |  |  |  |  |  | # Check Time::Piece first since there is a possibility that | 
| 2932 |  |  |  |  |  |  | # this is really a subclass of DateTime | 
| 2933 | 11678 |  |  |  |  | 16928 | my $dtime; | 
| 2934 | 11678 | 100 |  |  |  | 58824 | if ($test->isa( "Time::Piece")) { | 
|  |  | 50 |  |  |  |  |  | 
| 2935 | 151 |  |  |  |  | 217 | $dtime = 0; | 
| 2936 |  |  |  |  |  |  | } elsif ($test->isa("DateTime")) { | 
| 2937 | 11527 |  |  |  |  | 16406 | $dtime = 1; | 
| 2938 |  |  |  |  |  |  | } else { | 
| 2939 | 0 |  |  |  |  | 0 | croak "Unknown DateTime object class ". blessed($test); | 
| 2940 |  |  |  |  |  |  | } | 
| 2941 |  |  |  |  |  |  |  | 
| 2942 | 11678 |  |  |  |  | 22778 | return $dtime; | 
| 2943 |  |  |  |  |  |  | } | 
| 2944 |  |  |  |  |  |  |  | 
| 2945 |  |  |  |  |  |  | =item B<_extract_event> | 
| 2946 |  |  |  |  |  |  |  | 
| 2947 |  |  |  |  |  |  | Parse an options hash to determine the correct value of the "event" key | 
| 2948 |  |  |  |  |  |  | given backwards compatibility requirements. | 
| 2949 |  |  |  |  |  |  |  | 
| 2950 |  |  |  |  |  |  | $event = $self->_extract_event( %opt ); | 
| 2951 |  |  |  |  |  |  |  | 
| 2952 |  |  |  |  |  |  | - "nearest" trumps "event" if "nearest" is true. Returns event=0 | 
| 2953 |  |  |  |  |  |  |  | 
| 2954 |  |  |  |  |  |  | - If "event" is not defined, it defaults to "1" | 
| 2955 |  |  |  |  |  |  |  | 
| 2956 |  |  |  |  |  |  | - If "nearest" is false, "event" is used unless "event"=0 in | 
| 2957 |  |  |  |  |  |  | which case "nearest" trumps "event" and returns "1". | 
| 2958 |  |  |  |  |  |  |  | 
| 2959 |  |  |  |  |  |  | =cut | 
| 2960 |  |  |  |  |  |  |  | 
| 2961 |  |  |  |  |  |  | sub _extract_event { | 
| 2962 | 5104 |  |  | 5104 |  | 7122 | my $self = shift; | 
| 2963 |  |  |  |  |  |  |  | 
| 2964 |  |  |  |  |  |  | # make sure the $opt{event} always exists | 
| 2965 | 5104 |  |  |  |  | 7385 | my $default = 1; | 
| 2966 | 5104 |  |  |  |  | 13563 | my %opt = (event => $default, @_); | 
| 2967 |  |  |  |  |  |  |  | 
| 2968 | 5104 | 100 |  |  |  | 10665 | if (exists $opt{nearest}) { | 
| 2969 | 5 | 50 |  |  |  | 14 | if ($opt{nearest}) { | 
| 2970 |  |  |  |  |  |  | # request for nearest | 
| 2971 | 5 |  |  |  |  | 14 | return 0; | 
| 2972 |  |  |  |  |  |  | } else { | 
| 2973 |  |  |  |  |  |  | # request for not nearest | 
| 2974 | 0 | 0 |  |  |  | 0 | if ($opt{event} == 0) { | 
| 2975 |  |  |  |  |  |  | # this is incompatible with nearest=0 so return default | 
| 2976 | 0 |  |  |  |  | 0 | return $default; | 
| 2977 |  |  |  |  |  |  | } else { | 
| 2978 | 0 |  |  |  |  | 0 | return $opt{event}; | 
| 2979 |  |  |  |  |  |  | } | 
| 2980 |  |  |  |  |  |  | } | 
| 2981 |  |  |  |  |  |  |  | 
| 2982 |  |  |  |  |  |  | } else { | 
| 2983 | 5099 |  |  |  |  | 13415 | return $opt{event}; | 
| 2984 |  |  |  |  |  |  | } | 
| 2985 |  |  |  |  |  |  | } | 
| 2986 |  |  |  |  |  |  |  | 
| 2987 |  |  |  |  |  |  | =item B<_normalise_vframe> | 
| 2988 |  |  |  |  |  |  |  | 
| 2989 |  |  |  |  |  |  | Convert an input string representing a velocity frame, to | 
| 2990 |  |  |  |  |  |  | a standardised form recognized by the software. In most cases, | 
| 2991 |  |  |  |  |  |  | the string is upper cased and reduced two the first 3 characters. | 
| 2992 |  |  |  |  |  |  | LSRK and LSRD are special-cased. LSR is converted to LSRK. | 
| 2993 |  |  |  |  |  |  |  | 
| 2994 |  |  |  |  |  |  | $frame = $c->_normalise_vframe( $in ); | 
| 2995 |  |  |  |  |  |  |  | 
| 2996 |  |  |  |  |  |  | Unrecognized or undefined frames trigger an exception. | 
| 2997 |  |  |  |  |  |  |  | 
| 2998 |  |  |  |  |  |  | =cut | 
| 2999 |  |  |  |  |  |  |  | 
| 3000 |  |  |  |  |  |  | sub _normalise_vframe { | 
| 3001 | 10 |  |  | 10 |  | 13 | my $self = shift; | 
| 3002 | 10 |  |  |  |  | 16 | my $in = shift; | 
| 3003 |  |  |  |  |  |  |  | 
| 3004 | 10 | 50 |  |  |  | 26 | croak "Velocity frame not defined. Can not normalise" unless defined $in; | 
| 3005 |  |  |  |  |  |  |  | 
| 3006 |  |  |  |  |  |  | # upper case | 
| 3007 | 10 |  |  |  |  | 14 | $in = uc( $in ); | 
| 3008 |  |  |  |  |  |  |  | 
| 3009 |  |  |  |  |  |  | # LSRK or LSRD need no normalisation | 
| 3010 | 10 | 100 | 100 |  |  | 42 | return $in if ($in eq 'LSRK' || $in eq 'LSRD' || $in eq 'LG'); | 
|  |  |  | 66 |  |  |  |  | 
| 3011 |  |  |  |  |  |  |  | 
| 3012 |  |  |  |  |  |  | # Truncate | 
| 3013 | 6 |  |  |  |  | 12 | my $trunc = substr( $in, 0, 3 ); | 
| 3014 |  |  |  |  |  |  |  | 
| 3015 |  |  |  |  |  |  | # Verify | 
| 3016 | 6 | 50 |  |  |  | 28 | croak "Unrecognized velocity frame '$trunc'" | 
| 3017 |  |  |  |  |  |  | unless $trunc =~ /^(GEO|TOP|HEL|LSR|GAL|BAR)/; | 
| 3018 |  |  |  |  |  |  |  | 
| 3019 |  |  |  |  |  |  | # special case | 
| 3020 | 6 | 100 |  |  |  | 13 | $trunc = 'LSRK' if $trunc eq 'LSR'; | 
| 3021 |  |  |  |  |  |  |  | 
| 3022 |  |  |  |  |  |  | # okay | 
| 3023 | 6 |  |  |  |  | 14 | return $trunc; | 
| 3024 |  |  |  |  |  |  | } | 
| 3025 |  |  |  |  |  |  |  | 
| 3026 |  |  |  |  |  |  | =item B<_normalise_vdefn> | 
| 3027 |  |  |  |  |  |  |  | 
| 3028 |  |  |  |  |  |  | Convert an input string representing a velocity definition, to | 
| 3029 |  |  |  |  |  |  | a standardised form recognized by the software. In all cases the | 
| 3030 |  |  |  |  |  |  | string is truncated to 3 characters and upper-cased before validating | 
| 3031 |  |  |  |  |  |  | against known types. | 
| 3032 |  |  |  |  |  |  |  | 
| 3033 |  |  |  |  |  |  | $defn = $c->_normalise_vdefn( $in ); | 
| 3034 |  |  |  |  |  |  |  | 
| 3035 |  |  |  |  |  |  | Unrecognized or undefined frames trigger an exception. | 
| 3036 |  |  |  |  |  |  |  | 
| 3037 |  |  |  |  |  |  | =cut | 
| 3038 |  |  |  |  |  |  |  | 
| 3039 |  |  |  |  |  |  | sub _normalise_vdefn { | 
| 3040 | 2 |  |  | 2 |  | 4 | my $self = shift; | 
| 3041 | 2 |  |  |  |  | 3 | my $in = shift; | 
| 3042 |  |  |  |  |  |  |  | 
| 3043 | 2 | 50 |  |  |  | 4 | croak "Velocity definition not defined. Can not normalise" unless defined $in; | 
| 3044 |  |  |  |  |  |  |  | 
| 3045 |  |  |  |  |  |  | # upper case | 
| 3046 | 2 |  |  |  |  | 5 | $in = uc( $in ); | 
| 3047 |  |  |  |  |  |  |  | 
| 3048 |  |  |  |  |  |  | # Truncate | 
| 3049 | 2 |  |  |  |  | 4 | my $trunc = substr( $in, 0, 3 ); | 
| 3050 |  |  |  |  |  |  |  | 
| 3051 |  |  |  |  |  |  | # Verify | 
| 3052 | 2 | 100 |  |  |  | 14 | if ($trunc eq 'RAD') { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 3053 | 1 |  |  |  |  | 3 | return 'RADIO'; | 
| 3054 |  |  |  |  |  |  | } elsif ($trunc eq 'OPT') { | 
| 3055 | 0 |  |  |  |  | 0 | return 'OPTICAL'; | 
| 3056 |  |  |  |  |  |  | } elsif ($trunc eq 'RED') { | 
| 3057 | 1 |  |  |  |  | 4 | return 'REDSHIFT'; | 
| 3058 |  |  |  |  |  |  | } elsif ($trunc eq 'REL') { | 
| 3059 | 0 |  |  |  |  | 0 | return 'RELATIVISTIC'; | 
| 3060 |  |  |  |  |  |  | } else { | 
| 3061 | 0 |  |  |  |  | 0 | croak "Unrecognized velocity definition '$trunc'"; | 
| 3062 |  |  |  |  |  |  | } | 
| 3063 |  |  |  |  |  |  | } | 
| 3064 |  |  |  |  |  |  |  | 
| 3065 |  |  |  |  |  |  | =item B<_parse_equinox> | 
| 3066 |  |  |  |  |  |  |  | 
| 3067 |  |  |  |  |  |  | Given an equinox string of the form JYYYY.frac or BYYYY.frac | 
| 3068 |  |  |  |  |  |  | return the epoch of the equinox and the system of the equinox. | 
| 3069 |  |  |  |  |  |  |  | 
| 3070 |  |  |  |  |  |  | ($system, $epoch ) = $c->_parse_equinox( 'B1920.34' ); | 
| 3071 |  |  |  |  |  |  |  | 
| 3072 |  |  |  |  |  |  | If no leading letter, Julian epoch is assumed. If the string does not | 
| 3073 |  |  |  |  |  |  | match the reuquired pattern, J2000 will be assumed and a warning will | 
| 3074 |  |  |  |  |  |  | be issued. | 
| 3075 |  |  |  |  |  |  |  | 
| 3076 |  |  |  |  |  |  | System is returned as 'FK4' for Besselian epoch and 'FK5' for | 
| 3077 |  |  |  |  |  |  | Julian epoch. | 
| 3078 |  |  |  |  |  |  |  | 
| 3079 |  |  |  |  |  |  | =cut | 
| 3080 |  |  |  |  |  |  |  | 
| 3081 |  |  |  |  |  |  | sub _parse_equinox { | 
| 3082 | 116 |  |  | 116 |  | 191 | my $self = shift; | 
| 3083 | 116 |  |  |  |  | 174 | my $str = shift; | 
| 3084 | 116 |  |  |  |  | 265 | my ($sys, $epoch) = ('FK5', 2000.0); | 
| 3085 | 116 | 50 |  |  |  | 773 | if ($str =~ /^([BJ]?)(\d+(\.\d+)?)$/i) { | 
| 3086 | 116 |  |  |  |  | 327 | my $typ = $1; | 
| 3087 | 116 | 100 |  |  |  | 277 | $sys = ($typ eq 'B' ? 'FK4' : 'FK5' ); | 
| 3088 | 116 |  |  |  |  | 255 | $epoch = $2; | 
| 3089 |  |  |  |  |  |  | } else { | 
| 3090 | 0 |  |  |  |  | 0 | warnings::warnif( "Supplied equinox '$str' does not look like an equinox"); | 
| 3091 |  |  |  |  |  |  | } | 
| 3092 | 116 |  |  |  |  | 443 | return ($sys, $epoch); | 
| 3093 |  |  |  |  |  |  | } | 
| 3094 |  |  |  |  |  |  |  | 
| 3095 |  |  |  |  |  |  | =item B<_j2000_to_byyyy> | 
| 3096 |  |  |  |  |  |  |  | 
| 3097 |  |  |  |  |  |  | Since we always store in J2000 internally, converting between | 
| 3098 |  |  |  |  |  |  | different Julian equinoxes is straightforward. This routine takes a | 
| 3099 |  |  |  |  |  |  | J2000 coordinate pair (with proper motions and parallax already | 
| 3100 |  |  |  |  |  |  | applied) and converts them to Besselian equinox for the current epoch. | 
| 3101 |  |  |  |  |  |  |  | 
| 3102 |  |  |  |  |  |  | ($bra, $bdec) = $c->_j2000_to_BYYY( $equinox, $ra2000, $dec2000); | 
| 3103 |  |  |  |  |  |  |  | 
| 3104 |  |  |  |  |  |  | The equinox is the epoch year. It is assumed to be Besselian. | 
| 3105 |  |  |  |  |  |  |  | 
| 3106 |  |  |  |  |  |  | =cut | 
| 3107 |  |  |  |  |  |  |  | 
| 3108 |  |  |  |  |  |  | sub _j2000_to_byyyy { | 
| 3109 | 2 |  |  | 2 |  | 4 | my $self = shift; | 
| 3110 | 2 |  |  |  |  | 4 | my ($equ, $ra2000, $dec2000) = @_; | 
| 3111 |  |  |  |  |  |  |  | 
| 3112 |  |  |  |  |  |  | # First to 1950 | 
| 3113 | 2 |  |  |  |  | 6 | my ($rb, $db, $drb, $drd) = Astro::PAL::palFk54z($ra2000, $dec2000, | 
| 3114 |  |  |  |  |  |  | Astro::PAL::palEpb( $self->_mjd_tt ) ); | 
| 3115 |  |  |  |  |  |  |  | 
| 3116 |  |  |  |  |  |  | # Then preces to reference epoch frame | 
| 3117 |  |  |  |  |  |  | # I do not know whether fictitious proper motions should be included | 
| 3118 |  |  |  |  |  |  | # here with palPm or whether it is enough to use non-1950 epoch | 
| 3119 |  |  |  |  |  |  | # in palFk54z and then preces 1950 to the same epoch. Not enough test | 
| 3120 |  |  |  |  |  |  | # data for this rare case. | 
| 3121 | 2 | 100 |  |  |  | 9 | if ($equ != 1950) { | 
| 3122 |  |  |  |  |  |  | # Add E-terms | 
| 3123 | 1 |  |  |  |  | 6 | my ($rnoe, $dnoe) = Astro::PAL::palSubet( $rb, $db, 1950.0 ); | 
| 3124 |  |  |  |  |  |  |  | 
| 3125 |  |  |  |  |  |  | # preces | 
| 3126 | 1 |  |  |  |  | 8 | ($rnoe, $dnoe) = Astro::PAL::palPreces( 'FK4', 1950, $equ, $rnoe, $dnoe); | 
| 3127 |  |  |  |  |  |  |  | 
| 3128 |  |  |  |  |  |  | # Add E-terms | 
| 3129 | 1 |  |  |  |  | 5 | ($rb, $db) = Astro::PAL::palAddet( $rnoe, $dnoe, $equ ); | 
| 3130 |  |  |  |  |  |  |  | 
| 3131 |  |  |  |  |  |  | } | 
| 3132 | 2 |  |  |  |  | 7 | return ($rb, $db); | 
| 3133 |  |  |  |  |  |  | } | 
| 3134 |  |  |  |  |  |  |  | 
| 3135 |  |  |  |  |  |  | =item B<_sidereal_period> | 
| 3136 |  |  |  |  |  |  |  | 
| 3137 |  |  |  |  |  |  | Returns the length of the source's "day" in seconds, i.e. how long it takes | 
| 3138 |  |  |  |  |  |  | return to the same position in the sky.  This implementation | 
| 3139 |  |  |  |  |  |  | returns one sidereal day, but it must be overriden for fast-moving | 
| 3140 |  |  |  |  |  |  | objects such as the Sun and the Moon. | 
| 3141 |  |  |  |  |  |  |  | 
| 3142 |  |  |  |  |  |  | To be used internally for rise / set time calculation. | 
| 3143 |  |  |  |  |  |  |  | 
| 3144 |  |  |  |  |  |  | =cut | 
| 3145 |  |  |  |  |  |  |  | 
| 3146 |  |  |  |  |  |  | sub _sidereal_period { | 
| 3147 | 3488 |  |  | 3488 |  | 7082 | return 24 * 3600 * 365.2422/366.2422; | 
| 3148 |  |  |  |  |  |  | } | 
| 3149 |  |  |  |  |  |  |  | 
| 3150 |  |  |  |  |  |  | =back | 
| 3151 |  |  |  |  |  |  |  | 
| 3152 |  |  |  |  |  |  | =head2 Caching Routines | 
| 3153 |  |  |  |  |  |  |  | 
| 3154 |  |  |  |  |  |  | These methods provide a means of caching calculated answers for a fixed | 
| 3155 |  |  |  |  |  |  | epoch. It is usually faster to lookup a pre-calculated value for a given | 
| 3156 |  |  |  |  |  |  | time than it is to calculate that time. | 
| 3157 |  |  |  |  |  |  |  | 
| 3158 |  |  |  |  |  |  | The cache is per-object. | 
| 3159 |  |  |  |  |  |  |  | 
| 3160 |  |  |  |  |  |  | =over 4 | 
| 3161 |  |  |  |  |  |  |  | 
| 3162 |  |  |  |  |  |  | =item B<_cache_write> | 
| 3163 |  |  |  |  |  |  |  | 
| 3164 |  |  |  |  |  |  | Add the supplied values to the cache. Values can be provided in a hash. | 
| 3165 |  |  |  |  |  |  | The choice of key names is up to the caller. | 
| 3166 |  |  |  |  |  |  |  | 
| 3167 |  |  |  |  |  |  | $c->_cache_write( AZ => $az, EL => $el ); | 
| 3168 |  |  |  |  |  |  |  | 
| 3169 |  |  |  |  |  |  | Nothing is stored if the date stored in the object is not fixed. | 
| 3170 |  |  |  |  |  |  |  | 
| 3171 |  |  |  |  |  |  | =cut | 
| 3172 |  |  |  |  |  |  |  | 
| 3173 |  |  |  |  |  |  | sub _cache_write { | 
| 3174 | 25899 |  |  | 25899 |  | 39804 | my $self = shift; | 
| 3175 | 25899 |  |  |  |  | 70726 | my %add = @_; | 
| 3176 |  |  |  |  |  |  |  | 
| 3177 | 25899 |  |  |  |  | 54601 | my $primary = $self->_cache_key; | 
| 3178 | 25899 | 100 |  |  |  | 80912 | return () unless defined $primary; | 
| 3179 |  |  |  |  |  |  |  | 
| 3180 | 7741 |  |  |  |  | 15807 | my $C = $self->_cache_ref; | 
| 3181 |  |  |  |  |  |  |  | 
| 3182 | 7741 | 100 |  |  |  | 15848 | if (!exists $C->{$primary}) { | 
| 3183 | 4486 |  |  |  |  | 10305 | $C->{$primary} = {}; | 
| 3184 |  |  |  |  |  |  | } | 
| 3185 |  |  |  |  |  |  |  | 
| 3186 | 7741 |  |  |  |  | 11971 | my $local = $C->{$primary}; | 
| 3187 |  |  |  |  |  |  |  | 
| 3188 | 7741 |  |  |  |  | 20074 | for my $key (keys %add) { | 
| 3189 | 13851 |  |  |  |  | 26000 | $local->{$key} = $add{$key}; | 
| 3190 |  |  |  |  |  |  | } | 
| 3191 | 7741 |  |  |  |  | 21945 | return; | 
| 3192 |  |  |  |  |  |  | } | 
| 3193 |  |  |  |  |  |  |  | 
| 3194 |  |  |  |  |  |  | =item B<_cache_read> | 
| 3195 |  |  |  |  |  |  |  | 
| 3196 |  |  |  |  |  |  | Retrieve value(s) from the cache. Returns undef if no value is available. | 
| 3197 |  |  |  |  |  |  |  | 
| 3198 |  |  |  |  |  |  | ($az, $el) = $c->_cache_read( "AZ", "EL" ); | 
| 3199 |  |  |  |  |  |  |  | 
| 3200 |  |  |  |  |  |  | In scalar context, returns the first value. | 
| 3201 |  |  |  |  |  |  |  | 
| 3202 |  |  |  |  |  |  | =cut | 
| 3203 |  |  |  |  |  |  |  | 
| 3204 |  |  |  |  |  |  | sub _cache_read { | 
| 3205 | 53079 |  |  | 53079 |  | 71696 | my $self = shift; | 
| 3206 | 53079 |  |  |  |  | 94614 | my @keys = @_; | 
| 3207 |  |  |  |  |  |  |  | 
| 3208 | 53079 |  |  |  |  | 85029 | my $primary = $self->_cache_key; | 
| 3209 | 53079 | 100 |  |  |  | 121347 | return () unless defined $primary; | 
| 3210 |  |  |  |  |  |  |  | 
| 3211 | 21322 |  |  |  |  | 36670 | my $C = $self->_cache_ref; | 
| 3212 |  |  |  |  |  |  |  | 
| 3213 | 21322 | 100 |  |  |  | 54684 | return () unless exists $C->{$primary}; | 
| 3214 | 7515 |  |  |  |  | 11862 | my $local = $C->{$primary}; | 
| 3215 | 7515 | 50 |  |  |  | 12923 | return () unless defined $local; | 
| 3216 |  |  |  |  |  |  |  | 
| 3217 | 7515 |  |  |  |  | 14380 | my @answer =  map { $local->{$_} } @keys; | 
|  | 7555 |  |  |  |  | 23039 |  | 
| 3218 |  |  |  |  |  |  | #  print "Caller: ". join(":", caller() ) ."\n"; | 
| 3219 |  |  |  |  |  |  | #  print "Getting cache values for ". join(":",@keys) ."\n"; | 
| 3220 |  |  |  |  |  |  | #  print "Getting cache values for ". join(":",@answer) ."\n"; | 
| 3221 | 7515 | 100 |  |  |  | 21207 | return (wantarray() ? @answer : $answer[0] ); | 
| 3222 |  |  |  |  |  |  | } | 
| 3223 |  |  |  |  |  |  |  | 
| 3224 |  |  |  |  |  |  | =item B<_calc_cache_key> | 
| 3225 |  |  |  |  |  |  |  | 
| 3226 |  |  |  |  |  |  | Internal (to the cache system) function for calculating the cache | 
| 3227 |  |  |  |  |  |  | key for the current epoch. | 
| 3228 |  |  |  |  |  |  |  | 
| 3229 |  |  |  |  |  |  | $key = $c->_calc_cache_key; | 
| 3230 |  |  |  |  |  |  |  | 
| 3231 |  |  |  |  |  |  | Use the _cache_key() method to return the result. | 
| 3232 |  |  |  |  |  |  |  | 
| 3233 |  |  |  |  |  |  | Caching is disabled if C<datetime_is_unsafe>, C<usenow> or no | 
| 3234 |  |  |  |  |  |  | DateTime object is available. | 
| 3235 |  |  |  |  |  |  |  | 
| 3236 |  |  |  |  |  |  | =cut | 
| 3237 |  |  |  |  |  |  |  | 
| 3238 |  |  |  |  |  |  | sub _calc_cache_key { | 
| 3239 | 16067 |  |  | 16067 |  | 21914 | my $self = shift; | 
| 3240 |  |  |  |  |  |  | #  return; # This will disable caching | 
| 3241 |  |  |  |  |  |  |  | 
| 3242 |  |  |  |  |  |  | # if we have a floating clock we do not want to cache | 
| 3243 | 16067 | 100 | 66 |  |  | 30978 | if (!$self->has_datetime || $self->usenow || $self->datetime_is_unsafe) { | 
|  |  |  | 100 |  |  |  |  | 
| 3244 | 2658 |  |  |  |  | 6602 | $self->_cache_key( undef ); | 
| 3245 | 2658 |  |  |  |  | 4413 | return; | 
| 3246 |  |  |  |  |  |  | } | 
| 3247 |  |  |  |  |  |  |  | 
| 3248 |  |  |  |  |  |  | # The cache key currently uses the time and the telescope name | 
| 3249 | 13409 |  |  |  |  | 30688 | my $dt = $self->datetime; | 
| 3250 | 13409 |  |  |  |  | 27694 | my $tel = $self->telescope; | 
| 3251 | 13409 | 100 |  |  |  | 42810 | my $telName = (defined $tel ? $tel->name : "NONE" ); | 
| 3252 |  |  |  |  |  |  |  | 
| 3253 | 13409 | 50 |  |  |  | 86259 | print "# Calculating cache key using $telName and ". $dt->epoch ."\n" | 
| 3254 |  |  |  |  |  |  | if $DEBUG; | 
| 3255 |  |  |  |  |  |  |  | 
| 3256 |  |  |  |  |  |  | # usually epoch is quicker to generate than ISO date but we have to | 
| 3257 |  |  |  |  |  |  | # be careful about fractional seconds in DateTime (Time::Piece can | 
| 3258 |  |  |  |  |  |  | # not do it) | 
| 3259 |  |  |  |  |  |  |  | 
| 3260 | 13409 |  |  |  |  | 20776 | my $addendum = ""; | 
| 3261 | 13409 | 100 |  |  |  | 54599 | $addendum = $dt->nanosecond if $dt->can("nanosecond"); | 
| 3262 |  |  |  |  |  |  |  | 
| 3263 |  |  |  |  |  |  | # Use date + telescope name as key | 
| 3264 | 13409 |  |  |  |  | 84928 | $self->_cache_key($telName . "_" . $dt->epoch. $addendum); | 
| 3265 |  |  |  |  |  |  | } | 
| 3266 |  |  |  |  |  |  |  | 
| 3267 |  |  |  |  |  |  | =item B<_cache_key> | 
| 3268 |  |  |  |  |  |  |  | 
| 3269 |  |  |  |  |  |  | Retrieve the current key suitable for caching results. | 
| 3270 |  |  |  |  |  |  | The key should have been calculated using _calc_cache_key(). | 
| 3271 |  |  |  |  |  |  | Can be undef if caching is disabled. | 
| 3272 |  |  |  |  |  |  |  | 
| 3273 |  |  |  |  |  |  | $key = $c->_cache_key; | 
| 3274 |  |  |  |  |  |  |  | 
| 3275 |  |  |  |  |  |  | =cut | 
| 3276 |  |  |  |  |  |  |  | 
| 3277 |  |  |  |  |  |  | sub _cache_key { | 
| 3278 | 115519 |  |  | 115519 |  | 228616 | my $self = shift; | 
| 3279 | 115519 | 100 |  |  |  | 209036 | if (@_) { | 
| 3280 | 16067 |  |  |  |  | 31659 | $self->{_CACHE_KEY} = shift; | 
| 3281 |  |  |  |  |  |  | } | 
| 3282 | 115519 |  |  |  |  | 204133 | return $self->{_CACHE_KEY}; | 
| 3283 |  |  |  |  |  |  | } | 
| 3284 |  |  |  |  |  |  |  | 
| 3285 |  |  |  |  |  |  |  | 
| 3286 | 19 |  |  | 19 |  | 205067 | use constant GLOBAL_CACHE_MAX => 1000; | 
|  | 19 |  |  |  |  | 74 |  | 
|  | 19 |  |  |  |  | 7355 |  | 
| 3287 |  |  |  |  |  |  | our %_cache_global_lst = (); | 
| 3288 |  |  |  |  |  |  | our @_cache_global_lst = (); | 
| 3289 |  |  |  |  |  |  |  | 
| 3290 |  |  |  |  |  |  | =item B<_cache_read_global_lst> | 
| 3291 |  |  |  |  |  |  |  | 
| 3292 |  |  |  |  |  |  | Retrieves a value from the global cache of LST values. | 
| 3293 |  |  |  |  |  |  | Returns undef if no value is available. | 
| 3294 |  |  |  |  |  |  |  | 
| 3295 |  |  |  |  |  |  | $lst = $self->_cache_read_global_lst(); | 
| 3296 |  |  |  |  |  |  |  | 
| 3297 |  |  |  |  |  |  | =cut | 
| 3298 |  |  |  |  |  |  |  | 
| 3299 |  |  |  |  |  |  | sub _cache_read_global_lst { | 
| 3300 | 11716 |  |  | 11716 |  | 15896 | my $self = shift; | 
| 3301 | 11716 |  |  |  |  | 19939 | my $key = $self->_cache_key(); | 
| 3302 | 11716 | 100 |  |  |  | 24659 | return undef unless defined $key; | 
| 3303 | 7160 |  |  |  |  | 19407 | return $_cache_global_lst{$key}; | 
| 3304 |  |  |  |  |  |  | } | 
| 3305 |  |  |  |  |  |  |  | 
| 3306 |  |  |  |  |  |  | =item B<_cache_write_global_lst> | 
| 3307 |  |  |  |  |  |  |  | 
| 3308 |  |  |  |  |  |  | Writes a value into the global cache of LST values. | 
| 3309 |  |  |  |  |  |  |  | 
| 3310 |  |  |  |  |  |  | $self->_cache_write_global_lst($lst); | 
| 3311 |  |  |  |  |  |  |  | 
| 3312 |  |  |  |  |  |  | =cut | 
| 3313 |  |  |  |  |  |  |  | 
| 3314 |  |  |  |  |  |  | sub _cache_write_global_lst { | 
| 3315 | 8758 |  |  | 8758 |  | 12293 | my $self = shift; | 
| 3316 | 8758 |  |  |  |  | 11388 | my $value = shift; | 
| 3317 | 8758 | 50 |  |  |  | 16845 | return unless defined $value; | 
| 3318 | 8758 |  |  |  |  | 16781 | my $key = $self->_cache_key(); | 
| 3319 | 8758 | 100 |  |  |  | 18666 | return unless defined $key; | 
| 3320 | 4202 | 100 |  |  |  | 9959 | if (GLOBAL_CACHE_MAX < scalar @_cache_global_lst) { | 
| 3321 | 2072 |  |  |  |  | 6257 | my $remove = shift @_cache_global_lst; | 
| 3322 | 2072 |  |  |  |  | 16676 | delete $_cache_global_lst{$remove}; | 
| 3323 |  |  |  |  |  |  | } | 
| 3324 | 4202 |  |  |  |  | 11201 | $_cache_global_lst{$key} = $value; | 
| 3325 | 4202 |  |  |  |  | 9241 | push @_cache_global_lst, $key; | 
| 3326 |  |  |  |  |  |  | } | 
| 3327 |  |  |  |  |  |  |  | 
| 3328 |  |  |  |  |  |  | =item B<_cache_ref> | 
| 3329 |  |  |  |  |  |  |  | 
| 3330 |  |  |  |  |  |  | Returns reference to cache hash. Internal internal. | 
| 3331 |  |  |  |  |  |  |  | 
| 3332 |  |  |  |  |  |  | =cut | 
| 3333 |  |  |  |  |  |  |  | 
| 3334 |  |  |  |  |  |  | { | 
| 3335 |  |  |  |  |  |  | my $KEY = "__AC_CACHE"; | 
| 3336 |  |  |  |  |  |  | sub _cache_ref { | 
| 3337 | 29063 |  |  | 29063 |  | 38590 | my $self = shift; | 
| 3338 | 29063 | 100 |  |  |  | 55896 | if (!exists $self->{$KEY} ) { | 
| 3339 | 1530 |  |  |  |  | 4332 | $self->{$KEY} = {}; | 
| 3340 |  |  |  |  |  |  | } | 
| 3341 | 29063 |  |  |  |  | 47952 | return $self->{$KEY}; | 
| 3342 |  |  |  |  |  |  | } | 
| 3343 |  |  |  |  |  |  | } | 
| 3344 |  |  |  |  |  |  |  | 
| 3345 |  |  |  |  |  |  | =back | 
| 3346 |  |  |  |  |  |  |  | 
| 3347 |  |  |  |  |  |  | =end __PRIVATE_METHODS__ | 
| 3348 |  |  |  |  |  |  |  | 
| 3349 |  |  |  |  |  |  | =head1 NOTES | 
| 3350 |  |  |  |  |  |  |  | 
| 3351 |  |  |  |  |  |  | Many of the methods described in these classes return results as | 
| 3352 |  |  |  |  |  |  | either C<Astro::Coords::Angle> and C<Astro::Coords::Angle::Hour> | 
| 3353 |  |  |  |  |  |  | objects. This provides to the caller much more control in how to | 
| 3354 |  |  |  |  |  |  | represent the answer, especially when the default stringification may | 
| 3355 |  |  |  |  |  |  | not be suitable.  Whilst methods such as C<radec> and C<apparent> | 
| 3356 |  |  |  |  |  |  | always return objects, methods to return individual coordinate values | 
| 3357 |  |  |  |  |  |  | such as C<ra>, C<dec>, and C<az> can return the result in a variety of | 
| 3358 |  |  |  |  |  |  | formats. The default format is simply to return the underlying | 
| 3359 |  |  |  |  |  |  | C<Angle> object but an explicit format can be specified if you are | 
| 3360 |  |  |  |  |  |  | simply interested in the value in degrees, say, or are instantly | 
| 3361 |  |  |  |  |  |  | stringifying it. The supported formats are all documented in the | 
| 3362 |  |  |  |  |  |  | C<in_format> method documentation in the C<Astro::Coords::Angle> man | 
| 3363 |  |  |  |  |  |  | page but include all the standard options that have been available in | 
| 3364 |  |  |  |  |  |  | early versions of C<Astro::Coords>: 'sexagesimal', 'radians', | 
| 3365 |  |  |  |  |  |  | 'degrees'. | 
| 3366 |  |  |  |  |  |  |  | 
| 3367 |  |  |  |  |  |  | $radians = $c->ra( format => 'rad' ); | 
| 3368 |  |  |  |  |  |  | $string  = $c->ra( format => 'sex' ); | 
| 3369 |  |  |  |  |  |  | $deg     = $c->ra( format => 'deg' ); | 
| 3370 |  |  |  |  |  |  | $object  = $c->ra(); | 
| 3371 |  |  |  |  |  |  |  | 
| 3372 |  |  |  |  |  |  | =head1 CONSTANTS | 
| 3373 |  |  |  |  |  |  |  | 
| 3374 |  |  |  |  |  |  | In some cases when calculating events such as sunrise, sunset or | 
| 3375 |  |  |  |  |  |  | twilight time it is useful to have predefined constants containing | 
| 3376 |  |  |  |  |  |  | the standard elevations. These are available in the C<Astro::Coords> | 
| 3377 |  |  |  |  |  |  | namespace as: | 
| 3378 |  |  |  |  |  |  |  | 
| 3379 |  |  |  |  |  |  | SUN_RISE_SET: Position of Sun for sunrise or sunset (-50 arcminutes) | 
| 3380 |  |  |  |  |  |  | CIVIL_TWILIGHT: Civil twilight (-6 degrees) | 
| 3381 |  |  |  |  |  |  | NAUT_TWILIGHT: Nautical twilight (-12 degrees) | 
| 3382 |  |  |  |  |  |  | AST_TWILIGHT: Astronomical twilight (-18 degrees) | 
| 3383 |  |  |  |  |  |  |  | 
| 3384 |  |  |  |  |  |  | For example: | 
| 3385 |  |  |  |  |  |  |  | 
| 3386 |  |  |  |  |  |  | $set = $c->set_time( horizon => Astro::Coords::AST_TWILIGHT ); | 
| 3387 |  |  |  |  |  |  |  | 
| 3388 |  |  |  |  |  |  | These are usually only relevant for the Sun. Note that refraction | 
| 3389 |  |  |  |  |  |  | effects may affect the actual answer and these are simply average | 
| 3390 |  |  |  |  |  |  | definitions. | 
| 3391 |  |  |  |  |  |  |  | 
| 3392 |  |  |  |  |  |  | For the Sun and Moon the expected defaults are used if no horizon | 
| 3393 |  |  |  |  |  |  | is specified (ie SUN_RISE_SET is used for the Sun). | 
| 3394 |  |  |  |  |  |  |  | 
| 3395 |  |  |  |  |  |  | =head1 REQUIREMENTS | 
| 3396 |  |  |  |  |  |  |  | 
| 3397 |  |  |  |  |  |  | C<Astro::PAL> is used for all internal astrometric calculations. | 
| 3398 |  |  |  |  |  |  |  | 
| 3399 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 3400 |  |  |  |  |  |  |  | 
| 3401 |  |  |  |  |  |  | L<Astro::Telescope> and L<DateTime> are used to specify observer | 
| 3402 |  |  |  |  |  |  | location and reference epoch respectively. | 
| 3403 |  |  |  |  |  |  |  | 
| 3404 |  |  |  |  |  |  | L<Astro::Coords::Equatorial>, | 
| 3405 |  |  |  |  |  |  | L<Astro::Coords::Planet>, | 
| 3406 |  |  |  |  |  |  | L<Astro::Coords::Fixed>, | 
| 3407 |  |  |  |  |  |  | L<Astro::Coords::Interpolated>, | 
| 3408 |  |  |  |  |  |  | L<Astro::Coords::Calibration>, | 
| 3409 |  |  |  |  |  |  | L<Astro::Coords::Angle>, | 
| 3410 |  |  |  |  |  |  | L<Astro::Coords::Angle::Hour>. | 
| 3411 |  |  |  |  |  |  |  | 
| 3412 |  |  |  |  |  |  | =head1 AUTHOR | 
| 3413 |  |  |  |  |  |  |  | 
| 3414 |  |  |  |  |  |  | Tim Jenness E<lt>tjenness@cpan.orgE<gt> | 
| 3415 |  |  |  |  |  |  |  | 
| 3416 |  |  |  |  |  |  | =head1 COPYRIGHT | 
| 3417 |  |  |  |  |  |  |  | 
| 3418 |  |  |  |  |  |  | Copyright (C) 2008, 2010, 2012 Science & Technology Facilities Council. | 
| 3419 |  |  |  |  |  |  | Copyright (C) 2001-2005 Particle Physics and Astronomy Research Council. | 
| 3420 |  |  |  |  |  |  | All Rights Reserved. | 
| 3421 |  |  |  |  |  |  |  | 
| 3422 |  |  |  |  |  |  | This program is free software; you can redistribute it and/or modify it under | 
| 3423 |  |  |  |  |  |  | the terms of the GNU General Public License as published by the Free Software | 
| 3424 |  |  |  |  |  |  | Foundation; either version 3 of the License, or (at your option) any later | 
| 3425 |  |  |  |  |  |  | version. | 
| 3426 |  |  |  |  |  |  |  | 
| 3427 |  |  |  |  |  |  | This program is distributed in the hope that it will be useful,but WITHOUT ANY | 
| 3428 |  |  |  |  |  |  | WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A | 
| 3429 |  |  |  |  |  |  | PARTICULAR PURPOSE. See the GNU General Public License for more details. | 
| 3430 |  |  |  |  |  |  |  | 
| 3431 |  |  |  |  |  |  | You should have received a copy of the GNU General Public License along with | 
| 3432 |  |  |  |  |  |  | this program; if not, write to the Free Software Foundation, Inc., 59 Temple | 
| 3433 |  |  |  |  |  |  | Place,Suite 330, Boston, MA  02111-1307, USA | 
| 3434 |  |  |  |  |  |  |  | 
| 3435 |  |  |  |  |  |  | =cut | 
| 3436 |  |  |  |  |  |  |  |