| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | =head1 NAME | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | Astro::Coord::ECI::Utils - Utility routines for astronomical calculations | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | use Astro::Coord::ECI::Utils qw{ :all }; | 
| 8 |  |  |  |  |  |  | my $now = time (); | 
| 9 |  |  |  |  |  |  | print "The current Julian day is ", julianday ($now); | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | This module was written to provide a home for all the constants and | 
| 14 |  |  |  |  |  |  | utility subroutines used by B and its descendants. | 
| 15 |  |  |  |  |  |  | What ended up here was anything that was essentially a subroutine, not | 
| 16 |  |  |  |  |  |  | a method. | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | Because figuring out how to convert to and from Perl time bids fair to | 
| 19 |  |  |  |  |  |  | become complicated, this module is also responsible for figuring out how | 
| 20 |  |  |  |  |  |  | that is done, and exporting whatever is needful to export. See C<:time> | 
| 21 |  |  |  |  |  |  | below for the gory details. | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | This package exports nothing by default. But all the constants, | 
| 24 |  |  |  |  |  |  | variables, and subroutines documented below are exportable, and the | 
| 25 |  |  |  |  |  |  | following export tags may be used: | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | =over | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  | =item :all | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | This imports everything exportable into your name space. | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | =item :greg_time | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | This imports all time routines except the discouraged routines | 
| 36 |  |  |  |  |  |  | C and C. | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | =item :mainstream | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | This imports everything not deprecated into your name space. | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | =item :params | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | This imports the parameter validation routines C<__classisa> and | 
| 45 |  |  |  |  |  |  | C<__instance>. | 
| 46 |  |  |  |  |  |  |  | 
| 47 |  |  |  |  |  |  | =item :ref | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | This imports all the C<*_REF> constants. | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | =item :time | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | This imports the time routines into your name space. If | 
| 54 |  |  |  |  |  |  | L is available, it will be loaded, and both | 
| 55 |  |  |  |  |  |  | this tag and C<:all> will import C, C, C, | 
| 56 |  |  |  |  |  |  | C, C, and C into your name | 
| 57 |  |  |  |  |  |  | space. Otherwise, C will be loaded, and both | 
| 58 |  |  |  |  |  |  | this tag and C<:all> will import C, C, | 
| 59 |  |  |  |  |  |  | C, and C into your name space. | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | =item :vector | 
| 62 |  |  |  |  |  |  |  | 
| 63 |  |  |  |  |  |  | This imports the vector arithmetic routines. It includes anything whose | 
| 64 |  |  |  |  |  |  | name begins with C<'vector_'>. | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | =back | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | Under Perl 5.6 you may find that, if you use any of the above tags, you | 
| 69 |  |  |  |  |  |  | need to specify them first in your import list. | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | =head2 The following constants are exportable: | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | AU = number of kilometers in an astronomical unit | 
| 74 |  |  |  |  |  |  | JD_OF_EPOCH = the Julian Day of Perl epoch 0 | 
| 75 |  |  |  |  |  |  | LIGHTYEAR = number of kilometers in a light year | 
| 76 |  |  |  |  |  |  | PARSEC = number of kilometers in a parsec | 
| 77 |  |  |  |  |  |  | PERL2000 = January 1 2000, 12 noon universal, in Perl time | 
| 78 |  |  |  |  |  |  | PI = the circle ratio, computed as atan2 (0, -1) | 
| 79 |  |  |  |  |  |  | PIOVER2 = half the circle ratio | 
| 80 |  |  |  |  |  |  | SECSPERDAY = the number of seconds in a day | 
| 81 |  |  |  |  |  |  | SECS_PER_SIDERIAL_DAY = seconds in a siderial day | 
| 82 |  |  |  |  |  |  | SPEED_OF_LIGHT = speed of light in kilometers per second | 
| 83 |  |  |  |  |  |  | TWOPI = twice the circle ratio | 
| 84 |  |  |  |  |  |  |  | 
| 85 |  |  |  |  |  |  | ARRAY_REF  = 'ARRAY' | 
| 86 |  |  |  |  |  |  | CODE_REF   = 'CODE' | 
| 87 |  |  |  |  |  |  | HASH_REF   = 'HASH' | 
| 88 |  |  |  |  |  |  | SCALAR_REF = 'SCALAR' | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | =head2 The following global variables are exportable: | 
| 91 |  |  |  |  |  |  |  | 
| 92 |  |  |  |  |  |  | =head3 $DATETIMEFORMAT | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | This variable represents the POSIX::strftime format used to convert | 
| 95 |  |  |  |  |  |  | times to strings. The default value is '%a %b %d %Y %H:%M:%S' to be | 
| 96 |  |  |  |  |  |  | consistent with the behavior of gmtime (or, to be precise, the | 
| 97 |  |  |  |  |  |  | behavior of ctime as documented on my system). | 
| 98 |  |  |  |  |  |  |  | 
| 99 |  |  |  |  |  |  | =head3 $JD_GREGORIAN | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | This variable represents the Julian Day of the switch from Julian to | 
| 102 |  |  |  |  |  |  | Gregorian calendars. This is used by date2jd(), jd2date(), and the | 
| 103 |  |  |  |  |  |  | routines which depend on them, for deciding whether the date is to be | 
| 104 |  |  |  |  |  |  | interpreted as in the Julian or Gregorian calendar. Its initial setting | 
| 105 |  |  |  |  |  |  | is 2299160.5, which represents midnight October 15 1582 in the Gregorian | 
| 106 |  |  |  |  |  |  | calendar, which is the date that calendar was first adopted. This is | 
| 107 |  |  |  |  |  |  | slightly different than the value of 2299161 (noon of the same day) used | 
| 108 |  |  |  |  |  |  | by Jean Meeus. | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | If you are interested in historical calculations, you may wish to reset | 
| 111 |  |  |  |  |  |  | this appropriately. If you use date2jd to calculate the new value, be | 
| 112 |  |  |  |  |  |  | aware of the effect the current setting of $JD_GREGORIAN has on the | 
| 113 |  |  |  |  |  |  | interpretation of the date you give. | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | =head2 In addition, the following subroutines are exportable: | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | =over 4 | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | =cut | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | package Astro::Coord::ECI::Utils; | 
| 122 |  |  |  |  |  |  |  | 
| 123 | 18 |  |  | 18 |  | 67311 | use strict; | 
|  | 18 |  |  |  |  | 40 |  | 
|  | 18 |  |  |  |  | 505 |  | 
| 124 | 18 |  |  | 18 |  | 86 | use warnings; | 
|  | 18 |  |  |  |  | 33 |  | 
|  | 18 |  |  |  |  | 914 |  | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | our $VERSION = '0.129'; | 
| 127 |  |  |  |  |  |  | our @ISA = qw{Exporter}; | 
| 128 |  |  |  |  |  |  |  | 
| 129 | 18 |  |  | 18 |  | 135 | use Carp; | 
|  | 18 |  |  |  |  | 36 |  | 
|  | 18 |  |  |  |  | 1523 |  | 
| 130 |  |  |  |  |  |  | ## use Config; | 
| 131 |  |  |  |  |  |  | ## use Data::Dumper; | 
| 132 | 18 |  |  | 18 |  | 8249 | use POSIX qw{ floor modf strftime }; | 
|  | 18 |  |  |  |  | 124987 |  | 
|  | 18 |  |  |  |  | 97 |  | 
| 133 | 18 |  |  | 18 |  | 24492 | use Scalar::Util qw{ blessed }; | 
|  | 18 |  |  |  |  | 41 |  | 
|  | 18 |  |  |  |  | 9356 |  | 
| 134 |  |  |  |  |  |  |  | 
| 135 |  |  |  |  |  |  | my @greg_time_routines; | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | BEGIN { | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | # NOTE WELL | 
| 140 |  |  |  |  |  |  | # | 
| 141 |  |  |  |  |  |  | # The logic here should be consistent with the optional-module text | 
| 142 |  |  |  |  |  |  | # emitted by inc/Astro/Coord/ECI/Recommend.pm. | 
| 143 |  |  |  |  |  |  | # | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | eval { | 
| 146 | 18 |  |  |  |  | 2837 | require Time::y2038; | 
| 147 | 0 |  |  |  |  | 0 | Time::y2038->import( qw{ gmtime localtime } ); | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | # sub time_gm | 
| 150 |  |  |  |  |  |  | *time_gm = sub { | 
| 151 | 0 |  |  |  |  | 0 | my @date = @_; | 
| 152 | 0 |  |  |  |  | 0 | $date[5] = _year_adjust_y2038( $date[5] ); | 
| 153 | 0 |  |  |  |  | 0 | return Time::y2038::timegm( @date ); | 
| 154 | 0 |  |  |  |  | 0 | }; | 
| 155 |  |  |  |  |  |  | # greg_time_local | 
| 156 |  |  |  |  |  |  | *greg_time_gm = sub { | 
| 157 | 0 |  |  |  |  | 0 | my @date = @_; | 
| 158 | 0 |  |  |  |  | 0 | $date[5] -= 1900; | 
| 159 | 0 |  |  |  |  | 0 | return Time::y2038::timegm( @date ); | 
| 160 | 0 |  |  |  |  | 0 | }; | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | # sub time_local | 
| 163 |  |  |  |  |  |  | *time_local = sub { | 
| 164 | 0 |  |  |  |  | 0 | my @date = @_; | 
| 165 | 0 |  |  |  |  | 0 | $date[5] = _year_adjust_y2038( $date[5] ); | 
| 166 | 0 |  |  |  |  | 0 | return Time::y2038::timelocal( @date ); | 
| 167 | 0 |  |  |  |  | 0 | }; | 
| 168 |  |  |  |  |  |  | # sub greg_time_local | 
| 169 |  |  |  |  |  |  | *greg_time_local = sub { | 
| 170 | 0 |  |  |  |  | 0 | my @date = @_; | 
| 171 | 0 |  |  |  |  | 0 | $date[5] -= 1900; | 
| 172 | 0 |  |  |  |  | 0 | return Time::y2038::timelocal( @date ); | 
| 173 | 0 |  |  |  |  | 0 | }; | 
| 174 |  |  |  |  |  |  |  | 
| 175 | 0 |  |  |  |  | 0 | @greg_time_routines = qw{ | 
| 176 |  |  |  |  |  |  | gmtime localtime | 
| 177 |  |  |  |  |  |  | greg_time_gm greg_time_local | 
| 178 |  |  |  |  |  |  | __tle_year_to_Gregorian_year | 
| 179 |  |  |  |  |  |  | }; | 
| 180 |  |  |  |  |  |  |  | 
| 181 | 0 |  |  |  |  | 0 | 1; | 
| 182 | 18 | 50 |  | 18 |  | 75 | } or do { | 
| 183 | 18 |  |  |  |  | 7195 | require Time::Local; | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | # sub time_gm | 
| 186 | 18 |  |  |  |  | 33847 | *time_gm = Time::Local->can( 'timegm' ); | 
| 187 |  |  |  |  |  |  | # sub greg_time_gm | 
| 188 |  |  |  |  |  |  | *greg_time_gm = Time::Local->can( 'timegm_modern' ) || sub { | 
| 189 | 260 |  |  | 260 |  | 80344 | my @date = @_; | 
| 190 | 260 |  |  |  |  | 776 | $date[5] = _year_adjust_greg( $date[5] ); | 
| 191 | 260 |  |  |  |  | 962 | return Time::Local::timegm( @date ); | 
| 192 | 18 |  | 50 |  |  | 292 | }; | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | # sub time_local | 
| 195 | 18 |  |  |  |  | 84 | *time_local = Time::Local->can( 'timelocal' ); | 
| 196 |  |  |  |  |  |  | # sub greg_time_local | 
| 197 |  |  |  |  |  |  | *greg_time_local = Time::Local->can( 'timelocal_modern' ) || sub { | 
| 198 | 46 |  |  | 46 |  | 12226 | my @date = @_; | 
| 199 | 46 |  |  |  |  | 112 | $date[5] = _year_adjust_greg( $date[5] ); | 
| 200 | 46 |  |  |  |  | 114 | return Time::Local::timelocal( @date ); | 
| 201 | 18 |  | 50 |  |  | 178 | }; | 
| 202 |  |  |  |  |  |  |  | 
| 203 | 18 |  |  |  |  | 1302 | @greg_time_routines = qw{ | 
| 204 |  |  |  |  |  |  | greg_time_gm greg_time_local | 
| 205 |  |  |  |  |  |  | __tle_year_to_Gregorian_year | 
| 206 |  |  |  |  |  |  | }; | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | }; | 
| 209 |  |  |  |  |  |  | } | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | # This subroutine is used to convert year numbers to Perl years in | 
| 212 |  |  |  |  |  |  | # accordance with the documentation in the 5.24.0 version of | 
| 213 |  |  |  |  |  |  | # Time::Local. It is intended to be called by the Time::y2038 code, | 
| 214 |  |  |  |  |  |  | # which expects Perl years. | 
| 215 |  |  |  |  |  |  |  | 
| 216 |  |  |  |  |  |  | { | 
| 217 |  |  |  |  |  |  | # The following code is lifted verbatim from Time::Local 1.25. | 
| 218 |  |  |  |  |  |  | # Because that code bases the window used for expanding two-digit | 
| 219 |  |  |  |  |  |  | # years on the local year as of the time the module was loaded, I do | 
| 220 |  |  |  |  |  |  | # too. | 
| 221 |  |  |  |  |  |  |  | 
| 222 |  |  |  |  |  |  | my $ThisYear    = ( localtime() )[5]; | 
| 223 |  |  |  |  |  |  | my $Breakpoint  = ( $ThisYear + 50 ) % 100; | 
| 224 |  |  |  |  |  |  | my $NextCentury = $ThisYear - $ThisYear % 100; | 
| 225 |  |  |  |  |  |  | $NextCentury += 100 if $Breakpoint < 50; | 
| 226 |  |  |  |  |  |  | my $Century = $NextCentury - 100; | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | # The above code is lifted verbatim from Time::Local 1.25. | 
| 229 |  |  |  |  |  |  |  | 
| 230 | 18 |  |  |  |  | 7537 | use constant NOT_GREG	=> | 
| 231 | 18 |  |  | 18 |  | 129 | '%d not interpreted as Gregorian year by Time::Local::timegm'; | 
|  | 18 |  |  |  |  | 34 |  | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | # Adujst the year so that the Time::y2038 implementation of | 
| 234 |  |  |  |  |  |  | # time_gm() and time_local() mirrors the Time::Local timegm() and | 
| 235 |  |  |  |  |  |  | # timelocal() behavior. Kinda sorta. | 
| 236 |  |  |  |  |  |  | sub _year_adjust_y2038 { | 
| 237 | 0 |  |  | 0 |  | 0 | my ( $year ) = @_; | 
| 238 |  |  |  |  |  |  |  | 
| 239 | 0 | 0 |  |  |  | 0 | $year < 0 | 
| 240 |  |  |  |  |  |  | and return $year; | 
| 241 |  |  |  |  |  |  |  | 
| 242 | 0 | 0 |  |  |  | 0 | $year >= 1000 | 
| 243 |  |  |  |  |  |  | and return $year - 1900; | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | # The following line of code is lifted verbatim from Time::Local | 
| 246 |  |  |  |  |  |  | # 1.25. | 
| 247 | 0 | 0 |  |  |  | 0 | $year += ( $year > $Breakpoint ) ? $Century : $NextCentury; | 
| 248 |  |  |  |  |  |  |  | 
| 249 | 0 |  |  |  |  | 0 | return $year; | 
| 250 |  |  |  |  |  |  | } | 
| 251 |  |  |  |  |  |  | } | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | # Adjust a Gregorian year so that Time::Local timegm() and timelocal() | 
| 254 |  |  |  |  |  |  | # return epochs in that year. | 
| 255 |  |  |  |  |  |  | sub _year_adjust_greg { | 
| 256 | 306 |  |  | 306 |  | 577 | my ( $year ) = @_; | 
| 257 | 306 | 50 |  |  |  | 880 | return $year >= 1000 ? $year : $year - 1900; | 
| 258 |  |  |  |  |  |  | } | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | our @CARP_NOT = qw{ | 
| 261 |  |  |  |  |  |  | Astro::Coord::ECI | 
| 262 |  |  |  |  |  |  | Astro::Coord::ECI::Mixin | 
| 263 |  |  |  |  |  |  | Astro::Coord::ECI::Moon | 
| 264 |  |  |  |  |  |  | Astro::Coord::ECI::Star | 
| 265 |  |  |  |  |  |  | Astro::Coord::ECI::Sun | 
| 266 |  |  |  |  |  |  | Astro::Coord::ECI::TLE | 
| 267 |  |  |  |  |  |  | Astro::Coord::ECI::TLE::Set | 
| 268 |  |  |  |  |  |  | Astro::Coord::ECI::Utils | 
| 269 |  |  |  |  |  |  | }; | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | our @EXPORT; | 
| 272 |  |  |  |  |  |  | my @all_external = ( qw{ | 
| 273 |  |  |  |  |  |  | AU $DATETIMEFORMAT $JD_GREGORIAN JD_OF_EPOCH LIGHTYEAR PARSEC | 
| 274 |  |  |  |  |  |  | PERL2000 PI PIOVER2 SECSPERDAY SECS_PER_SIDERIAL_DAY | 
| 275 |  |  |  |  |  |  | SPEED_OF_LIGHT TWOPI | 
| 276 |  |  |  |  |  |  | ARRAY_REF CODE_REF HASH_REF SCALAR_REF | 
| 277 |  |  |  |  |  |  | acos add_magnitudes asin | 
| 278 |  |  |  |  |  |  | atmospheric_extinction date2epoch date2jd | 
| 279 |  |  |  |  |  |  | decode_space_track_json_time deg2rad distsq dynamical_delta | 
| 280 |  |  |  |  |  |  | embodies epoch2datetime find_first_true | 
| 281 |  |  |  |  |  |  | fold_case __format_epoch_time_usec | 
| 282 |  |  |  |  |  |  | format_space_track_json_time intensity_to_magnitude | 
| 283 |  |  |  |  |  |  | jcent2000 jd2date jd2datetime jday2000 julianday | 
| 284 |  |  |  |  |  |  | keplers_equation load_module looks_like_number max min mod2pi | 
| 285 |  |  |  |  |  |  | omega | 
| 286 |  |  |  |  |  |  | position_angle | 
| 287 |  |  |  |  |  |  | rad2deg rad2dms rad2hms tan theta0 thetag vector_cross_product | 
| 288 |  |  |  |  |  |  | vector_dot_product vector_magnitude vector_unitize __classisa | 
| 289 |  |  |  |  |  |  | __default_station __instance __subroutine_deprecation | 
| 290 |  |  |  |  |  |  | __sprintf | 
| 291 |  |  |  |  |  |  | }, | 
| 292 |  |  |  |  |  |  | qw{ time_gm time_local }, @greg_time_routines ); | 
| 293 |  |  |  |  |  |  | our @EXPORT_OK = ( | 
| 294 |  |  |  |  |  |  | qw{ @CARP_NOT },	# Package-private, undocumented | 
| 295 |  |  |  |  |  |  | @all_external, | 
| 296 |  |  |  |  |  |  | ); | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | my %deprecated_export = map { $_ => 1 } qw{ | 
| 299 |  |  |  |  |  |  | }; | 
| 300 |  |  |  |  |  |  |  | 
| 301 |  |  |  |  |  |  | our %EXPORT_TAGS = ( | 
| 302 |  |  |  |  |  |  | all => \@all_external, | 
| 303 |  |  |  |  |  |  | greg_time	=> \@greg_time_routines, | 
| 304 |  |  |  |  |  |  | mainstream => [ grep { ! $deprecated_export{$_} } @all_external ], | 
| 305 |  |  |  |  |  |  | params => [ qw{ __classisa __instance } ], | 
| 306 |  |  |  |  |  |  | ref	=> [ grep { m/ [[:upper:]]+ _REF \z /smx } @all_external ], | 
| 307 |  |  |  |  |  |  | time => [ qw{ time_gm time_local }, @greg_time_routines ], | 
| 308 |  |  |  |  |  |  | vector => [ grep { m/ \A vector_ /smx } @all_external ], | 
| 309 |  |  |  |  |  |  | ); | 
| 310 |  |  |  |  |  |  |  | 
| 311 | 18 |  |  | 18 |  | 143 | use constant AU => 149597870;		# 1 astronomical unit, per | 
|  | 18 |  |  |  |  | 35 |  | 
|  | 18 |  |  |  |  | 964 |  | 
| 312 |  |  |  |  |  |  | # Meeus, Appendix I pg 407. | 
| 313 | 18 |  |  | 18 |  | 101 | use constant LIGHTYEAR => 9.4607e12;	# 1 light-year, per Meeus, | 
|  | 18 |  |  |  |  | 36 |  | 
|  | 18 |  |  |  |  | 882 |  | 
| 314 |  |  |  |  |  |  | # Appendix I pg 407. | 
| 315 | 18 |  |  | 18 |  | 105 | use constant PARSEC => 30.8568e12;	# 1 parsec, per Meeus, | 
|  | 18 |  |  |  |  | 51 |  | 
|  | 18 |  |  |  |  | 1114 |  | 
| 316 |  |  |  |  |  |  | # Appendix I pg 407. | 
| 317 | 18 |  |  | 18 |  | 137 | use constant PERL2000 => greg_time_gm( 0, 0, 12, 1, 0, 2000 ); | 
|  | 18 |  |  |  |  | 74 |  | 
|  | 18 |  |  |  |  | 62 |  | 
| 318 | 18 |  |  | 18 |  | 1959 | use constant PI => atan2 (0, -1); | 
|  | 18 |  |  |  |  | 46 |  | 
|  | 18 |  |  |  |  | 1123 |  | 
| 319 | 18 |  |  | 18 |  | 121 | use constant PIOVER2 => PI / 2; | 
|  | 18 |  |  |  |  | 35 |  | 
|  | 18 |  |  |  |  | 1222 |  | 
| 320 | 18 |  |  | 18 |  | 221 | use constant SECSPERDAY => 86400; | 
|  | 18 |  |  |  |  | 49 |  | 
|  | 18 |  |  |  |  | 883 |  | 
| 321 | 18 |  |  | 18 |  | 113 | use constant SECS_PER_SIDERIAL_DAY => 86164.0905;	# Appendix I, page 408. | 
|  | 18 |  |  |  |  | 31 |  | 
|  | 18 |  |  |  |  | 944 |  | 
| 322 | 18 |  |  | 18 |  | 106 | use constant SPEED_OF_LIGHT => 299792.458;	# KM/sec, per NIST. | 
|  | 18 |  |  |  |  | 50 |  | 
|  | 18 |  |  |  |  | 1089 |  | 
| 323 |  |  |  |  |  |  | ### use constant SOLAR_RADIUS => 1392000 / 2;	# Meeus, Appendix I, page 407. | 
| 324 | 18 |  |  | 18 |  | 151 | use constant TWOPI => PI * 2; | 
|  | 18 |  |  |  |  | 57 |  | 
|  | 18 |  |  |  |  | 1391 |  | 
| 325 |  |  |  |  |  |  |  | 
| 326 | 18 |  |  | 18 |  | 144 | use constant ARRAY_REF	=> ref []; | 
|  | 18 |  |  |  |  | 36 |  | 
|  | 18 |  |  |  |  | 1168 |  | 
| 327 | 18 |  |  | 18 |  | 121 | use constant CODE_REF	=> ref sub {}; | 
|  | 18 |  |  |  |  | 52 |  | 
|  | 18 |  |  |  |  | 957 |  | 
| 328 | 18 |  |  | 18 |  | 110 | use constant HASH_REF	=> ref {}; | 
|  | 18 |  |  |  |  | 48 |  | 
|  | 18 |  |  |  |  | 1028 |  | 
| 329 | 18 |  |  | 18 |  | 103 | use constant SCALAR_REF	=> ref \0; | 
|  | 18 |  |  |  |  | 58 |  | 
|  | 18 |  |  |  |  | 6513 |  | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | =item $angle = acos ($value) | 
| 332 |  |  |  |  |  |  |  | 
| 333 |  |  |  |  |  |  | This subroutine calculates the arc in radians whose cosine is the given | 
| 334 |  |  |  |  |  |  | value. | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | =cut | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | sub acos { | 
| 339 | 919 | 50 |  | 919 | 1 | 3291 | abs ($_[0]) > 1 and confess < | 
| 340 |  |  |  |  |  |  | Programming error - Trying to take the arc cosine of a number greater | 
| 341 |  |  |  |  |  |  | than 1. | 
| 342 |  |  |  |  |  |  | eod | 
| 343 | 919 |  |  |  |  | 5462 | return atan2 (sqrt (1 - $_[0] * $_[0]), $_[0]) | 
| 344 |  |  |  |  |  |  | } | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | =item $mag = add_magnitudes( $mag1, $mag2, ... ); | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | This subroutine computes the total magnitude of a list of individual | 
| 349 |  |  |  |  |  |  | magnitudes.  The algorithm comes from Jean Meeus' "Astronomical | 
| 350 |  |  |  |  |  |  | Algorithms", Second Edition, Chapter 56, Page 393. | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | =cut | 
| 353 |  |  |  |  |  |  |  | 
| 354 |  |  |  |  |  |  | sub add_magnitudes { | 
| 355 | 1 |  |  | 1 | 1 | 486 | my @arg = @_; | 
| 356 | 1 |  |  |  |  | 3 | my $sum = 0; | 
| 357 | 1 |  |  |  |  | 3 | foreach my $mag ( @arg ) { | 
| 358 | 3 |  |  |  |  | 11 | $sum += 10 ** ( -0.4 * $mag ); | 
| 359 |  |  |  |  |  |  | } | 
| 360 | 1 |  |  |  |  | 6 | return -2.5 * log( $sum ) / log( 10 ); | 
| 361 |  |  |  |  |  |  | } | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | =item $angle = asin ($value) | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | This subroutine calculates the arc in radians whose sine is the given | 
| 366 |  |  |  |  |  |  | value. | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  | =cut | 
| 369 |  |  |  |  |  |  |  | 
| 370 | 25613 |  |  | 25613 | 1 | 67095 | sub asin {return atan2 ($_[0], sqrt (1 - $_[0] * $_[0]))} | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | =item $magnitude = atmospheric_extinction ($elevation, $height); | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | This subroutine calculates the typical atmospheric extinction in | 
| 375 |  |  |  |  |  |  | magnitudes at the given elevation above the horizon in radians and the | 
| 376 |  |  |  |  |  |  | given height above sea level in kilometers. | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | The algorithm comes from Daniel W. E. Green's article "Magnitude | 
| 379 |  |  |  |  |  |  | Corrections for Atmospheric Extinction", which was published in | 
| 380 |  |  |  |  |  |  | the July 1992 issue of "International Comet Quarterly", and is | 
| 381 |  |  |  |  |  |  | available online at | 
| 382 |  |  |  |  |  |  | L. The text of | 
| 383 |  |  |  |  |  |  | this article makes it clear that the actual value of the | 
| 384 |  |  |  |  |  |  | atmospheric extinction can vary greatly from the typical | 
| 385 |  |  |  |  |  |  | values given even in the absence of cloud cover. | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | =cut | 
| 388 |  |  |  |  |  |  |  | 
| 389 |  |  |  |  |  |  | #	Note that the "constant" 0.120 in Aaer (aerosol scattering) is | 
| 390 |  |  |  |  |  |  | #	based on a compromise value A0 = 0.050 in Green's equation 3 | 
| 391 |  |  |  |  |  |  | #	(not exhibited here), which can vary from 0.035 in the winter to | 
| 392 |  |  |  |  |  |  | #	0.065 in the summer. This makes a difference of a couple tenths | 
| 393 |  |  |  |  |  |  | #	at 20 degrees elevation, but a couple magnitudes at the | 
| 394 |  |  |  |  |  |  | #	horizon. Green also remarks that the 1.5 denominator in the | 
| 395 |  |  |  |  |  |  | #	same equation (a.k.a. the scale height) can be up to twice | 
| 396 |  |  |  |  |  |  | #	that. | 
| 397 |  |  |  |  |  |  |  | 
| 398 |  |  |  |  |  |  | sub atmospheric_extinction { | 
| 399 | 3 |  |  | 3 | 1 | 1542 | my ($elevation, $height) = @_; | 
| 400 | 3 |  |  |  |  | 17 | my $cosZ = cos (PIOVER2 - $elevation); | 
| 401 | 3 |  |  |  |  | 13 | my $X = 1/($cosZ + 0.025 * exp (-11 * $cosZ));	# Green 1 | 
| 402 | 3 |  |  |  |  | 9 | my $Aray = 0.1451 * exp (-$height / 7.996);	# Green 2 | 
| 403 | 3 |  |  |  |  | 7 | my $Aaer = 0.120 * exp (-$height / 1.5);	# Green 4 | 
| 404 | 3 |  |  |  |  | 9 | return ($Aray + $Aaer + 0.016) * $X;	# Green 5, 6 | 
| 405 |  |  |  |  |  |  | } | 
| 406 |  |  |  |  |  |  |  | 
| 407 |  |  |  |  |  |  | =item $jd = date2jd ($sec, $min, $hr, $day, $mon, $yr) | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | This subroutine converts the given date to the corresponding Julian day. | 
| 410 |  |  |  |  |  |  | The inputs are a Perl date and time; $mon is in the range 0 - | 
| 411 |  |  |  |  |  |  | 11, and $yr is from 1900, with earlier years being negative. The year 1 | 
| 412 |  |  |  |  |  |  | BC is represented as -1900. | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | If less than 6 arguments are provided, zeroes will be prepended to the | 
| 415 |  |  |  |  |  |  | argument list as needed. | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | The date is presumed to be in the Gregorian calendar. If the resultant | 
| 418 |  |  |  |  |  |  | Julian Day is before $JD_GREGORIAN, the date is reinterpreted as being | 
| 419 |  |  |  |  |  |  | from the Julian calendar. | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  | The only validation is that the month be between 0 and 11 inclusive, and | 
| 422 |  |  |  |  |  |  | that the year be not less than -6612 (4713 BC). Fractional days are | 
| 423 |  |  |  |  |  |  | accepted. | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | The algorithm is from Jean Meeus' "Astronomical Algorithms", second | 
| 426 |  |  |  |  |  |  | edition, chapter 7 ("Julian Day"), pages 60ff, but the month is | 
| 427 |  |  |  |  |  |  | zero-based, not 1-based, and years are 1900-based. | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | =cut | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | our $DATETIMEFORMAT; | 
| 432 |  |  |  |  |  |  | our $JD_GREGORIAN; | 
| 433 |  |  |  |  |  |  | BEGIN { | 
| 434 | 18 |  |  | 18 |  | 71 | $DATETIMEFORMAT = '%a %b %d %Y %H:%M:%S'; | 
| 435 | 18 |  |  |  |  | 5189 | $JD_GREGORIAN = 2299160.5; | 
| 436 |  |  |  |  |  |  | } | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | sub date2jd { | 
| 439 | 21 |  |  | 21 | 1 | 1002 | my @args = @_; | 
| 440 | 21 |  |  |  |  | 114 | unshift @args, 0 while @args < 6; | 
| 441 | 21 |  |  |  |  | 69 | my ($sec, $min, $hr, $day, $mon, $yr) = @args; | 
| 442 | 21 |  |  |  |  | 46 | $mon++;		# Algorithm expects month 1-12. | 
| 443 | 21 |  |  |  |  | 59 | $yr += 1900;	# Algorithm expects zero-based year. | 
| 444 | 21 | 50 |  |  |  | 76 | ($yr < -4712) and croak "Error - Invalid year $yr"; | 
| 445 | 21 | 50 | 33 |  |  | 137 | ($mon < 1 || $mon > 12) and croak "Error - Invalid month $mon"; | 
| 446 | 21 | 100 |  |  |  | 67 | if ($mon < 3) { | 
| 447 | 20 |  |  |  |  | 49 | --$yr; | 
| 448 | 20 |  |  |  |  | 60 | $mon += 12; | 
| 449 |  |  |  |  |  |  | } | 
| 450 | 21 |  |  |  |  | 145 | my $A = floor ($yr / 100); | 
| 451 | 21 |  |  |  |  | 134 | my $B = 2 - $A + floor ($A / 4); | 
| 452 | 21 |  | 50 |  |  | 355 | my $jd = floor (365.25 * ($yr + 4716)) + | 
|  |  |  | 50 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 453 |  |  |  |  |  |  | floor (30.6001 * ($mon + 1)) + $day + $B - 1524.5 + | 
| 454 |  |  |  |  |  |  | ((($sec || 0) / 60 + ($min || 0)) / 60 + ($hr || 0)) / 24; | 
| 455 | 21 | 100 | 50 |  |  | 89 | $jd < $JD_GREGORIAN and | 
|  |  |  | 50 |  |  |  |  | 
|  |  |  | 50 |  |  |  |  | 
| 456 |  |  |  |  |  |  | $jd = floor (365.25 * ($yr + 4716)) + | 
| 457 |  |  |  |  |  |  | floor (30.6001 * ($mon + 1)) + $day - 1524.5 + | 
| 458 |  |  |  |  |  |  | ((($sec || 0) / 60 + ($min || 0)) / 60 + ($hr || 0)) / 24; | 
| 459 | 21 |  |  |  |  | 39882 | return $jd; | 
| 460 |  |  |  |  |  |  | } | 
| 461 |  |  |  |  |  |  |  | 
| 462 | 18 |  |  | 18 |  | 126 | use constant JD_OF_EPOCH => date2jd (gmtime (0)); | 
|  | 18 |  |  |  |  | 32 |  | 
|  | 18 |  |  |  |  | 87 |  | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | =item $epoch = date2epoch ($sec, $min, $hr, $day, $mon, $yr) | 
| 465 |  |  |  |  |  |  |  | 
| 466 |  |  |  |  |  |  | This is a convenience routine that converts the given date to seconds | 
| 467 |  |  |  |  |  |  | since the epoch, going through date2jd() to do so. The arguments are the | 
| 468 |  |  |  |  |  |  | same as those of date2jd(). | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  | If less than 6 arguments are provided, zeroes will be prepended to the | 
| 471 |  |  |  |  |  |  | argument list as needed. | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  | The functionality is the same as B, but this | 
| 474 |  |  |  |  |  |  | function lacks timegm's limited date range under Perls before 5.12.0. If | 
| 475 |  |  |  |  |  |  | you have Perl 5.12.0 or better, the core L | 
| 476 |  |  |  |  |  |  | C will probably do what you want.  If you have an earlier | 
| 477 |  |  |  |  |  |  | Perl, L C may do what you want. | 
| 478 |  |  |  |  |  |  |  | 
| 479 |  |  |  |  |  |  | =cut | 
| 480 |  |  |  |  |  |  |  | 
| 481 |  |  |  |  |  |  | sub date2epoch { | 
| 482 | 1 |  |  | 1 | 1 | 506 | my @args = @_; | 
| 483 | 1 |  |  |  |  | 8 | unshift @args, 0 while @args < 6; | 
| 484 | 1 |  |  |  |  | 4 | my ($sec, $min, $hr, $day, $mon, $yr) = @args; | 
| 485 | 1 |  | 50 |  |  | 3 | return (date2jd ($day, $mon, $yr) - JD_OF_EPOCH) * SECSPERDAY + | 
|  |  |  | 50 |  |  |  |  | 
|  |  |  | 50 |  |  |  |  | 
| 486 |  |  |  |  |  |  | (($hr || 0) * 60 + ($min || 0)) * 60 + ($sec || 0); | 
| 487 |  |  |  |  |  |  | } | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | =item $time = decode_space_track_json_time( $string ) | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | This subroutine decodes a time in the format Space Track uses in their | 
| 492 |  |  |  |  |  |  | JSON code. This is ISO-8601-ish, but with a possible fractional part and | 
| 493 |  |  |  |  |  |  | no zone. | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | =cut | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | sub decode_space_track_json_time { | 
| 498 | 0 |  |  | 0 | 1 | 0 | my ( $string ) = @_; | 
| 499 | 0 | 0 |  |  |  | 0 | $string =~ m{ \A \s* | 
| 500 |  |  |  |  |  |  | ( [0-9]+ ) [^0-9]+ ( [0-9]+ ) [^0-9]+ ( [0-9]+ ) [^0-9]+ | 
| 501 |  |  |  |  |  |  | ( [0-9]+ ) [^0-9]+ ( [0-9]+ ) [^0-9]+ ( [0-9]+ ) (?: ( [.] [0-9]* ) )? \s* \z }smx | 
| 502 |  |  |  |  |  |  | or return; | 
| 503 | 0 |  |  |  |  | 0 | my @time = ( $1, $2, $3, $4, $5, $6 ); | 
| 504 | 0 |  |  |  |  | 0 | my $frac = $7; | 
| 505 | 0 |  |  |  |  | 0 | $time[0] = __tle_year_to_Gregorian_year( $time[0] ); | 
| 506 | 0 |  |  |  |  | 0 | $time[1] -= 1; | 
| 507 | 0 |  |  |  |  | 0 | my $rslt = greg_time_gm( reverse @time ); | 
| 508 | 0 | 0 | 0 |  |  | 0 | defined $frac | 
| 509 |  |  |  |  |  |  | and $frac ne '.' | 
| 510 |  |  |  |  |  |  | and $rslt += $frac; | 
| 511 | 0 |  |  |  |  | 0 | return $rslt; | 
| 512 |  |  |  |  |  |  | } | 
| 513 |  |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  | # my ( $self, $station, @args ) = __default_station( @_ ) | 
| 515 |  |  |  |  |  |  | # | 
| 516 |  |  |  |  |  |  | # This exportable subroutine checks whether the second argument embodies | 
| 517 |  |  |  |  |  |  | # an Astro::Coord::ECI object. If so, the arguments are returned | 
| 518 |  |  |  |  |  |  | # verbatim. If not, the 'station' attribute of the invocant is inserted | 
| 519 |  |  |  |  |  |  | # after the first argument and the result returned. If the 'station' | 
| 520 |  |  |  |  |  |  | # attribute of the invocant has not been set, an exception is thrown. | 
| 521 |  |  |  |  |  |  |  | 
| 522 |  |  |  |  |  |  | sub __default_station { | 
| 523 | 23 |  |  | 23 |  | 73 | my ( $self, @args ) = @_; | 
| 524 | 23 | 100 |  |  |  | 73 | if ( ! embodies( $args[0], 'Astro::Coord::ECI' ) ) { | 
| 525 | 8 | 50 |  |  |  | 42 | my $sta = $self->get( 'station' ) | 
| 526 |  |  |  |  |  |  | or croak 'Station attribute not set'; | 
| 527 | 8 |  |  |  |  | 27 | unshift @args, $sta; | 
| 528 |  |  |  |  |  |  | } | 
| 529 | 23 |  |  |  |  | 82 | return ( $self, @args ); | 
| 530 |  |  |  |  |  |  | } | 
| 531 |  |  |  |  |  |  |  | 
| 532 |  |  |  |  |  |  | =item $rad = deg2rad ($degr) | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  | This subroutine converts degrees to radians. If the argument is | 
| 535 |  |  |  |  |  |  | C, C will be returned. | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | =cut | 
| 538 |  |  |  |  |  |  |  | 
| 539 | 165031 | 100 |  | 165031 | 1 | 581973 | sub deg2rad { return defined $_[0] ? $_[0] * PI / 180 : undef } | 
| 540 |  |  |  |  |  |  |  | 
| 541 |  |  |  |  |  |  | =item $value = distsq (\@coord1, \@coord2) | 
| 542 |  |  |  |  |  |  |  | 
| 543 |  |  |  |  |  |  | This subroutine calculates the square of the distance between the two | 
| 544 |  |  |  |  |  |  | sets of Cartesian coordinates. We do not take the square root here | 
| 545 |  |  |  |  |  |  | because of cases (e.g. the law of cosines) where we would just have | 
| 546 |  |  |  |  |  |  | to square the result again. | 
| 547 |  |  |  |  |  |  |  | 
| 548 |  |  |  |  |  |  | B that the subroutine does B assume three-dimensional | 
| 549 |  |  |  |  |  |  | coordinates. If @coord1 and @coord2 have six entries, you will get a | 
| 550 |  |  |  |  |  |  | six-dimensional distance. | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | =cut | 
| 553 |  |  |  |  |  |  |  | 
| 554 |  |  |  |  |  |  | sub distsq { | 
| 555 | 1 |  |  | 1 | 1 | 532 | my ( $x, $y ) = @_; | 
| 556 |  |  |  |  |  |  | ARRAY_REF eq ref $x | 
| 557 |  |  |  |  |  |  | and ARRAY_REF eq ref $y | 
| 558 | 1 | 50 | 33 |  |  | 10 | and @{ $x } == @{ $y } | 
|  | 1 |  | 33 |  |  | 4 |  | 
|  | 1 |  |  |  |  | 5 |  | 
| 559 |  |  |  |  |  |  | or confess <<'EOD'; | 
| 560 |  |  |  |  |  |  | Programming error - Both arguments to distsq must be references to | 
| 561 |  |  |  |  |  |  | arrays of the same length. | 
| 562 |  |  |  |  |  |  | EOD | 
| 563 |  |  |  |  |  |  |  | 
| 564 | 1 |  |  |  |  | 3 | my $sum = 0; | 
| 565 | 1 |  |  |  |  | 2 | my $size = @$x; | 
| 566 | 1 |  |  |  |  | 75 | for (my $inx = 0; $inx < $size; $inx++) { | 
| 567 | 2 |  |  |  |  | 6 | my $delta = $x->[$inx] - $y->[$inx]; | 
| 568 | 2 |  |  |  |  | 5 | $sum += $delta * $delta; | 
| 569 |  |  |  |  |  |  | } | 
| 570 | 1 |  |  |  |  | 4 | return $sum | 
| 571 |  |  |  |  |  |  | } | 
| 572 |  |  |  |  |  |  |  | 
| 573 |  |  |  |  |  |  | =item $seconds = dynamical_delta ($time); | 
| 574 |  |  |  |  |  |  |  | 
| 575 |  |  |  |  |  |  | This method returns the difference between dynamical and universal time | 
| 576 |  |  |  |  |  |  | at the given universal time. That is, | 
| 577 |  |  |  |  |  |  |  | 
| 578 |  |  |  |  |  |  | $dynamical = $time + dynamical_delta ($time) | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  | if $time is universal time. | 
| 581 |  |  |  |  |  |  |  | 
| 582 |  |  |  |  |  |  | The algorithm is from Jean Meeus' "Astronomical Algorithms", 2nd | 
| 583 |  |  |  |  |  |  | Edition, Chapter 10, page 78. Meeus notes that this is actually an | 
| 584 |  |  |  |  |  |  | observed quantity, and the algorithm is an approximation. | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | =cut | 
| 587 |  |  |  |  |  |  |  | 
| 588 |  |  |  |  |  |  | sub dynamical_delta { | 
| 589 | 31458 |  |  | 31458 | 1 | 59634 | my ($time) = @_; | 
| 590 | 31458 |  |  |  |  | 113016 | my $year = (gmtime $time)[5] + 1900; | 
| 591 | 31458 |  |  |  |  | 65675 | my $t = ($year - 2000) / 100; | 
| 592 | 31458 |  |  |  |  | 52713 | my $correction = .37 * ($year - 2100);	# Meeus' correction to (10.2) | 
| 593 | 31458 |  |  |  |  | 122736 | return (25.3 * $t + 102) * $t + 102		# Meeus (10.2) | 
| 594 |  |  |  |  |  |  | + $correction;			# Meeus' correction. | 
| 595 |  |  |  |  |  |  | } | 
| 596 |  |  |  |  |  |  |  | 
| 597 |  |  |  |  |  |  | =item $boolean = embodies ($thingy, $class) | 
| 598 |  |  |  |  |  |  |  | 
| 599 |  |  |  |  |  |  | This subroutine represents a safe way to call the 'represents' method on | 
| 600 |  |  |  |  |  |  | $thingy. You get back true if and only if $thingy->can('represents') | 
| 601 |  |  |  |  |  |  | does not throw an exception and returns true, and | 
| 602 |  |  |  |  |  |  | $thingy->represents($class) returns true. Otherwise it returns false. | 
| 603 |  |  |  |  |  |  | Any exception is trapped and dismissed. | 
| 604 |  |  |  |  |  |  |  | 
| 605 |  |  |  |  |  |  | This subroutine is called 'embodies' because it was too confusing to | 
| 606 |  |  |  |  |  |  | call it 'represents', both for the author and for the Perl interpreter. | 
| 607 |  |  |  |  |  |  |  | 
| 608 |  |  |  |  |  |  | =cut | 
| 609 |  |  |  |  |  |  |  | 
| 610 |  |  |  |  |  |  | sub embodies { | 
| 611 | 41866 |  |  | 41866 | 1 | 72494 | my ($thingy, $class) = @_; | 
| 612 | 41866 |  |  |  |  | 61092 | my $code = eval {$thingy->can('represents')}; | 
|  | 41866 |  |  |  |  | 134780 |  | 
| 613 | 41866 | 100 |  |  |  | 117553 | return $code ? $code->($thingy, $class) : undef; | 
| 614 |  |  |  |  |  |  | } | 
| 615 |  |  |  |  |  |  |  | 
| 616 |  |  |  |  |  |  | =item ($sec, $min, $hr, $day, $mon, $yr, $wday, $yday, 0) = epoch2datetime ($epoch) | 
| 617 |  |  |  |  |  |  |  | 
| 618 |  |  |  |  |  |  | This convenience subroutine converts the given time in seconds from the | 
| 619 |  |  |  |  |  |  | system epoch to the corresponding date and time. It is implemented in | 
| 620 |  |  |  |  |  |  | terms of jd2date (), with the year and month returned from that | 
| 621 |  |  |  |  |  |  | subroutine. The day is a whole number, with the fractional part | 
| 622 |  |  |  |  |  |  | converted to hours, minutes, and seconds. The $wday is the day of the | 
| 623 |  |  |  |  |  |  | week, with Sunday being 0. The $yday is the day of the year, with | 
| 624 |  |  |  |  |  |  | January 1 being 0. The trailing 0 is the summer time (or daylight saving | 
| 625 |  |  |  |  |  |  | time) indicator which is always 0 to be consistent with gmtime. | 
| 626 |  |  |  |  |  |  |  | 
| 627 |  |  |  |  |  |  | If called in scalar context, it returns the date formatted by | 
| 628 |  |  |  |  |  |  | POSIX::strftime, using the format string in $DATETIMEFORMAT. | 
| 629 |  |  |  |  |  |  |  | 
| 630 |  |  |  |  |  |  | The functionality is the same as the core C, but this function | 
| 631 |  |  |  |  |  |  | lacks gmtime's limited date range under Perls before 5.12.0. If you have | 
| 632 |  |  |  |  |  |  | Perl 5.12.0 or better, the core C will probably do what you | 
| 633 |  |  |  |  |  |  | want.  If you have an earlier Perl, L | 
| 634 |  |  |  |  |  |  | C may do what you want. | 
| 635 |  |  |  |  |  |  |  | 
| 636 |  |  |  |  |  |  | The input must convert to a non-negative Julian date. The exact lower | 
| 637 |  |  |  |  |  |  | limit depends on the system, but is computed by -(JD_OF_EPOCH * 86400). | 
| 638 |  |  |  |  |  |  | For Unix systems with an epoch of January 1 1970, this is -210866760000. | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | Additional algorithms for day of week and day of year come from Jean | 
| 641 |  |  |  |  |  |  | Meeus' "Astronomical Algorithms", 2nd Edition, Chapter 7 (Julian Day), | 
| 642 |  |  |  |  |  |  | page 65. | 
| 643 |  |  |  |  |  |  |  | 
| 644 |  |  |  |  |  |  | =cut | 
| 645 |  |  |  |  |  |  |  | 
| 646 |  |  |  |  |  |  | sub epoch2datetime { | 
| 647 | 27 |  |  | 27 | 1 | 2990 | my ($time) = @_; | 
| 648 | 27 |  |  |  |  | 86 | my $day = floor ($time / SECSPERDAY); | 
| 649 | 27 |  |  |  |  | 54 | my $sec = $time - $day * SECSPERDAY; | 
| 650 | 27 |  |  |  |  | 58 | ($day, my $mon, my $yr, undef, my $leap) = jd2date ( | 
| 651 |  |  |  |  |  |  | my $jd = $day + JD_OF_EPOCH); | 
| 652 | 27 |  |  |  |  | 71 | $day = floor ($day + .5); | 
| 653 | 27 |  |  |  |  | 56 | my $min = floor ($sec / 60); | 
| 654 | 27 |  |  |  |  | 46 | $sec = $sec - $min * 60; | 
| 655 | 27 |  |  |  |  | 53 | my $hr = floor ($min / 60); | 
| 656 | 27 |  |  |  |  | 42 | $min = $min - $hr * 60; | 
| 657 | 27 |  |  |  |  | 46 | my $wday = ($jd + 1.5) % 7; | 
| 658 | 27 |  |  |  |  | 85 | my $yd = floor (275 * ($mon + 1) / 9) - (2 - $leap) * floor (($mon + | 
| 659 |  |  |  |  |  |  | 10) / 12) + $day - 31; | 
| 660 | 27 | 50 |  |  |  | 132 | wantarray and return ($sec, $min, $hr, $day, $mon, $yr, $wday, $yd, | 
| 661 |  |  |  |  |  |  | 0); | 
| 662 | 0 |  |  |  |  | 0 | return strftime ($DATETIMEFORMAT, $sec, $min, $hr, $day, $mon, $yr, | 
| 663 |  |  |  |  |  |  | $wday, $yd, 0); | 
| 664 |  |  |  |  |  |  | } | 
| 665 |  |  |  |  |  |  |  | 
| 666 |  |  |  |  |  |  | =item $time = find_first_true ($start, $end, \&test, $limit); | 
| 667 |  |  |  |  |  |  |  | 
| 668 |  |  |  |  |  |  | This function finds the first time between $start and $end for which | 
| 669 |  |  |  |  |  |  | test ($time) is true. The resolution is $limit, which defaults to 1 if | 
| 670 |  |  |  |  |  |  | not specified. If the times are reversed (i.e. the start time is after | 
| 671 |  |  |  |  |  |  | the end time) the time returned is the last time test ($time) is true. | 
| 672 |  |  |  |  |  |  |  | 
| 673 |  |  |  |  |  |  | The C function is called with the Perl time as its only | 
| 674 |  |  |  |  |  |  | argument. It is assumed to be false for the first part of the interval, | 
| 675 |  |  |  |  |  |  | and true for the rest. If this assumption is violated, the result of | 
| 676 |  |  |  |  |  |  | this subroutine should be considered meaningless. | 
| 677 |  |  |  |  |  |  |  | 
| 678 |  |  |  |  |  |  | The calculation is done by, essentially, a binary search; the interval | 
| 679 |  |  |  |  |  |  | is repeatedly split, the function is evaluated at the midpoint, and a | 
| 680 |  |  |  |  |  |  | new interval selected based on whether the result is true or false. | 
| 681 |  |  |  |  |  |  |  | 
| 682 |  |  |  |  |  |  | Actually, nothing in this function says the independent variable has to | 
| 683 |  |  |  |  |  |  | be time. | 
| 684 |  |  |  |  |  |  |  | 
| 685 |  |  |  |  |  |  | =cut | 
| 686 |  |  |  |  |  |  |  | 
| 687 |  |  |  |  |  |  | sub find_first_true { | 
| 688 | 421 |  |  | 421 | 1 | 1866 | my ($begin, $end, $test, $limit) = @_; | 
| 689 | 421 |  | 100 |  |  | 1890 | $limit ||= 1; | 
| 690 | 421 | 50 |  |  |  | 1239 | defined $begin | 
| 691 |  |  |  |  |  |  | or confess 'Programming error - $begin undefined'; | 
| 692 | 421 | 50 |  |  |  | 1017 | defined $end | 
| 693 |  |  |  |  |  |  | or confess 'Programming error - $end undefined'; | 
| 694 | 421 | 100 |  |  |  | 1160 | if ($limit >= 1) { | 
| 695 | 375 | 50 |  |  |  | 811 | if ($begin <= $end) { | 
| 696 | 375 |  |  |  |  | 1203 | $begin = floor ($begin); | 
| 697 | 375 | 50 |  |  |  | 1378 | $end = floor ($end) == $end ? $end : floor ($end) + 1; | 
| 698 |  |  |  |  |  |  | } else { | 
| 699 | 0 |  |  |  |  | 0 | $end = floor ($end); | 
| 700 | 0 | 0 |  |  |  | 0 | $begin = floor ($begin) == $begin ? $begin : floor ($begin) + 1; | 
| 701 |  |  |  |  |  |  | } | 
| 702 |  |  |  |  |  |  | } | 
| 703 |  |  |  |  |  |  | my $iterator = ( | 
| 704 |  |  |  |  |  |  | $end > $begin ? | 
| 705 | 3993 |  |  | 3993 |  | 11338 | sub {$end - $begin > $limit} : | 
| 706 | 0 |  |  | 0 |  | 0 | sub {$begin - $end > $limit} | 
| 707 | 421 | 50 |  |  |  | 1976 | ); | 
| 708 | 421 |  |  |  |  | 1053 | while ($iterator->()) { | 
| 709 | 3572 | 100 |  |  |  | 11222 | my $mid = $limit >= 1 ? | 
| 710 |  |  |  |  |  |  | floor (($begin + $end) / 2) : ($begin + $end) / 2; | 
| 711 | 3572 | 100 |  |  |  | 8338 | ($begin, $end) = ($test->($mid)) ? | 
| 712 |  |  |  |  |  |  | ($begin, $mid) : ($mid, $end); | 
| 713 |  |  |  |  |  |  | } | 
| 714 | 421 |  |  |  |  | 2544 | return $end; | 
| 715 |  |  |  |  |  |  | } | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  | =item $folded = fold_case( $text ); | 
| 718 |  |  |  |  |  |  |  | 
| 719 |  |  |  |  |  |  | This function folds the case of its input, kinda sorta. It maps to | 
| 720 |  |  |  |  |  |  | C if that is available, otherwise it maps to C. | 
| 721 |  |  |  |  |  |  |  | 
| 722 |  |  |  |  |  |  | =cut | 
| 723 |  |  |  |  |  |  |  | 
| 724 |  |  |  |  |  |  | *fold_case = CORE->can( 'fc' ) || sub ($) { return lc $_[0] }; | 
| 725 |  |  |  |  |  |  |  | 
| 726 |  |  |  |  |  |  | =item $fmtd = format_space_track_json_time( time() ) | 
| 727 |  |  |  |  |  |  |  | 
| 728 |  |  |  |  |  |  | This function takes as input a Perl time, and returns that time | 
| 729 |  |  |  |  |  |  | in a format consistent with the Space Track JSON data. This is | 
| 730 |  |  |  |  |  |  | ISO-8601-ish, in Universal time, but without the zone indicated. | 
| 731 |  |  |  |  |  |  |  | 
| 732 |  |  |  |  |  |  | B that Space Track does not represent fractional seconds, even in | 
| 733 |  |  |  |  |  |  | the epoch. This subroutine deals with this by truncating the epoch to | 
| 734 |  |  |  |  |  |  | seconds, and leaving the fractional seconds to the caller to deal with. | 
| 735 |  |  |  |  |  |  |  | 
| 736 |  |  |  |  |  |  | =cut | 
| 737 |  |  |  |  |  |  |  | 
| 738 |  |  |  |  |  |  | sub format_space_track_json_time { | 
| 739 | 1 |  |  | 1 | 1 | 550 | my ( $time ) = @_; | 
| 740 | 1 | 50 | 33 |  |  | 10 | defined $time | 
| 741 |  |  |  |  |  |  | and $time =~ m/ \S /smx | 
| 742 |  |  |  |  |  |  | or return; | 
| 743 | 1 |  |  |  |  | 7 | my ( undef, $sec ) = modf( $time ); | 
| 744 | 1 |  |  |  |  | 9 | my @parts = ( gmtime $sec )[ reverse 0 .. 5 ]; | 
| 745 | 1 |  |  |  |  | 3 | $parts[0] += 1900; | 
| 746 | 1 |  |  |  |  | 3 | $parts[1] += 1; | 
| 747 | 1 |  |  |  |  | 7 | return sprintf '%04d-%02d-%02d %02d:%02d:%02d', @parts; | 
| 748 |  |  |  |  |  |  | } | 
| 749 |  |  |  |  |  |  |  | 
| 750 |  |  |  |  |  |  | =item $fmtd = __format_epoch_time_usec( time(), '%F %T' ) | 
| 751 |  |  |  |  |  |  |  | 
| 752 |  |  |  |  |  |  | This function takes as input a Perl time with a possible fractional | 
| 753 |  |  |  |  |  |  | part, and returns that time as GMT in the given C format, but | 
| 754 |  |  |  |  |  |  | with seconds expressed to the nearest microsecond. | 
| 755 |  |  |  |  |  |  |  | 
| 756 |  |  |  |  |  |  | =cut | 
| 757 |  |  |  |  |  |  |  | 
| 758 |  |  |  |  |  |  | { | 
| 759 |  |  |  |  |  |  | # The test of this (which uses format '%F %T') failed under Windows, | 
| 760 |  |  |  |  |  |  | # at least undef Strawberry, returning the empty string. Expanding | 
| 761 |  |  |  |  |  |  | # %F fixed this, so I decided to expand all the 'equivalent to' | 
| 762 |  |  |  |  |  |  | # format strings I could find. | 
| 763 |  |  |  |  |  |  | my %equiv = ( | 
| 764 |  |  |  |  |  |  | 'D'	=> 'm/%d/%y', | 
| 765 |  |  |  |  |  |  | 'F'	=> 'Y-%m-%d', | 
| 766 |  |  |  |  |  |  | 'r'	=> 'I:%M:%S %p', | 
| 767 |  |  |  |  |  |  | 'R'	=> 'H:%M', | 
| 768 |  |  |  |  |  |  | 'T'	=> 'H:%M:%S', | 
| 769 |  |  |  |  |  |  | 'V'	=> 'e-%b-%Y', | 
| 770 |  |  |  |  |  |  | ); | 
| 771 |  |  |  |  |  |  |  | 
| 772 |  |  |  |  |  |  | sub __format_epoch_time_usec { | 
| 773 | 1 |  |  | 1 |  | 604 | my ( $epoch, $date_format ) = @_; | 
| 774 | 1 |  |  |  |  | 5 | my ( $microseconds, $seconds ) = modf( $epoch ); | 
| 775 | 1 |  |  |  |  | 5 | my @parts = gmtime $seconds; | 
| 776 | 1 |  |  |  |  | 9 | my $string_us = sprintf '%.6f', $parts[0] + $microseconds; | 
| 777 | 1 |  |  |  |  | 8 | $string_us =~ s/ [^.]* //smx; | 
| 778 | 1 | 50 |  |  |  | 8 | $date_format =~ s{ ( %+ ) ( [DFrRTV] ) } | 
|  | 2 |  |  |  |  | 14 |  | 
| 779 | 1 | 50 |  |  |  | 7 | { length( $1 ) % 2 ?  "$1$equiv{$2}" : "$1$2" }smxge; | 
|  | 1 |  |  |  |  | 6 |  | 
| 780 | 1 |  |  |  |  | 69 | $date_format =~ s{ ( %+ ) S } | 
| 781 |  |  |  |  |  |  | { length( $1 ) % 2 ?  "${1}S$string_us" : "$1$2" }smxge; | 
| 782 |  |  |  |  |  |  | return strftime( $date_format, @parts ); | 
| 783 |  |  |  |  |  |  | } | 
| 784 |  |  |  |  |  |  | } | 
| 785 |  |  |  |  |  |  |  | 
| 786 |  |  |  |  |  |  | =item $epoch = greg_time_gm( $sec, $min, $hr, $day, $mon, $yr ); | 
| 787 |  |  |  |  |  |  |  | 
| 788 |  |  |  |  |  |  | This exportable subroutine is a wrapper for either | 
| 789 |  |  |  |  |  |  | C (if that module is installed), | 
| 790 |  |  |  |  |  |  | C (if that is available), or | 
| 791 |  |  |  |  |  |  | C (if not.) | 
| 792 |  |  |  |  |  |  |  | 
| 793 |  |  |  |  |  |  | This subroutine interprets years as Gregorian years. | 
| 794 |  |  |  |  |  |  |  | 
| 795 |  |  |  |  |  |  | The difference between this and C is that C | 
| 796 |  |  |  |  |  |  | interprets the year the way C does.  For that | 
| 797 |  |  |  |  |  |  | reason, this subroutine is preferred over c. | 
| 798 |  |  |  |  |  |  |  | 
| 799 |  |  |  |  |  |  | This wrapper is needed because the routines have subtly different | 
| 800 |  |  |  |  |  |  | signatures. L C interprets years | 
| 801 |  |  |  |  |  |  | strictly as Perl years. L C | 
| 802 |  |  |  |  |  |  | interprets them strictly as Gregorian years. L | 
| 803 |  |  |  |  |  |  | C interprets them differently depending on the value of the | 
| 804 |  |  |  |  |  |  | year. Years greater than or equal to 1000 are Gregorian years, but all | 
| 805 |  |  |  |  |  |  | others are Perl years, except for the range 0 to 99 inclusive, which are | 
| 806 |  |  |  |  |  |  | within 50 years of the current year. | 
| 807 |  |  |  |  |  |  |  | 
| 808 |  |  |  |  |  |  | If you are doing historical calculations, see | 
| 809 |  |  |  |  |  |  | L | 
| 810 |  |  |  |  |  |  | in the L documentation | 
| 811 |  |  |  |  |  |  | for a discussion of input and output time conversion. | 
| 812 |  |  |  |  |  |  |  | 
| 813 |  |  |  |  |  |  | =item $epoch = greg_time_local( $sec, $min, $hr, $day, $mon, $yr ); | 
| 814 |  |  |  |  |  |  |  | 
| 815 |  |  |  |  |  |  | This exportable subroutine is a wrapper for either | 
| 816 |  |  |  |  |  |  | C (if that module is installed), | 
| 817 |  |  |  |  |  |  | C (if that is available), or | 
| 818 |  |  |  |  |  |  | C (if not.) | 
| 819 |  |  |  |  |  |  |  | 
| 820 |  |  |  |  |  |  | This subroutine interprets years as Gregorian years. | 
| 821 |  |  |  |  |  |  |  | 
| 822 |  |  |  |  |  |  | The difference between this and c is that C | 
| 823 |  |  |  |  |  |  | interprets the year the way C does.  For that | 
| 824 |  |  |  |  |  |  | reason, this subroutine is preferred over c. | 
| 825 |  |  |  |  |  |  |  | 
| 826 |  |  |  |  |  |  | This wrapper is needed for the same reason C is | 
| 827 |  |  |  |  |  |  | needed. | 
| 828 |  |  |  |  |  |  |  | 
| 829 |  |  |  |  |  |  | If you are doing historical calculations, see | 
| 830 |  |  |  |  |  |  | L | 
| 831 |  |  |  |  |  |  | in the L documentation | 
| 832 |  |  |  |  |  |  | for a discussion of input and output time conversion. | 
| 833 |  |  |  |  |  |  |  | 
| 834 |  |  |  |  |  |  | =item $difference = intensity_to_magnitude ($ratio) | 
| 835 |  |  |  |  |  |  |  | 
| 836 |  |  |  |  |  |  | This function converts a ratio of light intensities to a difference in | 
| 837 |  |  |  |  |  |  | stellar magnitudes. The algorithm comes from Jean Meeus' "Astronomical | 
| 838 |  |  |  |  |  |  | Algorithms", Second Edition, Chapter 56, Page 395. | 
| 839 |  |  |  |  |  |  |  | 
| 840 |  |  |  |  |  |  | Note that, because of the way magnitudes work (a more negative number | 
| 841 |  |  |  |  |  |  | represents a brighter star) you get back a positive result for an | 
| 842 |  |  |  |  |  |  | intensity ratio less than 1, and a negative result for an intensity | 
| 843 |  |  |  |  |  |  | ratio greater than 1. | 
| 844 |  |  |  |  |  |  |  | 
| 845 |  |  |  |  |  |  | =cut | 
| 846 |  |  |  |  |  |  |  | 
| 847 |  |  |  |  |  |  | {	# Begin local symbol block | 
| 848 | 1 |  | 50 | 1 | 1 | 484 | my $intensity_to_mag_factor;	# Calculate only if needed. | 
| 849 |  |  |  |  |  |  | sub intensity_to_magnitude { | 
| 850 |  |  |  |  |  |  | return - ($intensity_to_mag_factor ||= 2.5 / log (10)) * log ($_[0]); | 
| 851 |  |  |  |  |  |  | } | 
| 852 |  |  |  |  |  |  | } | 
| 853 |  |  |  |  |  |  |  | 
| 854 |  |  |  |  |  |  | =item ($day, $mon, $yr, $greg, $leap) = jd2date ($jd) | 
| 855 |  |  |  |  |  |  |  | 
| 856 |  |  |  |  |  |  | This subroutine converts the given Julian day to the corresponding date. | 
| 857 |  |  |  |  |  |  | The returns are year - 1900, month (0 to 11), day (which may have a | 
| 858 |  |  |  |  |  |  | fractional part), a Gregorian calendar indicator which is true if the | 
| 859 |  |  |  |  |  |  | date is in the Gregorian calendar and false if it is in the Julian | 
| 860 |  |  |  |  |  |  | calendar, and a leap (or bissextile) year indicator which is true if the | 
| 861 |  |  |  |  |  |  | year is a leap year and false otherwise. The year 1 BC is returned as | 
| 862 |  |  |  |  |  |  | -1900 (i.e. as year 0), and so on. The date will probably have a | 
| 863 |  |  |  |  |  |  | fractional part (e.g. 2006 1 1.5 for noon January first 2006). | 
| 864 |  |  |  |  |  |  |  | 
| 865 |  |  |  |  |  |  | If the $jd is before $JD_GREGORIAN, the date will be in the Julian | 
| 866 |  |  |  |  |  |  | calendar; otherwise it will be in the Gregorian calendar. | 
| 867 |  |  |  |  |  |  |  | 
| 868 |  |  |  |  |  |  | The input may not be less than 0. | 
| 869 |  |  |  |  |  |  |  | 
| 870 |  |  |  |  |  |  | The algorithm is from Jean Meeus' "Astronomical Algorithms", second | 
| 871 |  |  |  |  |  |  | edition, chapter 7 ("Julian Day"), pages 63ff, but the month is | 
| 872 |  |  |  |  |  |  | zero-based, not 1-based, and the year is 1900-based. | 
| 873 |  |  |  |  |  |  |  | 
| 874 |  |  |  |  |  |  | =cut | 
| 875 | 36 |  |  | 36 | 1 | 4489 |  | 
| 876 | 36 |  |  |  |  | 56 | sub jd2date { | 
| 877 | 36 |  |  |  |  | 79 | my ($time) = @_; | 
| 878 | 36 |  |  |  |  | 61 | my $mod_jd = $time + 0.5; | 
| 879 | 36 | 100 |  |  |  | 89 | my $Z = floor ($mod_jd); | 
| 880 | 30 |  |  |  |  | 63 | my $F = $mod_jd - $Z; | 
| 881 | 30 |  |  |  |  | 95 | my $A = (my $julian = $Z < $JD_GREGORIAN) ? $Z : do { | 
| 882 |  |  |  |  |  |  | my $alpha = floor (($Z - 1867216.25)/36524.25); | 
| 883 | 36 |  |  |  |  | 56 | $Z + 1 + $alpha - floor ($alpha / 4); | 
| 884 | 36 |  |  |  |  | 72 | }; | 
| 885 | 36 |  |  |  |  | 71 | my $B = $A + 1524; | 
| 886 | 36 |  |  |  |  | 72 | my $C = floor (($B - 122.1) / 365.25); | 
| 887 | 36 |  |  |  |  | 86 | my $D = floor (365.25 * $C); | 
| 888 | 36 | 100 |  |  |  | 78 | my $E = floor (($B - $D) / 30.6001); | 
| 889 | 36 | 100 |  |  |  | 77 | my $day = $B - $D - floor (30.6001 * $E) + $F; | 
| 890 | 36 | 50 |  |  |  | 1173 | my $mon = $E < 14 ? $E - 1 : $E - 13; | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 891 |  |  |  |  |  |  | my $yr = $mon > 2 ? $C - 4716 : $C - 4715; | 
| 892 |  |  |  |  |  |  | return ($day, $mon - 1, $yr - 1900, !$julian, ($julian ? !($yr % 4) : ( | 
| 893 |  |  |  |  |  |  | $yr % 400 ? $yr % 100 ? !($yr % 4) : 0 : 1))); | 
| 894 |  |  |  |  |  |  | ##	% 400 ? 1 : $yr % 100 ? 0 : !($yr % 4)))); | 
| 895 |  |  |  |  |  |  | } | 
| 896 |  |  |  |  |  |  |  | 
| 897 |  |  |  |  |  |  | =item ($sec, $min, $hr, $day, $mon, $yr, $wday, $yday, 0) = jd2datetime ($jd) | 
| 898 |  |  |  |  |  |  |  | 
| 899 |  |  |  |  |  |  | This convenience subroutine converts the given Julian day to the | 
| 900 |  |  |  |  |  |  | corresponding date and time. All this really does is converts its | 
| 901 |  |  |  |  |  |  | argument to seconds since the system epoch, and pass off to | 
| 902 |  |  |  |  |  |  | epoch2datetime(). | 
| 903 |  |  |  |  |  |  |  | 
| 904 |  |  |  |  |  |  | The input may not be less than 0. | 
| 905 |  |  |  |  |  |  |  | 
| 906 |  |  |  |  |  |  | =cut | 
| 907 | 21 |  |  | 21 | 1 | 10461 |  | 
| 908 | 21 |  |  |  |  | 56 | sub jd2datetime { | 
| 909 |  |  |  |  |  |  | my ($time) = @_; | 
| 910 |  |  |  |  |  |  | return epoch2datetime(($time - JD_OF_EPOCH) * SECSPERDAY); | 
| 911 |  |  |  |  |  |  | } | 
| 912 |  |  |  |  |  |  |  | 
| 913 |  |  |  |  |  |  | =item $century = jcent2000 ($time); | 
| 914 |  |  |  |  |  |  |  | 
| 915 |  |  |  |  |  |  | Several of the algorithms in Jean Meeus' "Astronomical Algorithms" | 
| 916 |  |  |  |  |  |  | are expressed in terms of the number of Julian centuries from epoch | 
| 917 |  |  |  |  |  |  | J2000.0 (e.g equations 12.1, 22.1). This subroutine encapsulates | 
| 918 |  |  |  |  |  |  | that calculation. | 
| 919 |  |  |  |  |  |  |  | 
| 920 | 74290 |  |  | 74290 | 1 | 134102 | =cut | 
| 921 |  |  |  |  |  |  |  | 
| 922 |  |  |  |  |  |  | sub jcent2000 {return jday2000 ($_[0]) / 36525} | 
| 923 |  |  |  |  |  |  |  | 
| 924 |  |  |  |  |  |  | =item $jd = jday2000 ($time); | 
| 925 |  |  |  |  |  |  |  | 
| 926 |  |  |  |  |  |  | This subroutine converts a Perl date to the number of Julian days | 
| 927 |  |  |  |  |  |  | (and fractions thereof) since Julian 2000.0. This quantity is used | 
| 928 |  |  |  |  |  |  | in a number of the algorithms in Jean Meeus' "Astronomical | 
| 929 |  |  |  |  |  |  | Algorithms". | 
| 930 |  |  |  |  |  |  |  | 
| 931 |  |  |  |  |  |  | The computation makes use of information from Jean Meeus' "Astronomical | 
| 932 |  |  |  |  |  |  | Algorithms", 2nd Edition, Chapter 7, page 62. | 
| 933 |  |  |  |  |  |  |  | 
| 934 | 104229 |  |  | 104229 | 1 | 255498 | =cut | 
| 935 |  |  |  |  |  |  |  | 
| 936 |  |  |  |  |  |  | sub jday2000 {return ($_[0] - PERL2000) / SECSPERDAY} | 
| 937 |  |  |  |  |  |  |  | 
| 938 |  |  |  |  |  |  | =item $jd = julianday ($time); | 
| 939 |  |  |  |  |  |  |  | 
| 940 |  |  |  |  |  |  | This subroutine converts a Perl date to a Julian day number. | 
| 941 |  |  |  |  |  |  |  | 
| 942 |  |  |  |  |  |  | The computation makes use of information from Jean Meeus' "Astronomical | 
| 943 |  |  |  |  |  |  | Algorithms", 2nd Edition, Chapter 7, page 62. | 
| 944 |  |  |  |  |  |  |  | 
| 945 | 2 |  |  | 2 | 1 | 994 | =cut | 
| 946 |  |  |  |  |  |  |  | 
| 947 |  |  |  |  |  |  | sub julianday {return jday2000($_[0]) + 2_451_545.0} | 
| 948 |  |  |  |  |  |  |  | 
| 949 |  |  |  |  |  |  | =item $ea = keplers_equation( $M, $e, $prec ); | 
| 950 |  |  |  |  |  |  |  | 
| 951 |  |  |  |  |  |  | This subroutine solves Kepler's equation for the given mean anomaly | 
| 952 |  |  |  |  |  |  | C<$M> in radians, eccentricity C<$e> and precision C<$prec> in radians. | 
| 953 |  |  |  |  |  |  | It returns the eccentric anomaly in radians, to the given precision. | 
| 954 |  |  |  |  |  |  |  | 
| 955 |  |  |  |  |  |  | The C<$prec> argument is optional, and defaults to the radian equivalent | 
| 956 |  |  |  |  |  |  | of 0.001 degrees. | 
| 957 |  |  |  |  |  |  |  | 
| 958 |  |  |  |  |  |  | The algorithm is Meeus' equation 30.7, with John M. Steele's amendment | 
| 959 |  |  |  |  |  |  | for large values for the correction, given on page 205 of Meeus' book, | 
| 960 |  |  |  |  |  |  |  | 
| 961 |  |  |  |  |  |  | This subroutine is B used in the computation of satellite orbits, | 
| 962 |  |  |  |  |  |  | since the models have their own implementation. | 
| 963 |  |  |  |  |  |  |  | 
| 964 |  |  |  |  |  |  | =cut | 
| 965 | 1 |  |  | 1 | 1 | 505 |  | 
| 966 | 1 | 50 |  |  |  | 6 | sub keplers_equation { | 
| 967 |  |  |  |  |  |  | my ( $mean_anomaly, $eccentricity, $precision ) = @_; | 
| 968 | 1 |  |  |  |  | 2 | defined $precision | 
| 969 | 1 |  |  |  |  | 3 | or $precision = 1.74533e-5;	# Radians, = 0.001 degrees | 
| 970 |  |  |  |  |  |  | my $curr = $mean_anomaly; | 
| 971 |  |  |  |  |  |  | my $prev; | 
| 972 | 1 |  |  |  |  | 2 | # Meeus' equation 30.7, page 199. | 
|  | 3 |  |  |  |  | 4 |  | 
| 973 | 3 |  |  |  |  | 12 | { | 
| 974 |  |  |  |  |  |  | $prev = $curr; | 
| 975 |  |  |  |  |  |  | my $delta = ( $mean_anomaly + $eccentricity * sin( $curr | 
| 976 | 3 |  |  |  |  | 11 | ) - $curr ) / ( 1 - $eccentricity * cos $curr ); | 
| 977 | 3 | 100 |  |  |  | 9 | # Steele's correction, page 205 | 
| 978 |  |  |  |  |  |  | $curr = $curr + max( -.5, min( .5, $delta ) ); | 
| 979 |  |  |  |  |  |  | $precision < abs( $curr - $prev ) | 
| 980 | 1 |  |  |  |  | 4 | and redo; | 
| 981 |  |  |  |  |  |  | } | 
| 982 |  |  |  |  |  |  | return $curr; | 
| 983 |  |  |  |  |  |  | } | 
| 984 |  |  |  |  |  |  |  | 
| 985 |  |  |  |  |  |  | =item $rslt = load_module ($module_name) | 
| 986 |  |  |  |  |  |  |  | 
| 987 |  |  |  |  |  |  | This convenience method loads the named module (using 'require'), | 
| 988 |  |  |  |  |  |  | throwing an exception if the load fails. If the load succeeds, it | 
| 989 |  |  |  |  |  |  | returns the result of the 'require' built-in, which is required to be | 
| 990 |  |  |  |  |  |  | true for a successful load.  Results are cached, and subsequent attempts | 
| 991 |  |  |  |  |  |  | to load the same module simply give the cached results. | 
| 992 |  |  |  |  |  |  |  | 
| 993 |  |  |  |  |  |  | =cut | 
| 994 |  |  |  |  |  |  |  | 
| 995 |  |  |  |  |  |  | {	# Local symbol block. Oh, for 5.10 and state variables. | 
| 996 |  |  |  |  |  |  | my %error; | 
| 997 | 243 |  |  | 243 | 1 | 480 | my %rslt; | 
| 998 | 243 | 50 |  |  |  | 585 | sub load_module { | 
| 999 | 243 | 100 |  |  |  | 740 | my  ($module) = @_; | 
| 1000 |  |  |  |  |  |  | exists $error{$module} and croak $error{$module}; | 
| 1001 |  |  |  |  |  |  | exists $rslt{$module} and return $rslt{$module}; | 
| 1002 |  |  |  |  |  |  | # I considered Module::Load here, but it appears not to support | 
| 1003 |  |  |  |  |  |  | # .pmc files. No, it's not an issue at the moment, but it may be | 
| 1004 | 48 | 50 |  |  |  | 3285 | # if Perl 6 becomes a reality. | 
| 1005 | 48 |  |  |  |  | 261 | $rslt{$module} = eval "require $module" | 
| 1006 |  |  |  |  |  |  | or croak( $error{$module} = $@ ); | 
| 1007 |  |  |  |  |  |  | return $rslt{$module}; | 
| 1008 |  |  |  |  |  |  | } | 
| 1009 |  |  |  |  |  |  | }	# End local symbol block. | 
| 1010 |  |  |  |  |  |  |  | 
| 1011 |  |  |  |  |  |  | =item $boolean = looks_like_number ($string); | 
| 1012 |  |  |  |  |  |  |  | 
| 1013 |  |  |  |  |  |  | This subroutine returns true if the input looks like a number. It uses | 
| 1014 |  |  |  |  |  |  | Scalar::Util::looks_like_number if that is available, otherwise it uses | 
| 1015 |  |  |  |  |  |  | its own code, which is lifted verbatim from Scalar::Util 1.19, which in | 
| 1016 |  |  |  |  |  |  | turn leans heavily on perlfaq4. | 
| 1017 |  |  |  |  |  |  |  | 
| 1018 |  |  |  |  |  |  | =cut | 
| 1019 |  |  |  |  |  |  |  | 
| 1020 | 18 |  |  | 18 |  | 162 | unless (eval {require Scalar::Util; Scalar::Util->import | 
|  | 18 |  |  |  |  | 44 |  | 
|  | 18 |  |  |  |  | 5389 |  | 
| 1021 |  |  |  |  |  |  | ('looks_like_number'); 1}) { | 
| 1022 |  |  |  |  |  |  | no warnings qw{once}; | 
| 1023 |  |  |  |  |  |  | *looks_like_number = sub { | 
| 1024 |  |  |  |  |  |  | local $_ = shift; | 
| 1025 |  |  |  |  |  |  |  | 
| 1026 |  |  |  |  |  |  | # checks from perlfaq4 | 
| 1027 |  |  |  |  |  |  | return 0 if !defined($_) || ref($_); | 
| 1028 |  |  |  |  |  |  | return 1 if (/^[+-]?[0-9]+$/); # is a +/- integer | 
| 1029 |  |  |  |  |  |  | return 1 if (/^([+-]?)(?=[0-9]|\.[0-9])[0-9]*(\.[0-9]*)?([Ee]([+-]?[0-9]+))?$/); # a C float | 
| 1030 |  |  |  |  |  |  | return 1 if ($] >= 5.008 and /^(Inf(inity)?|NaN)$/i) | 
| 1031 |  |  |  |  |  |  | or ($] >= 5.006001 and /^Inf$/i); | 
| 1032 |  |  |  |  |  |  |  | 
| 1033 |  |  |  |  |  |  | return 0; | 
| 1034 |  |  |  |  |  |  | }; | 
| 1035 |  |  |  |  |  |  | } | 
| 1036 |  |  |  |  |  |  |  | 
| 1037 |  |  |  |  |  |  | =item $maximum = max (...); | 
| 1038 |  |  |  |  |  |  |  | 
| 1039 |  |  |  |  |  |  | This subroutine returns the maximum of its arguments.  If List::Util can | 
| 1040 |  |  |  |  |  |  | be loaded and 'max' imported, that's what you get. Otherwise you get a | 
| 1041 |  |  |  |  |  |  | pure Perl implementation. | 
| 1042 |  |  |  |  |  |  |  | 
| 1043 |  |  |  |  |  |  | =cut | 
| 1044 | 18 |  |  | 18 |  | 149 |  | 
|  | 18 |  |  |  |  | 71 |  | 
|  | 18 |  |  |  |  | 2336 |  | 
| 1045 |  |  |  |  |  |  | unless (eval {require List::Util; List::Util->import ('max'); 1}) { | 
| 1046 |  |  |  |  |  |  | no warnings qw{once}; | 
| 1047 |  |  |  |  |  |  | *max = sub { | 
| 1048 |  |  |  |  |  |  | my $rslt; | 
| 1049 |  |  |  |  |  |  | foreach (@_) { | 
| 1050 |  |  |  |  |  |  | defined $_ or next; | 
| 1051 |  |  |  |  |  |  | if (!defined $rslt || $_ > $rslt) { | 
| 1052 |  |  |  |  |  |  | $rslt = $_; | 
| 1053 |  |  |  |  |  |  | } | 
| 1054 |  |  |  |  |  |  | } | 
| 1055 |  |  |  |  |  |  | $rslt; | 
| 1056 |  |  |  |  |  |  | }; | 
| 1057 |  |  |  |  |  |  | } | 
| 1058 |  |  |  |  |  |  |  | 
| 1059 |  |  |  |  |  |  | =item $minimum = min (...); | 
| 1060 |  |  |  |  |  |  |  | 
| 1061 |  |  |  |  |  |  | This subroutine returns the minimum of its arguments.  If List::Util can | 
| 1062 |  |  |  |  |  |  | be loaded and 'min' imported, that's what you get. Otherwise you get a | 
| 1063 |  |  |  |  |  |  | pure Perl implementation. | 
| 1064 |  |  |  |  |  |  |  | 
| 1065 |  |  |  |  |  |  | =cut | 
| 1066 | 18 |  |  | 18 |  | 157 |  | 
|  | 18 |  |  |  |  | 38 |  | 
|  | 18 |  |  |  |  | 37037 |  | 
| 1067 |  |  |  |  |  |  | unless (eval {require List::Util; List::Util->import ('min'); 1}) { | 
| 1068 |  |  |  |  |  |  | no warnings qw{once}; | 
| 1069 |  |  |  |  |  |  | *min = sub { | 
| 1070 |  |  |  |  |  |  | my $rslt; | 
| 1071 |  |  |  |  |  |  | foreach (@_) { | 
| 1072 |  |  |  |  |  |  | defined $_ or next; | 
| 1073 |  |  |  |  |  |  | if (!defined $rslt || $_ < $rslt) { | 
| 1074 |  |  |  |  |  |  | $rslt = $_; | 
| 1075 |  |  |  |  |  |  | } | 
| 1076 |  |  |  |  |  |  | } | 
| 1077 |  |  |  |  |  |  | $rslt; | 
| 1078 |  |  |  |  |  |  | }; | 
| 1079 |  |  |  |  |  |  | } | 
| 1080 |  |  |  |  |  |  |  | 
| 1081 |  |  |  |  |  |  | =item $theta = mod2pi ($theta) | 
| 1082 |  |  |  |  |  |  |  | 
| 1083 |  |  |  |  |  |  | This subroutine reduces the given angle in radians to the range 0 <= | 
| 1084 |  |  |  |  |  |  | $theta < TWOPI. | 
| 1085 |  |  |  |  |  |  |  | 
| 1086 | 177429 |  |  | 177429 | 1 | 485246 | =cut | 
| 1087 |  |  |  |  |  |  |  | 
| 1088 |  |  |  |  |  |  | sub mod2pi {return $_[0] - floor ($_[0] / TWOPI) * TWOPI} | 
| 1089 |  |  |  |  |  |  |  | 
| 1090 |  |  |  |  |  |  | =item $radians = omega ($time); | 
| 1091 |  |  |  |  |  |  |  | 
| 1092 |  |  |  |  |  |  | This subroutine calculates the ecliptic longitude of the ascending node | 
| 1093 |  |  |  |  |  |  | of the Moon's mean orbit at the given B time. | 
| 1094 |  |  |  |  |  |  |  | 
| 1095 |  |  |  |  |  |  | The algorithm comes from Jean Meeus' "Astronomical Algorithms", 2nd | 
| 1096 |  |  |  |  |  |  | Edition, Chapter 22, pages 143ff. | 
| 1097 |  |  |  |  |  |  |  | 
| 1098 |  |  |  |  |  |  | =cut | 
| 1099 | 1 |  |  | 1 | 1 | 512 |  | 
| 1100 | 1 |  |  |  |  | 7 | sub omega { | 
| 1101 |  |  |  |  |  |  | my $T = jcent2000 (shift);	# Meeus (22.1) | 
| 1102 |  |  |  |  |  |  | return mod2pi (deg2rad ((($T / 450000 + .0020708) * $T - | 
| 1103 |  |  |  |  |  |  | 1934.136261) * $T + 125.04452)); | 
| 1104 |  |  |  |  |  |  | } | 
| 1105 |  |  |  |  |  |  |  | 
| 1106 |  |  |  |  |  |  | =item $pa = position_angle( $alpha1, $delta1, $alpha2, $delta2 ); | 
| 1107 |  |  |  |  |  |  |  | 
| 1108 |  |  |  |  |  |  | This low-level subroutine calculates the position angle in right | 
| 1109 |  |  |  |  |  |  | ascension of the second body with respect to the first, given the first | 
| 1110 |  |  |  |  |  |  | body's right ascension and declination and the second body's right | 
| 1111 |  |  |  |  |  |  | ascension and declination in that order, B. | 
| 1112 |  |  |  |  |  |  |  | 
| 1113 |  |  |  |  |  |  | The return is the position angle B, in the range | 
| 1114 |  |  |  |  |  |  | C<< -PI <= $pa < PI >>. | 
| 1115 |  |  |  |  |  |  |  | 
| 1116 |  |  |  |  |  |  | The algorithm comes from Jean Meeus' "Astronomical Algorithms", 2nd | 
| 1117 |  |  |  |  |  |  | Edition, page 116, but his algorithm is for the position angle of the | 
| 1118 |  |  |  |  |  |  | first body with respect to the second (i.e. the roles of the two bodies | 
| 1119 |  |  |  |  |  |  | are reversed). The order of arguments for this subroutine is consistent | 
| 1120 |  |  |  |  |  |  | with The IDL Astronomy User's Library at | 
| 1121 |  |  |  |  |  |  | L, function C. | 
| 1122 |  |  |  |  |  |  |  | 
| 1123 |  |  |  |  |  |  | This is exposed because in principal you could calculate the position | 
| 1124 |  |  |  |  |  |  | angle in any spherical coordinate system, you would just need to get the | 
| 1125 |  |  |  |  |  |  | order of arguments right (e.g. azimuth, elevation or longitude, | 
| 1126 |  |  |  |  |  |  | latitude). | 
| 1127 |  |  |  |  |  |  |  | 
| 1128 |  |  |  |  |  |  | =cut | 
| 1129 | 1 |  |  | 1 | 1 | 1268 |  | 
| 1130 | 1 |  |  |  |  | 3 | sub position_angle { | 
| 1131 | 1 |  |  |  |  | 6 | my ( $alpha1, $delta1, $alpha2, $delta2 ) = @_; | 
| 1132 |  |  |  |  |  |  | my $delta_alpha = $alpha2 - $alpha1; | 
| 1133 |  |  |  |  |  |  | return atan2( sin( $delta_alpha ), | 
| 1134 |  |  |  |  |  |  | cos( $delta1 ) * tan( $delta2 ) - | 
| 1135 |  |  |  |  |  |  | sin( $delta1 ) * cos( $delta_alpha ) ); | 
| 1136 |  |  |  |  |  |  | } | 
| 1137 |  |  |  |  |  |  |  | 
| 1138 |  |  |  |  |  |  | =item $degrees = rad2deg ($radians) | 
| 1139 |  |  |  |  |  |  |  | 
| 1140 |  |  |  |  |  |  | This subroutine converts the given angle in radians to its equivalent | 
| 1141 |  |  |  |  |  |  | in degrees. If the argument is C, C will be returned. | 
| 1142 |  |  |  |  |  |  |  | 
| 1143 | 8361 | 100 |  | 8361 | 1 | 47385 | =cut | 
| 1144 |  |  |  |  |  |  |  | 
| 1145 |  |  |  |  |  |  | sub rad2deg { return defined $_[0] ? $_[0] / PI * 180 : undef } | 
| 1146 |  |  |  |  |  |  |  | 
| 1147 |  |  |  |  |  |  | =item $deg_min_sec = rad2dms( $radians, $decimals, $degree_sign ) | 
| 1148 |  |  |  |  |  |  |  | 
| 1149 |  |  |  |  |  |  | This subroutine converts the given angle in radians to its equivalent in | 
| 1150 |  |  |  |  |  |  | degrees, minutes and seconds, represented as a string. Degrees will be | 
| 1151 |  |  |  |  |  |  | suppressed if zero, and minutes will be suppressed if both degrees and | 
| 1152 |  |  |  |  |  |  | minutes are zero. If degrees are present the default delimiter is a | 
| 1153 |  |  |  |  |  |  | degree sign. The delimiters for minutes and seconds are C<'> and C<"> | 
| 1154 |  |  |  |  |  |  | respectively, with the C<"> appearing before the decimal point. | 
| 1155 |  |  |  |  |  |  |  | 
| 1156 |  |  |  |  |  |  | The optional C<$decimals> argument specifies the number of decimal | 
| 1157 |  |  |  |  |  |  | places in the seconds value, and defaults to C<3>. | 
| 1158 |  |  |  |  |  |  |  | 
| 1159 |  |  |  |  |  |  | The optional C<$degree_sign> argument specifies the degree sign. The | 
| 1160 |  |  |  |  |  |  | default is a Unicode degree sign, C<"\N{DEGREE SIGN}">, a.k.a. | 
| 1161 |  |  |  |  |  |  | C<"\N{U+00B0}">. | 
| 1162 |  |  |  |  |  |  |  | 
| 1163 |  |  |  |  |  |  | =cut | 
| 1164 | 2 |  |  | 2 | 1 | 949 |  | 
| 1165 | 2 | 100 |  |  |  | 7 | sub rad2dms { | 
| 1166 |  |  |  |  |  |  | my ( $rad, $dp, $ds ) = @_; | 
| 1167 | 1 | 50 |  |  |  | 12 | defined $rad | 
| 1168 |  |  |  |  |  |  | or return $rad; | 
| 1169 | 1 | 50 |  |  |  | 5 | defined $dp | 
| 1170 |  |  |  |  |  |  | or $dp = 3; | 
| 1171 | 1 |  |  |  |  | 3 | defined $ds | 
| 1172 |  |  |  |  |  |  | or $ds = "\N{U+00B0}"; | 
| 1173 | 1 | 50 |  |  |  | 4 | my $wid = $dp + 3; | 
| 1174 | 1 |  |  |  |  | 4 |  | 
| 1175 | 1 |  |  |  |  | 7 | ( $rad, my $sgn ) = $rad < 0 ? ( -$rad, '-' ) : ( $rad, '' ); | 
| 1176 | 1 |  |  |  |  | 4 | my $sec = rad2deg( $rad ); | 
| 1177 | 1 |  |  |  |  | 3 | ( $sec, my $deg ) = modf( $sec ); | 
| 1178 | 1 |  |  |  |  | 3 | ( $sec, my $min ) = modf( $sec * 60 ); | 
| 1179 | 1 | 50 |  |  |  | 3 | $sec *= 60; | 
|  |  | 0 |  |  |  |  |  | 
| 1180 | 1 |  |  |  |  | 14 | my $rslt; | 
| 1181 |  |  |  |  |  |  | if ( $deg ) { | 
| 1182 | 0 |  |  |  |  | 0 | $rslt = sprintf qq<%d$ds%02d'%$wid.${dp}f>, $deg, $min, $sec; | 
| 1183 |  |  |  |  |  |  | } elsif ( $min ) { | 
| 1184 | 0 |  |  |  |  | 0 | $rslt = sprintf qq<%d'%$wid.${dp}f>, $min, $sec; | 
| 1185 |  |  |  |  |  |  | } else { | 
| 1186 | 1 | 50 |  |  |  | 11 | $rslt = sprintf qq<%.${dp}f>, $sec; | 
| 1187 |  |  |  |  |  |  | } | 
| 1188 | 1 |  |  |  |  | 4 | $rslt =~ s/ [.] /"./smx | 
| 1189 |  |  |  |  |  |  | or $rslt .= q<">; | 
| 1190 |  |  |  |  |  |  | return "$sgn$rslt"; | 
| 1191 |  |  |  |  |  |  | } | 
| 1192 |  |  |  |  |  |  |  | 
| 1193 |  |  |  |  |  |  | =item $hr_min_sec = rad2hms( $radians, $decimals ) | 
| 1194 |  |  |  |  |  |  |  | 
| 1195 |  |  |  |  |  |  | This subroutine converts the given angle in radians to its equivalent in | 
| 1196 |  |  |  |  |  |  | hours, minutes and seconds (presumably of right ascension), represented | 
| 1197 |  |  |  |  |  |  | as a string. Hours will be suppressed if zero, and minutes will be | 
| 1198 |  |  |  |  |  |  | suppressed if both hours and minutes are zero. The delimiters for hours, | 
| 1199 |  |  |  |  |  |  | minutes, and seconds are C<'h'>, C<'m'>, and C<'s'> respectively, with | 
| 1200 |  |  |  |  |  |  | the C<'s'> appearing before the decimal point. | 
| 1201 |  |  |  |  |  |  |  | 
| 1202 |  |  |  |  |  |  | The optional C<$decimals> argument specifies the number of decimal | 
| 1203 |  |  |  |  |  |  | places in the seconds value, and defaults to C<3>. | 
| 1204 |  |  |  |  |  |  |  | 
| 1205 |  |  |  |  |  |  | =cut | 
| 1206 | 2 |  |  | 2 | 1 | 968 |  | 
| 1207 | 2 | 100 |  |  |  | 8 | sub rad2hms { | 
| 1208 |  |  |  |  |  |  | my ( $rad, $dp ) = @_; | 
| 1209 | 1 | 50 |  |  |  | 5 | defined $rad | 
| 1210 |  |  |  |  |  |  | or return $rad; | 
| 1211 |  |  |  |  |  |  | defined $dp | 
| 1212 | 1 |  |  |  |  | 3 | or $dp = 3; | 
| 1213 | 1 | 50 |  |  |  | 6 |  | 
| 1214 | 1 |  |  |  |  | 4 | my $wid = $dp + 3; | 
| 1215 | 1 |  |  |  |  | 4 | ( $rad, my $sgn ) = $rad < 0 ? ( -$rad, '-' ) : ( $rad, '' ); | 
| 1216 | 1 |  |  |  |  | 5 | my $sec = $rad * 12 / PI; | 
| 1217 | 1 |  |  |  |  | 3 | ( $sec, my $hr ) = modf( $sec ); | 
| 1218 | 1 |  |  |  |  | 2 | ( $sec, my $min ) = modf( $sec * 60 ); | 
| 1219 | 1 | 50 |  |  |  | 3 | $sec *= 60; | 
|  |  | 0 |  |  |  |  |  | 
| 1220 | 1 |  |  |  |  | 12 | my $rslt; | 
| 1221 |  |  |  |  |  |  | if ( $hr ) { | 
| 1222 | 0 |  |  |  |  | 0 | $rslt = sprintf "%dh%02dm%$wid.${dp}f", $hr, $min, $sec; | 
| 1223 |  |  |  |  |  |  | } elsif ( $min ) { | 
| 1224 | 0 |  |  |  |  | 0 | $rslt = sprintf "%dm%$wid.${dp}f", $min, $sec; | 
| 1225 |  |  |  |  |  |  | } else { | 
| 1226 | 1 | 50 |  |  |  | 7 | $rslt = sprintf "%.${dp}f", $sec; | 
| 1227 |  |  |  |  |  |  | } | 
| 1228 | 1 |  |  |  |  | 5 | $rslt =~ s/ [.] /s./smx | 
| 1229 |  |  |  |  |  |  | or $rslt .= 's'; | 
| 1230 |  |  |  |  |  |  | return "$sgn$rslt"; | 
| 1231 |  |  |  |  |  |  | } | 
| 1232 |  |  |  |  |  |  |  | 
| 1233 |  |  |  |  |  |  | =item $value = tan ($angle) | 
| 1234 |  |  |  |  |  |  |  | 
| 1235 |  |  |  |  |  |  | This subroutine computes the tangent of the given angle in radians. | 
| 1236 |  |  |  |  |  |  |  | 
| 1237 | 8068 |  |  | 8068 | 1 | 26029 | =cut | 
| 1238 |  |  |  |  |  |  |  | 
| 1239 |  |  |  |  |  |  | sub tan {return sin ($_[0]) / cos ($_[0])} | 
| 1240 |  |  |  |  |  |  |  | 
| 1241 |  |  |  |  |  |  | =item $value = theta0 ($time); | 
| 1242 |  |  |  |  |  |  |  | 
| 1243 |  |  |  |  |  |  | This subroutine returns the Greenwich hour angle of the mean equinox at | 
| 1244 |  |  |  |  |  |  | 0 hours universal on the day whose time is given (i.e. the argument is | 
| 1245 |  |  |  |  |  |  | a standard Perl time). | 
| 1246 |  |  |  |  |  |  |  | 
| 1247 |  |  |  |  |  |  | =cut | 
| 1248 | 1 |  |  | 1 | 1 | 513 |  | 
| 1249 | 1 |  |  |  |  | 7 | sub theta0 { | 
| 1250 | 1 |  |  |  |  | 3 | my ($time) = @_; | 
| 1251 | 1 |  |  |  |  | 6 | my @t = gmtime $time; | 
| 1252 |  |  |  |  |  |  | $t[5] += 1900; | 
| 1253 |  |  |  |  |  |  | return thetag( greg_time_gm( 0, 0, 0, @t[3 .. 5] ) ); | 
| 1254 |  |  |  |  |  |  | } | 
| 1255 |  |  |  |  |  |  |  | 
| 1256 |  |  |  |  |  |  | =item $value = thetag ($time); | 
| 1257 |  |  |  |  |  |  |  | 
| 1258 |  |  |  |  |  |  | This subroutine returns the Greenwich hour angle of the mean equinox at | 
| 1259 |  |  |  |  |  |  | the given time. | 
| 1260 |  |  |  |  |  |  |  | 
| 1261 |  |  |  |  |  |  | The algorithm comes from Jean Meeus' "Astronomical Algorithms", 2nd | 
| 1262 |  |  |  |  |  |  | Edition, equation 12.4, page 88. | 
| 1263 |  |  |  |  |  |  |  | 
| 1264 |  |  |  |  |  |  | =cut | 
| 1265 |  |  |  |  |  |  |  | 
| 1266 |  |  |  |  |  |  | #	Meeus, pg 88, equation 12.4, converted to radians and Perl dates. | 
| 1267 | 29935 |  |  | 29935 | 1 | 52222 |  | 
| 1268 | 29935 |  |  |  |  | 55391 | sub thetag { | 
| 1269 | 29935 |  |  |  |  | 59663 | my ($time) = @_; | 
| 1270 |  |  |  |  |  |  | my $T = jcent2000 ($time); | 
| 1271 |  |  |  |  |  |  | return mod2pi (4.89496121273579 + 6.30038809898496 * | 
| 1272 |  |  |  |  |  |  | jday2000 ($time)) | 
| 1273 |  |  |  |  |  |  | + (6.77070812713916e-06 - 4.5087296615715e-10 * $T) * $T * $T; | 
| 1274 |  |  |  |  |  |  | } | 
| 1275 |  |  |  |  |  |  |  | 
| 1276 |  |  |  |  |  |  | # time_gm and time_local are actually created at the top of the module. | 
| 1277 |  |  |  |  |  |  |  | 
| 1278 |  |  |  |  |  |  | =item $epoch = time_gm( $sec, $min, $hr, $day, $mon, $yr ); | 
| 1279 |  |  |  |  |  |  |  | 
| 1280 |  |  |  |  |  |  | This exportable subroutine is a wrapper for either | 
| 1281 |  |  |  |  |  |  | C (if that module is installed), or | 
| 1282 |  |  |  |  |  |  | C (if not.) | 
| 1283 |  |  |  |  |  |  |  | 
| 1284 |  |  |  |  |  |  | This subroutine interprets years the same way C | 
| 1285 |  |  |  |  |  |  | does. | 
| 1286 |  |  |  |  |  |  |  | 
| 1287 |  |  |  |  |  |  | This wrapper is needed because the routines have subtly different | 
| 1288 |  |  |  |  |  |  | signatures. L C interprets years | 
| 1289 |  |  |  |  |  |  | strictly as Perl years. L C | 
| 1290 |  |  |  |  |  |  | interprets years differently depending on the value of the year; greater | 
| 1291 |  |  |  |  |  |  | than 999 as Gregorian years, but other years are Perl years, except for | 
| 1292 |  |  |  |  |  |  | the years 0 to 99 inclusive, which are interpreted as within 50 years of | 
| 1293 |  |  |  |  |  |  | the current year. | 
| 1294 |  |  |  |  |  |  |  | 
| 1295 |  |  |  |  |  |  | This subroutine is discouraged in favor of C, which | 
| 1296 |  |  |  |  |  |  | always interprets years as Gregorian years. | 
| 1297 |  |  |  |  |  |  |  | 
| 1298 |  |  |  |  |  |  | =item $epoch = time_local( $sec, $min, $hr, $day, $mon, $yr ); | 
| 1299 |  |  |  |  |  |  |  | 
| 1300 |  |  |  |  |  |  | This exportable subroutine is a wrapper for either | 
| 1301 |  |  |  |  |  |  | C (if that module is installed), or | 
| 1302 |  |  |  |  |  |  | C (if not.) | 
| 1303 |  |  |  |  |  |  |  | 
| 1304 |  |  |  |  |  |  | This subroutine interprets years the same way | 
| 1305 |  |  |  |  |  |  | C does. | 
| 1306 |  |  |  |  |  |  |  | 
| 1307 |  |  |  |  |  |  | This wrapper is needed for the same reason C is | 
| 1308 |  |  |  |  |  |  | needed. | 
| 1309 |  |  |  |  |  |  |  | 
| 1310 |  |  |  |  |  |  | This subroutine is discouraged in favor of C, which | 
| 1311 |  |  |  |  |  |  | always interprets years as Gregorian years. | 
| 1312 |  |  |  |  |  |  |  | 
| 1313 |  |  |  |  |  |  | =item $a = vector_cross_product( $b, $c ); | 
| 1314 |  |  |  |  |  |  |  | 
| 1315 |  |  |  |  |  |  | This subroutine computes and returns the vector cross product of $b and | 
| 1316 |  |  |  |  |  |  | $c. Vectors are represented by array references.  The cross product is | 
| 1317 |  |  |  |  |  |  | only defined if both arrays have 3 elements. | 
| 1318 |  |  |  |  |  |  |  | 
| 1319 |  |  |  |  |  |  | =cut | 
| 1320 | 3 |  |  | 3 | 1 | 1516 |  | 
| 1321 | 3 | 50 | 33 |  |  | 5 | sub vector_cross_product { | 
|  | 3 |  |  |  |  | 11 |  | 
|  | 3 |  |  |  |  | 13 |  | 
| 1322 |  |  |  |  |  |  | my ( $b, $c ) = @_; | 
| 1323 |  |  |  |  |  |  | @{ $b } == 3 and @{ $c } == 3 | 
| 1324 |  |  |  |  |  |  | or confess 'Programming error - vector_cross_product arguments', | 
| 1325 | 3 |  |  |  |  | 15 | ' must be references to arrays of length 3'; | 
| 1326 |  |  |  |  |  |  | return [ | 
| 1327 |  |  |  |  |  |  | $b->[1] * $c->[2] - $b->[2] * $c->[1], | 
| 1328 |  |  |  |  |  |  | $b->[2] * $c->[0] - $b->[0] * $c->[2], | 
| 1329 |  |  |  |  |  |  | $b->[0] * $c->[1] - $b->[1] * $c->[0], | 
| 1330 |  |  |  |  |  |  | ]; | 
| 1331 |  |  |  |  |  |  | } | 
| 1332 |  |  |  |  |  |  |  | 
| 1333 |  |  |  |  |  |  | =item $a = vector_dot_product( $b, $c ); | 
| 1334 |  |  |  |  |  |  |  | 
| 1335 |  |  |  |  |  |  | This subroutine computes and returns the vector dot product of $b and | 
| 1336 |  |  |  |  |  |  | $c. Vectors are represented by array references. The dot product is only | 
| 1337 |  |  |  |  |  |  | defined if both arrays have the same number of elements. | 
| 1338 |  |  |  |  |  |  |  | 
| 1339 |  |  |  |  |  |  | =cut | 
| 1340 | 50698 |  |  | 50698 | 1 | 83585 |  | 
| 1341 | 50698 | 50 |  |  |  | 62971 | sub vector_dot_product { | 
|  | 50698 |  |  |  |  | 76192 |  | 
|  | 50698 |  |  |  |  | 94337 |  | 
| 1342 |  |  |  |  |  |  | my ( $b, $c ) = @_; | 
| 1343 |  |  |  |  |  |  | @{ $b } == @{ $c } | 
| 1344 | 50698 |  |  |  |  | 71926 | or confess 'Programming error - vector_dot_product arguments ', | 
| 1345 | 50698 |  |  |  |  | 61358 | 'must be references to arrays of the same length'; | 
|  | 50698 |  |  |  |  | 72278 |  | 
| 1346 | 50698 |  |  |  |  | 86217 | my $prod = 0; | 
| 1347 | 152094 |  |  |  |  | 239053 | my $size = @{ $b } - 1; | 
| 1348 |  |  |  |  |  |  | foreach my $inx ( 0 .. $size ) { | 
| 1349 | 50698 |  |  |  |  | 139413 | $prod += $b->[$inx] * $c->[$inx]; | 
| 1350 |  |  |  |  |  |  | } | 
| 1351 |  |  |  |  |  |  | return $prod; | 
| 1352 |  |  |  |  |  |  | } | 
| 1353 |  |  |  |  |  |  |  | 
| 1354 |  |  |  |  |  |  | =item $m = vector_magnitude( $x ); | 
| 1355 |  |  |  |  |  |  |  | 
| 1356 |  |  |  |  |  |  | This subroutine computes and returns the magnitude of vector $x. The | 
| 1357 |  |  |  |  |  |  | vector is represented by an array reference. | 
| 1358 |  |  |  |  |  |  |  | 
| 1359 |  |  |  |  |  |  | =cut | 
| 1360 | 24490 |  |  | 24490 | 1 | 41002 |  | 
| 1361 | 24490 | 50 |  |  |  | 61975 | sub vector_magnitude { | 
| 1362 |  |  |  |  |  |  | my ( $x ) = @_; | 
| 1363 |  |  |  |  |  |  | ARRAY_REF eq ref $x | 
| 1364 | 24490 |  |  |  |  | 38863 | or confess 'Programming error - vector_magnitude argument ', | 
| 1365 | 24490 |  |  |  |  | 32029 | 'must be a reference to an array'; | 
|  | 24490 |  |  |  |  | 40985 |  | 
| 1366 | 24490 |  |  |  |  | 49739 | my $mag = 0; | 
| 1367 | 73467 |  |  |  |  | 127364 | my $size = @{ $x } - 1; | 
| 1368 |  |  |  |  |  |  | foreach my $inx ( 0 .. $size ) { | 
| 1369 | 24490 |  |  |  |  | 53806 | $mag += $x->[$inx] * $x->[$inx]; | 
| 1370 |  |  |  |  |  |  | } | 
| 1371 |  |  |  |  |  |  | return sqrt $mag; | 
| 1372 |  |  |  |  |  |  | } | 
| 1373 |  |  |  |  |  |  |  | 
| 1374 |  |  |  |  |  |  | =item $u = vector_unitize( $x ); | 
| 1375 |  |  |  |  |  |  |  | 
| 1376 |  |  |  |  |  |  | This subroutine computes and returns a unit vector pointing in the same | 
| 1377 |  |  |  |  |  |  | direction as $x. The vectors are represented by array references. | 
| 1378 |  |  |  |  |  |  |  | 
| 1379 |  |  |  |  |  |  | =cut | 
| 1380 | 2 |  |  | 2 | 1 | 974 |  | 
| 1381 | 2 | 50 |  |  |  | 7 | sub vector_unitize { | 
| 1382 |  |  |  |  |  |  | my ( $x ) = @_; | 
| 1383 |  |  |  |  |  |  | ARRAY_REF eq ref $x | 
| 1384 | 2 |  |  |  |  | 6 | or confess 'Programming error - vector_unitize argument ', | 
| 1385 | 2 |  |  |  |  | 4 | 'must be a reference to an array'; | 
|  | 4 |  |  |  |  | 14 |  | 
|  | 2 |  |  |  |  | 5 |  | 
| 1386 |  |  |  |  |  |  | my $mag = vector_magnitude( $x ); | 
| 1387 |  |  |  |  |  |  | return [ map { $_ / $mag } @{ $x } ]; | 
| 1388 |  |  |  |  |  |  | } | 
| 1389 |  |  |  |  |  |  |  | 
| 1390 |  |  |  |  |  |  | #	__classisa( 'Foo', 'Bar' ); | 
| 1391 |  |  |  |  |  |  | # | 
| 1392 |  |  |  |  |  |  | #	Returns true if Foo->isa( 'Bar' ) is true, and false otherwise. | 
| 1393 |  |  |  |  |  |  | #	In particular, returns false if the first argument is a | 
| 1394 |  |  |  |  |  |  | #	reference. | 
| 1395 | 42738 |  |  | 42738 |  | 77584 |  | 
| 1396 | 42738 | 50 |  |  |  | 78480 | sub __classisa { | 
| 1397 | 42738 | 50 |  |  |  | 77902 | my ( $invocant, $class ) = @_; | 
| 1398 | 42738 |  |  |  |  | 200916 | ref $invocant and return; | 
| 1399 |  |  |  |  |  |  | defined $invocant or return; | 
| 1400 |  |  |  |  |  |  | return $invocant->isa( $class ); | 
| 1401 |  |  |  |  |  |  | } | 
| 1402 |  |  |  |  |  |  |  | 
| 1403 |  |  |  |  |  |  | #	__instance( $foo, 'Bar' ); | 
| 1404 |  |  |  |  |  |  | # | 
| 1405 |  |  |  |  |  |  | #	Returns true if $foo is an instance of 'Bar', and false | 
| 1406 |  |  |  |  |  |  | #	otherwise. In particular, returns false if $foo is not a | 
| 1407 |  |  |  |  |  |  | #	reference, or if it is not blessed. | 
| 1408 | 115 |  |  | 115 |  | 225 |  | 
| 1409 | 115 | 50 |  |  |  | 298 | sub __instance { | 
| 1410 | 115 | 50 |  |  |  | 378 | my ( $object, $class ) = @_; | 
| 1411 | 115 |  |  |  |  | 576 | ref $object or return; | 
| 1412 |  |  |  |  |  |  | blessed( $object ) or return; | 
| 1413 |  |  |  |  |  |  | return $object->isa( $class ); | 
| 1414 |  |  |  |  |  |  | } | 
| 1415 |  |  |  |  |  |  |  | 
| 1416 |  |  |  |  |  |  | #	$epoch_time = __parse_time_iso_8601 | 
| 1417 |  |  |  |  |  |  | # | 
| 1418 |  |  |  |  |  |  | #	Parse ISO 8601 date/time. I do not intend to expose this, since | 
| 1419 |  |  |  |  |  |  | #	it will probably go away when the satpass script is dropped. It | 
| 1420 |  |  |  |  |  |  | #	would actually be in that script except for the fact that it can | 
| 1421 |  |  |  |  |  |  | #	be more easily tested here, and because of the possibility that | 
| 1422 |  |  |  |  |  |  | #	it will be used in App::Satpass2. | 
| 1423 |  |  |  |  |  |  | { | 
| 1424 |  |  |  |  |  |  |  | 
| 1425 |  |  |  |  |  |  | my %special_day_offset = ( | 
| 1426 |  |  |  |  |  |  | yesterday => -SECSPERDAY(), | 
| 1427 |  |  |  |  |  |  | today => 0, | 
| 1428 |  |  |  |  |  |  | tomorrow => SECSPERDAY(), | 
| 1429 |  |  |  |  |  |  | ); | 
| 1430 | 51 |  |  | 51 |  | 2428 |  | 
| 1431 |  |  |  |  |  |  | sub __parse_time_iso_8601 { | 
| 1432 | 51 |  |  |  |  | 80 | my ( $string ) = @_; | 
| 1433 | 51 | 100 |  |  |  | 505 |  | 
| 1434 |  |  |  |  |  |  | my @zone; | 
| 1435 |  |  |  |  |  |  | $string =~ s/ \s* (?: ( Z ) | | 
| 1436 | 51 |  |  |  |  | 89 | ( [+-] ) ( [0-9]{2} ) :? ( [0-9]{2} )? ) \z //smx | 
| 1437 |  |  |  |  |  |  | and @zone = ( $1, $2, $3, $4 ); | 
| 1438 |  |  |  |  |  |  | my @date; | 
| 1439 | 51 | 50 |  |  |  | 281 |  | 
|  |  | 0 |  |  |  |  |  | 
| 1440 |  |  |  |  |  |  | # ISO 8601 date | 
| 1441 |  |  |  |  |  |  | if ( $string =~ m{ \A | 
| 1442 |  |  |  |  |  |  | ( [0-9]{4} [^0-9]? | [0-9]{2} [^0-9] )		# year: $1 | 
| 1443 |  |  |  |  |  |  | (?: ( [0-9]{1,2} ) [^0-9]?			# month: $2 | 
| 1444 |  |  |  |  |  |  | (?: ( [0-9]{1,2} ) (?: \s* | [^0-9]? )	# day: $3 | 
| 1445 |  |  |  |  |  |  | (?: ( [0-9]{1,2} ) [^0-9]?		# hour: $4 | 
| 1446 |  |  |  |  |  |  | (?: ( [0-9]{1,2} ) [^0-9]?		# minute: $5 | 
| 1447 |  |  |  |  |  |  | (?: ( [0-9]{1,2} ) [^0-9]?	# second: $6 | 
| 1448 |  |  |  |  |  |  | ( [0-9]* )			# fract: $7 | 
| 1449 |  |  |  |  |  |  | )? | 
| 1450 |  |  |  |  |  |  | )? | 
| 1451 |  |  |  |  |  |  | )? | 
| 1452 |  |  |  |  |  |  | )? | 
| 1453 |  |  |  |  |  |  | )? | 
| 1454 | 51 |  |  |  |  | 250 | \z | 
| 1455 |  |  |  |  |  |  | }smx ) { | 
| 1456 |  |  |  |  |  |  | @date = ( $1, $2, $3, $4, $5, $6, $7, undef ); | 
| 1457 |  |  |  |  |  |  |  | 
| 1458 |  |  |  |  |  |  | # special-case 'yesterday', 'today', and 'tomorrow'. | 
| 1459 |  |  |  |  |  |  | } elsif ( $string =~ m< \A | 
| 1460 |  |  |  |  |  |  | ( yesterday | today | tomorrow )	# day: $1 | 
| 1461 |  |  |  |  |  |  | (?: [^0-9]* ( [0-9]{1,2} ) [^0-9]?	# hour: $2 | 
| 1462 |  |  |  |  |  |  | (?: ( [0-9]{1,2} ) [^0-9]?		# minute: $3 | 
| 1463 |  |  |  |  |  |  | (?: ( [0-9]{1,2} ) [^0-9]?	# second: $4 | 
| 1464 |  |  |  |  |  |  | ( [0-9]* )			# fract: $5 | 
| 1465 |  |  |  |  |  |  | )? | 
| 1466 |  |  |  |  |  |  | )? | 
| 1467 | 0 | 0 |  |  |  | 0 | )? | 
| 1468 |  |  |  |  |  |  | \z >smx ) { | 
| 1469 | 0 |  |  |  |  | 0 | my @today = @zone ? gmtime : localtime; | 
| 1470 |  |  |  |  |  |  | @date = ( $today[5] + 1900, $today[4] + 1, $today[3], $2, $3, | 
| 1471 |  |  |  |  |  |  | $4, $5, $special_day_offset{$1} ); | 
| 1472 |  |  |  |  |  |  |  | 
| 1473 | 0 |  |  |  |  | 0 | } else { | 
| 1474 |  |  |  |  |  |  |  | 
| 1475 |  |  |  |  |  |  | return; | 
| 1476 |  |  |  |  |  |  |  | 
| 1477 | 51 |  | 50 |  |  | 195 | } | 
| 1478 | 51 | 100 | 100 |  |  | 166 |  | 
| 1479 | 5 |  |  |  |  | 12 | my $offset = pop @date || 0; | 
| 1480 | 5 |  | 100 |  |  | 28 | if ( @zone && !$zone[0] ) { | 
| 1481 |  |  |  |  |  |  | my ( undef, $sign, $hr, $min ) = @zone; | 
| 1482 |  |  |  |  |  |  | $offset -= $sign . ( ( $hr * 60 + ( $min || 0 ) ) * 60 ) | 
| 1483 | 51 |  |  |  |  | 101 | } | 
| 1484 | 357 | 100 |  |  |  | 739 |  | 
| 1485 |  |  |  |  |  |  | foreach ( @date ) { | 
| 1486 |  |  |  |  |  |  | defined $_ and s/ [^0-9]+ //smxg; | 
| 1487 | 51 |  |  |  |  | 105 | } | 
| 1488 |  |  |  |  |  |  |  | 
| 1489 | 51 | 100 |  |  |  | 140 | $date[0] = __tle_year_to_Gregorian_year( $date[0] ); | 
| 1490 | 51 | 100 |  |  |  | 109 |  | 
| 1491 | 51 |  |  |  |  | 78 | defined $date[1] and --$date[1]; | 
| 1492 |  |  |  |  |  |  | defined $date[2] or $date[2] = 1; | 
| 1493 | 51 |  |  |  |  | 96 | my $frc = pop @date; | 
| 1494 | 306 | 100 |  |  |  | 524 |  | 
| 1495 |  |  |  |  |  |  | foreach ( @date ) { | 
| 1496 |  |  |  |  |  |  | defined $_ or $_ = 0; | 
| 1497 | 51 |  |  |  |  | 66 | } | 
| 1498 | 51 | 100 |  |  |  | 95 |  | 
| 1499 | 28 |  |  |  |  | 62 | my $time; | 
| 1500 |  |  |  |  |  |  | if ( @zone ) { | 
| 1501 | 23 |  |  |  |  | 45 | $time = greg_time_gm( reverse @date ); | 
| 1502 |  |  |  |  |  |  | } else { | 
| 1503 |  |  |  |  |  |  | $time = greg_time_local( reverse @date ); | 
| 1504 | 51 | 50 | 66 |  |  | 2003 | } | 
| 1505 | 0 |  |  |  |  | 0 |  | 
| 1506 | 0 |  |  |  |  | 0 | if ( defined $frc  && $frc ne '') { | 
| 1507 |  |  |  |  |  |  | my $denom = 1 . ( 0 x length $frc ); | 
| 1508 |  |  |  |  |  |  | $time += $frc / $denom; | 
| 1509 | 51 |  |  |  |  | 156 | } | 
| 1510 |  |  |  |  |  |  |  | 
| 1511 |  |  |  |  |  |  | return $time + $offset; | 
| 1512 |  |  |  |  |  |  | } | 
| 1513 |  |  |  |  |  |  |  | 
| 1514 |  |  |  |  |  |  | } | 
| 1515 | 30 |  |  | 30 |  | 106 |  | 
| 1516 | 30 | 100 |  |  |  | 102 | sub __sprintf { | 
| 1517 |  |  |  |  |  |  | my ( $tplt, @args ) = @_; | 
| 1518 | 18 |  |  | 18 |  | 12204 | defined $tplt | 
|  | 18 |  |  |  |  | 257 |  | 
|  | 18 |  |  |  |  | 105 |  | 
| 1519 | 27 |  |  |  |  | 232 | or return undef;	## no critic (ProhibitExplicitReturnUndef) | 
| 1520 |  |  |  |  |  |  | no if $] gt '5.021002', qw{ warnings redundant }; | 
| 1521 |  |  |  |  |  |  | return sprintf $tplt, @args; | 
| 1522 |  |  |  |  |  |  | } | 
| 1523 |  |  |  |  |  |  |  | 
| 1524 |  |  |  |  |  |  | { | 
| 1525 |  |  |  |  |  |  | my %deprecate = ( | 
| 1526 |  |  |  |  |  |  | ); | 
| 1527 | 0 |  |  | 0 |  | 0 |  | 
| 1528 | 0 | 0 |  |  |  | 0 | sub __subroutine_deprecation { | 
| 1529 |  |  |  |  |  |  | ( my $sub = ( caller 1 )[3] ) =~ s/ .* :: //smx; | 
| 1530 | 0 | 0 |  |  |  | 0 | my $info = $deprecate{$sub} or return; | 
| 1531 | 0 |  |  |  |  | 0 | $info->{level} | 
| 1532 | 0 |  | 0 |  |  | 0 | or return; | 
| 1533 | 0 | 0 |  |  |  | 0 | my $msg = "Subroutine $sub() deprecated in favor of @{[ | 
| 1534 |  |  |  |  |  |  | $info->{method} || $sub ]}() method"; | 
| 1535 | 0 |  |  |  |  | 0 | $info->{level} >= 3 | 
| 1536 |  |  |  |  |  |  | and croak $msg; | 
| 1537 | 0 | 0 |  |  |  | 0 | carp $msg; | 
| 1538 | 0 |  |  |  |  | 0 | $info->{level} == 1 | 
| 1539 |  |  |  |  |  |  | and $info->{level} = 0; | 
| 1540 |  |  |  |  |  |  | return; | 
| 1541 |  |  |  |  |  |  | } | 
| 1542 |  |  |  |  |  |  | } | 
| 1543 |  |  |  |  |  |  |  | 
| 1544 |  |  |  |  |  |  | =item $year = __tle_year_to_Gregorian_year( $year ) | 
| 1545 |  |  |  |  |  |  |  | 
| 1546 |  |  |  |  |  |  | The TLE data contain the year in two-digit form. NORAD decided to deal | 
| 1547 |  |  |  |  |  |  | with Y2K by decreeing that year numbers lower than 57 (the launch of | 
| 1548 |  |  |  |  |  |  | Sputnik 1) are converted to Gregorian by adding 2000. Years numbers of | 
| 1549 |  |  |  |  |  |  | 57 or greater are still converted to Gregorian by adding 1900. This | 
| 1550 |  |  |  |  |  |  | subroutine encapsulates this logic. Years of 100 or greater are returned | 
| 1551 |  |  |  |  |  |  | unmodified. | 
| 1552 |  |  |  |  |  |  |  | 
| 1553 |  |  |  |  |  |  | This subroutine is B to this package, and can be changed or | 
| 1554 |  |  |  |  |  |  | revoked without notice. | 
| 1555 |  |  |  |  |  |  |  | 
| 1556 |  |  |  |  |  |  | =cut | 
| 1557 | 97 |  |  | 97 |  | 252 |  | 
| 1558 | 97 | 100 |  |  |  | 415 | sub __tle_year_to_Gregorian_year { | 
|  |  | 100 |  |  |  |  |  | 
| 1559 |  |  |  |  |  |  | my ( $year ) = @_; | 
| 1560 |  |  |  |  |  |  | return $year < 57 ? $year + 2000 : | 
| 1561 |  |  |  |  |  |  | $year < 100 ? $year + 1900 : $year; | 
| 1562 |  |  |  |  |  |  | } | 
| 1563 |  |  |  |  |  |  |  | 
| 1564 |  |  |  |  |  |  | 1; | 
| 1565 |  |  |  |  |  |  |  | 
| 1566 |  |  |  |  |  |  | __END__ |