| 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
|
|
70514
|
use strict; |
|
|
18
|
|
|
|
|
48
|
|
|
|
18
|
|
|
|
|
568
|
|
|
124
|
18
|
|
|
18
|
|
92
|
use warnings; |
|
|
18
|
|
|
|
|
34
|
|
|
|
18
|
|
|
|
|
913
|
|
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
our $VERSION = '0.129_01'; |
|
127
|
|
|
|
|
|
|
our @ISA = qw{Exporter}; |
|
128
|
|
|
|
|
|
|
|
|
129
|
18
|
|
|
18
|
|
101
|
use Carp; |
|
|
18
|
|
|
|
|
30
|
|
|
|
18
|
|
|
|
|
1603
|
|
|
130
|
|
|
|
|
|
|
## use Config; |
|
131
|
|
|
|
|
|
|
## use Data::Dumper; |
|
132
|
18
|
|
|
18
|
|
8490
|
use POSIX qw{ floor modf strftime }; |
|
|
18
|
|
|
|
|
126234
|
|
|
|
18
|
|
|
|
|
108
|
|
|
133
|
18
|
|
|
18
|
|
25013
|
use Scalar::Util qw{ blessed }; |
|
|
18
|
|
|
|
|
41
|
|
|
|
18
|
|
|
|
|
9652
|
|
|
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
|
|
|
|
|
2826
|
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
|
|
84
|
} or do { |
|
183
|
18
|
|
|
|
|
7764
|
require Time::Local; |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# sub time_gm |
|
186
|
18
|
|
|
|
|
34673
|
*time_gm = Time::Local->can( 'timegm' ); |
|
187
|
|
|
|
|
|
|
# sub greg_time_gm |
|
188
|
|
|
|
|
|
|
*greg_time_gm = Time::Local->can( 'timegm_modern' ) || sub { |
|
189
|
260
|
|
|
260
|
|
72156
|
my @date = @_; |
|
190
|
260
|
|
|
|
|
703
|
$date[5] = _year_adjust_greg( $date[5] ); |
|
191
|
260
|
|
|
|
|
891
|
return Time::Local::timegm( @date ); |
|
192
|
18
|
|
50
|
|
|
272
|
}; |
|
193
|
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
# sub time_local |
|
195
|
18
|
|
|
|
|
79
|
*time_local = Time::Local->can( 'timelocal' ); |
|
196
|
|
|
|
|
|
|
# sub greg_time_local |
|
197
|
|
|
|
|
|
|
*greg_time_local = Time::Local->can( 'timelocal_modern' ) || sub { |
|
198
|
46
|
|
|
46
|
|
12302
|
my @date = @_; |
|
199
|
46
|
|
|
|
|
100
|
$date[5] = _year_adjust_greg( $date[5] ); |
|
200
|
46
|
|
|
|
|
122
|
return Time::Local::timelocal( @date ); |
|
201
|
18
|
|
50
|
|
|
168
|
}; |
|
202
|
|
|
|
|
|
|
|
|
203
|
18
|
|
|
|
|
1285
|
@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
|
|
|
|
|
7886
|
use constant NOT_GREG => |
|
231
|
18
|
|
|
18
|
|
130
|
'%d not interpreted as Gregorian year by Time::Local::timegm'; |
|
|
18
|
|
|
|
|
37
|
|
|
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
|
|
625
|
my ( $year ) = @_; |
|
257
|
306
|
50
|
|
|
|
895
|
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
|
|
160
|
use constant AU => 149597870; # 1 astronomical unit, per |
|
|
18
|
|
|
|
|
38
|
|
|
|
18
|
|
|
|
|
1027
|
|
|
312
|
|
|
|
|
|
|
# Meeus, Appendix I pg 407. |
|
313
|
18
|
|
|
18
|
|
105
|
use constant LIGHTYEAR => 9.4607e12; # 1 light-year, per Meeus, |
|
|
18
|
|
|
|
|
38
|
|
|
|
18
|
|
|
|
|
959
|
|
|
314
|
|
|
|
|
|
|
# Appendix I pg 407. |
|
315
|
18
|
|
|
18
|
|
132
|
use constant PARSEC => 30.8568e12; # 1 parsec, per Meeus, |
|
|
18
|
|
|
|
|
43
|
|
|
|
18
|
|
|
|
|
1175
|
|
|
316
|
|
|
|
|
|
|
# Appendix I pg 407. |
|
317
|
18
|
|
|
18
|
|
153
|
use constant PERL2000 => greg_time_gm( 0, 0, 12, 1, 0, 2000 ); |
|
|
18
|
|
|
|
|
53
|
|
|
|
18
|
|
|
|
|
49
|
|
|
318
|
18
|
|
|
18
|
|
1989
|
use constant PI => atan2 (0, -1); |
|
|
18
|
|
|
|
|
37
|
|
|
|
18
|
|
|
|
|
1118
|
|
|
319
|
18
|
|
|
18
|
|
124
|
use constant PIOVER2 => PI / 2; |
|
|
18
|
|
|
|
|
35
|
|
|
|
18
|
|
|
|
|
1307
|
|
|
320
|
18
|
|
|
18
|
|
225
|
use constant SECSPERDAY => 86400; |
|
|
18
|
|
|
|
|
52
|
|
|
|
18
|
|
|
|
|
952
|
|
|
321
|
18
|
|
|
18
|
|
118
|
use constant SECS_PER_SIDERIAL_DAY => 86164.0905; # Appendix I, page 408. |
|
|
18
|
|
|
|
|
32
|
|
|
|
18
|
|
|
|
|
929
|
|
|
322
|
18
|
|
|
18
|
|
109
|
use constant SPEED_OF_LIGHT => 299792.458; # KM/sec, per NIST. |
|
|
18
|
|
|
|
|
34
|
|
|
|
18
|
|
|
|
|
1032
|
|
|
323
|
|
|
|
|
|
|
### use constant SOLAR_RADIUS => 1392000 / 2; # Meeus, Appendix I, page 407. |
|
324
|
18
|
|
|
18
|
|
157
|
use constant TWOPI => PI * 2; |
|
|
18
|
|
|
|
|
51
|
|
|
|
18
|
|
|
|
|
1117
|
|
|
325
|
|
|
|
|
|
|
|
|
326
|
18
|
|
|
18
|
|
110
|
use constant ARRAY_REF => ref []; |
|
|
18
|
|
|
|
|
32
|
|
|
|
18
|
|
|
|
|
1164
|
|
|
327
|
18
|
|
|
18
|
|
135
|
use constant CODE_REF => ref sub {}; |
|
|
18
|
|
|
|
|
52
|
|
|
|
18
|
|
|
|
|
1001
|
|
|
328
|
18
|
|
|
18
|
|
107
|
use constant HASH_REF => ref {}; |
|
|
18
|
|
|
|
|
36
|
|
|
|
18
|
|
|
|
|
1025
|
|
|
329
|
18
|
|
|
18
|
|
106
|
use constant SCALAR_REF => ref \0; |
|
|
18
|
|
|
|
|
33
|
|
|
|
18
|
|
|
|
|
6495
|
|
|
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
|
3272
|
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
|
|
|
|
|
5392
|
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
|
482
|
my @arg = @_; |
|
356
|
1
|
|
|
|
|
4
|
my $sum = 0; |
|
357
|
1
|
|
|
|
|
3
|
foreach my $mag ( @arg ) { |
|
358
|
3
|
|
|
|
|
12
|
$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
|
63502
|
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
|
1475
|
my ($elevation, $height) = @_; |
|
400
|
3
|
|
|
|
|
16
|
my $cosZ = cos (PIOVER2 - $elevation); |
|
401
|
3
|
|
|
|
|
15
|
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
|
|
|
|
|
8
|
my $Aaer = 0.120 * exp (-$height / 1.5); # Green 4 |
|
404
|
3
|
|
|
|
|
8
|
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
|
|
78
|
$DATETIMEFORMAT = '%a %b %d %Y %H:%M:%S'; |
|
435
|
18
|
|
|
|
|
5155
|
$JD_GREGORIAN = 2299160.5; |
|
436
|
|
|
|
|
|
|
} |
|
437
|
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
sub date2jd { |
|
439
|
21
|
|
|
21
|
1
|
1085
|
my @args = @_; |
|
440
|
21
|
|
|
|
|
135
|
unshift @args, 0 while @args < 6; |
|
441
|
21
|
|
|
|
|
71
|
my ($sec, $min, $hr, $day, $mon, $yr) = @args; |
|
442
|
21
|
|
|
|
|
51
|
$mon++; # Algorithm expects month 1-12. |
|
443
|
21
|
|
|
|
|
56
|
$yr += 1900; # Algorithm expects zero-based year. |
|
444
|
21
|
50
|
|
|
|
84
|
($yr < -4712) and croak "Error - Invalid year $yr"; |
|
445
|
21
|
50
|
33
|
|
|
154
|
($mon < 1 || $mon > 12) and croak "Error - Invalid month $mon"; |
|
446
|
21
|
100
|
|
|
|
71
|
if ($mon < 3) { |
|
447
|
20
|
|
|
|
|
41
|
--$yr; |
|
448
|
20
|
|
|
|
|
47
|
$mon += 12; |
|
449
|
|
|
|
|
|
|
} |
|
450
|
21
|
|
|
|
|
121
|
my $A = floor ($yr / 100); |
|
451
|
21
|
|
|
|
|
103
|
my $B = 2 - $A + floor ($A / 4); |
|
452
|
21
|
|
50
|
|
|
363
|
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
|
|
|
118
|
$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
|
|
|
|
|
41039
|
return $jd; |
|
460
|
|
|
|
|
|
|
} |
|
461
|
|
|
|
|
|
|
|
|
462
|
18
|
|
|
18
|
|
159
|
use constant JD_OF_EPOCH => date2jd (gmtime (0)); |
|
|
18
|
|
|
|
|
34
|
|
|
|
18
|
|
|
|
|
86
|
|
|
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
|
489
|
my @args = @_; |
|
483
|
1
|
|
|
|
|
9
|
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
|
|
77
|
my ( $self, @args ) = @_; |
|
524
|
23
|
100
|
|
|
|
98
|
if ( ! embodies( $args[0], 'Astro::Coord::ECI' ) ) { |
|
525
|
8
|
50
|
|
|
|
32
|
my $sta = $self->get( 'station' ) |
|
526
|
|
|
|
|
|
|
or croak 'Station attribute not set'; |
|
527
|
8
|
|
|
|
|
24
|
unshift @args, $sta; |
|
528
|
|
|
|
|
|
|
} |
|
529
|
23
|
|
|
|
|
97
|
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
|
584512
|
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
|
499
|
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
|
|
|
3
|
|
|
|
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
|
|
|
|
|
2
|
my $sum = 0; |
|
565
|
1
|
|
|
|
|
3
|
my $size = @$x; |
|
566
|
1
|
|
|
|
|
74
|
for (my $inx = 0; $inx < $size; $inx++) { |
|
567
|
2
|
|
|
|
|
4
|
my $delta = $x->[$inx] - $y->[$inx]; |
|
568
|
2
|
|
|
|
|
6
|
$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
|
54533
|
my ($time) = @_; |
|
590
|
31458
|
|
|
|
|
108411
|
my $year = (gmtime $time)[5] + 1900; |
|
591
|
31458
|
|
|
|
|
64528
|
my $t = ($year - 2000) / 100; |
|
592
|
31458
|
|
|
|
|
51594
|
my $correction = .37 * ($year - 2100); # Meeus' correction to (10.2) |
|
593
|
31458
|
|
|
|
|
117238
|
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
|
74411
|
my ($thingy, $class) = @_; |
|
612
|
41866
|
|
|
|
|
57332
|
my $code = eval {$thingy->can('represents')}; |
|
|
41866
|
|
|
|
|
121975
|
|
|
613
|
41866
|
100
|
|
|
|
114822
|
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
|
3025
|
my ($time) = @_; |
|
648
|
27
|
|
|
|
|
84
|
my $day = floor ($time / SECSPERDAY); |
|
649
|
27
|
|
|
|
|
79
|
my $sec = $time - $day * SECSPERDAY; |
|
650
|
27
|
|
|
|
|
59
|
($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
|
|
|
|
|
55
|
my $min = floor ($sec / 60); |
|
654
|
27
|
|
|
|
|
43
|
$sec = $sec - $min * 60; |
|
655
|
27
|
|
|
|
|
48
|
my $hr = floor ($min / 60); |
|
656
|
27
|
|
|
|
|
44
|
$min = $min - $hr * 60; |
|
657
|
27
|
|
|
|
|
47
|
my $wday = ($jd + 1.5) % 7; |
|
658
|
27
|
|
|
|
|
83
|
my $yd = floor (275 * ($mon + 1) / 9) - (2 - $leap) * floor (($mon + |
|
659
|
|
|
|
|
|
|
10) / 12) + $day - 31; |
|
660
|
27
|
50
|
|
|
|
131
|
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
|
1706
|
my ($begin, $end, $test, $limit) = @_; |
|
689
|
421
|
|
100
|
|
|
1732
|
$limit ||= 1; |
|
690
|
421
|
50
|
|
|
|
1202
|
defined $begin |
|
691
|
|
|
|
|
|
|
or confess 'Programming error - $begin undefined'; |
|
692
|
421
|
50
|
|
|
|
901
|
defined $end |
|
693
|
|
|
|
|
|
|
or confess 'Programming error - $end undefined'; |
|
694
|
421
|
100
|
|
|
|
919
|
if ($limit >= 1) { |
|
695
|
375
|
50
|
|
|
|
757
|
if ($begin <= $end) { |
|
696
|
375
|
|
|
|
|
992
|
$begin = floor ($begin); |
|
697
|
375
|
50
|
|
|
|
1176
|
$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
|
|
11203
|
sub {$end - $begin > $limit} : |
|
706
|
0
|
|
|
0
|
|
0
|
sub {$begin - $end > $limit} |
|
707
|
421
|
50
|
|
|
|
1787
|
); |
|
708
|
421
|
|
|
|
|
878
|
while ($iterator->()) { |
|
709
|
3572
|
100
|
|
|
|
11002
|
my $mid = $limit >= 1 ? |
|
710
|
|
|
|
|
|
|
floor (($begin + $end) / 2) : ($begin + $end) / 2; |
|
711
|
3572
|
100
|
|
|
|
8516
|
($begin, $end) = ($test->($mid)) ? |
|
712
|
|
|
|
|
|
|
($begin, $mid) : ($mid, $end); |
|
713
|
|
|
|
|
|
|
} |
|
714
|
421
|
|
|
|
|
2378
|
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
|
548
|
my ( $time ) = @_; |
|
740
|
1
|
50
|
33
|
|
|
12
|
defined $time |
|
741
|
|
|
|
|
|
|
and $time =~ m/ \S /smx |
|
742
|
|
|
|
|
|
|
or return; |
|
743
|
1
|
|
|
|
|
6
|
my ( undef, $sec ) = modf( $time ); |
|
744
|
1
|
|
|
|
|
9
|
my @parts = ( gmtime $sec )[ reverse 0 .. 5 ]; |
|
745
|
1
|
|
|
|
|
3
|
$parts[0] += 1900; |
|
746
|
1
|
|
|
|
|
2
|
$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
|
|
602
|
my ( $epoch, $date_format ) = @_; |
|
774
|
1
|
|
|
|
|
5
|
my ( $microseconds, $seconds ) = modf( $epoch ); |
|
775
|
1
|
|
|
|
|
6
|
my @parts = gmtime $seconds; |
|
776
|
1
|
|
|
|
|
8
|
my $string_us = sprintf '%.6f', $parts[0] + $microseconds; |
|
777
|
1
|
|
|
|
|
8
|
$string_us =~ s/ [^.]* //smx; |
|
778
|
1
|
50
|
|
|
|
7
|
$date_format =~ s{ ( %+ ) ( [DFrRTV] ) } |
|
|
2
|
|
|
|
|
15
|
|
|
779
|
1
|
50
|
|
|
|
8
|
{ length( $1 ) % 2 ? "$1$equiv{$2}" : "$1$2" }smxge; |
|
|
1
|
|
|
|
|
5
|
|
|
780
|
1
|
|
|
|
|
729
|
$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
|
511
|
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
|
6179
|
|
|
876
|
36
|
|
|
|
|
64
|
sub jd2date { |
|
877
|
36
|
|
|
|
|
72
|
my ($time) = @_; |
|
878
|
36
|
|
|
|
|
62
|
my $mod_jd = $time + 0.5; |
|
879
|
36
|
100
|
|
|
|
85
|
my $Z = floor ($mod_jd); |
|
880
|
30
|
|
|
|
|
70
|
my $F = $mod_jd - $Z; |
|
881
|
30
|
|
|
|
|
93
|
my $A = (my $julian = $Z < $JD_GREGORIAN) ? $Z : do { |
|
882
|
|
|
|
|
|
|
my $alpha = floor (($Z - 1867216.25)/36524.25); |
|
883
|
36
|
|
|
|
|
59
|
$Z + 1 + $alpha - floor ($alpha / 4); |
|
884
|
36
|
|
|
|
|
73
|
}; |
|
885
|
36
|
|
|
|
|
65
|
my $B = $A + 1524; |
|
886
|
36
|
|
|
|
|
74
|
my $C = floor (($B - 122.1) / 365.25); |
|
887
|
36
|
|
|
|
|
86
|
my $D = floor (365.25 * $C); |
|
888
|
36
|
100
|
|
|
|
79
|
my $E = floor (($B - $D) / 30.6001); |
|
889
|
36
|
100
|
|
|
|
73
|
my $day = $B - $D - floor (30.6001 * $E) + $F; |
|
890
|
36
|
50
|
|
|
|
175
|
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
|
10412
|
|
|
908
|
21
|
|
|
|
|
63
|
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
|
131658
|
=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
|
234390
|
=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
|
971
|
=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
|
483
|
|
|
966
|
1
|
50
|
|
|
|
5
|
sub keplers_equation { |
|
967
|
|
|
|
|
|
|
my ( $mean_anomaly, $eccentricity, $precision ) = @_; |
|
968
|
1
|
|
|
|
|
2
|
defined $precision |
|
969
|
1
|
|
|
|
|
2
|
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
|
|
|
|
|
5
|
|
|
973
|
3
|
|
|
|
|
11
|
{ |
|
974
|
|
|
|
|
|
|
$prev = $curr; |
|
975
|
|
|
|
|
|
|
my $delta = ( $mean_anomaly + $eccentricity * sin( $curr |
|
976
|
3
|
|
|
|
|
11
|
) - $curr ) / ( 1 - $eccentricity * cos $curr ); |
|
977
|
3
|
100
|
|
|
|
11
|
# Steele's correction, page 205 |
|
978
|
|
|
|
|
|
|
$curr = $curr + max( -.5, min( .5, $delta ) ); |
|
979
|
|
|
|
|
|
|
$precision < abs( $curr - $prev ) |
|
980
|
1
|
|
|
|
|
3
|
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
|
467
|
my %rslt; |
|
998
|
243
|
50
|
|
|
|
552
|
sub load_module { |
|
999
|
243
|
100
|
|
|
|
1008
|
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
|
|
|
|
3226
|
# if Perl 6 becomes a reality. |
|
1005
|
48
|
|
|
|
|
227
|
$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
|
|
152
|
unless (eval {require Scalar::Util; Scalar::Util->import |
|
|
18
|
|
|
|
|
49
|
|
|
|
18
|
|
|
|
|
5567
|
|
|
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
|
|
147
|
|
|
|
18
|
|
|
|
|
61
|
|
|
|
18
|
|
|
|
|
2425
|
|
|
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
|
|
146
|
|
|
|
18
|
|
|
|
|
48
|
|
|
|
18
|
|
|
|
|
36442
|
|
|
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
|
477219
|
=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
|
505
|
|
|
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
|
1278
|
|
|
1130
|
1
|
|
|
|
|
2
|
sub position_angle { |
|
1131
|
1
|
|
|
|
|
7
|
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
|
46904
|
=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
|
933
|
|
|
1165
|
2
|
100
|
|
|
|
9
|
sub rad2dms { |
|
1166
|
|
|
|
|
|
|
my ( $rad, $dp, $ds ) = @_; |
|
1167
|
1
|
50
|
|
|
|
10
|
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
|
|
|
|
|
3
|
|
|
1175
|
1
|
|
|
|
|
11
|
( $rad, my $sgn ) = $rad < 0 ? ( -$rad, '-' ) : ( $rad, '' ); |
|
1176
|
1
|
|
|
|
|
5
|
my $sec = rad2deg( $rad ); |
|
1177
|
1
|
|
|
|
|
3
|
( $sec, my $deg ) = modf( $sec ); |
|
1178
|
1
|
|
|
|
|
2
|
( $sec, my $min ) = modf( $sec * 60 ); |
|
1179
|
1
|
50
|
|
|
|
2
|
$sec *= 60; |
|
|
|
0
|
|
|
|
|
|
|
1180
|
1
|
|
|
|
|
16
|
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
|
|
|
|
10
|
$rslt = sprintf qq<%.${dp}f>, $sec; |
|
1187
|
|
|
|
|
|
|
} |
|
1188
|
1
|
|
|
|
|
5
|
$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
|
957
|
|
|
1207
|
2
|
100
|
|
|
|
9
|
sub rad2hms { |
|
1208
|
|
|
|
|
|
|
my ( $rad, $dp ) = @_; |
|
1209
|
1
|
50
|
|
|
|
3
|
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
|
|
|
|
|
4
|
my $sec = $rad * 12 / PI; |
|
1217
|
1
|
|
|
|
|
2
|
( $sec, my $hr ) = modf( $sec ); |
|
1218
|
1
|
|
|
|
|
3
|
( $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
|
|
|
|
8
|
$rslt = sprintf "%.${dp}f", $sec; |
|
1227
|
|
|
|
|
|
|
} |
|
1228
|
1
|
|
|
|
|
4
|
$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
|
25469
|
=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
|
543
|
|
|
1249
|
1
|
|
|
|
|
9
|
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
|
50962
|
|
|
1268
|
29935
|
|
|
|
|
51223
|
sub thetag { |
|
1269
|
29935
|
|
|
|
|
50935
|
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
|
1415
|
|
|
1321
|
3
|
50
|
33
|
|
|
6
|
sub vector_cross_product { |
|
|
3
|
|
|
|
|
11
|
|
|
|
3
|
|
|
|
|
11
|
|
|
1322
|
|
|
|
|
|
|
my ( $b, $c ) = @_; |
|
1323
|
|
|
|
|
|
|
@{ $b } == 3 and @{ $c } == 3 |
|
1324
|
|
|
|
|
|
|
or confess 'Programming error - vector_cross_product arguments', |
|
1325
|
3
|
|
|
|
|
16
|
' 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
|
76907
|
|
|
1341
|
50698
|
50
|
|
|
|
59007
|
sub vector_dot_product { |
|
|
50698
|
|
|
|
|
70157
|
|
|
|
50698
|
|
|
|
|
90542
|
|
|
1342
|
|
|
|
|
|
|
my ( $b, $c ) = @_; |
|
1343
|
|
|
|
|
|
|
@{ $b } == @{ $c } |
|
1344
|
50698
|
|
|
|
|
68393
|
or confess 'Programming error - vector_dot_product arguments ', |
|
1345
|
50698
|
|
|
|
|
59092
|
'must be references to arrays of the same length'; |
|
|
50698
|
|
|
|
|
67700
|
|
|
1346
|
50698
|
|
|
|
|
80374
|
my $prod = 0; |
|
1347
|
152094
|
|
|
|
|
224404
|
my $size = @{ $b } - 1; |
|
1348
|
|
|
|
|
|
|
foreach my $inx ( 0 .. $size ) { |
|
1349
|
50698
|
|
|
|
|
136450
|
$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
|
41596
|
|
|
1361
|
24490
|
50
|
|
|
|
57944
|
sub vector_magnitude { |
|
1362
|
|
|
|
|
|
|
my ( $x ) = @_; |
|
1363
|
|
|
|
|
|
|
ARRAY_REF eq ref $x |
|
1364
|
24490
|
|
|
|
|
36464
|
or confess 'Programming error - vector_magnitude argument ', |
|
1365
|
24490
|
|
|
|
|
31182
|
'must be a reference to an array'; |
|
|
24490
|
|
|
|
|
39322
|
|
|
1366
|
24490
|
|
|
|
|
51066
|
my $mag = 0; |
|
1367
|
73467
|
|
|
|
|
121796
|
my $size = @{ $x } - 1; |
|
1368
|
|
|
|
|
|
|
foreach my $inx ( 0 .. $size ) { |
|
1369
|
24490
|
|
|
|
|
54339
|
$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
|
982
|
|
|
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
|
|
72625
|
|
|
1396
|
42738
|
50
|
|
|
|
73620
|
sub __classisa { |
|
1397
|
42738
|
50
|
|
|
|
72247
|
my ( $invocant, $class ) = @_; |
|
1398
|
42738
|
|
|
|
|
194794
|
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
|
|
221
|
|
|
1409
|
115
|
50
|
|
|
|
243
|
sub __instance { |
|
1410
|
115
|
50
|
|
|
|
413
|
my ( $object, $class ) = @_; |
|
1411
|
115
|
|
|
|
|
566
|
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
|
|
2388
|
|
|
1431
|
|
|
|
|
|
|
sub __parse_time_iso_8601 { |
|
1432
|
51
|
|
|
|
|
84
|
my ( $string ) = @_; |
|
1433
|
51
|
100
|
|
|
|
537
|
|
|
1434
|
|
|
|
|
|
|
my @zone; |
|
1435
|
|
|
|
|
|
|
$string =~ s/ \s* (?: ( Z ) | |
|
1436
|
51
|
|
|
|
|
92
|
( [+-] ) ( [0-9]{2} ) :? ( [0-9]{2} )? ) \z //smx |
|
1437
|
|
|
|
|
|
|
and @zone = ( $1, $2, $3, $4 ); |
|
1438
|
|
|
|
|
|
|
my @date; |
|
1439
|
51
|
50
|
|
|
|
285
|
|
|
|
|
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
|
|
|
|
|
264
|
\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
|
|
|
197
|
} |
|
1478
|
51
|
100
|
100
|
|
|
162
|
|
|
1479
|
5
|
|
|
|
|
14
|
my $offset = pop @date || 0; |
|
1480
|
5
|
|
100
|
|
|
27
|
if ( @zone && !$zone[0] ) { |
|
1481
|
|
|
|
|
|
|
my ( undef, $sign, $hr, $min ) = @zone; |
|
1482
|
|
|
|
|
|
|
$offset -= $sign . ( ( $hr * 60 + ( $min || 0 ) ) * 60 ) |
|
1483
|
51
|
|
|
|
|
110
|
} |
|
1484
|
357
|
100
|
|
|
|
812
|
|
|
1485
|
|
|
|
|
|
|
foreach ( @date ) { |
|
1486
|
|
|
|
|
|
|
defined $_ and s/ [^0-9]+ //smxg; |
|
1487
|
51
|
|
|
|
|
114
|
} |
|
1488
|
|
|
|
|
|
|
|
|
1489
|
51
|
100
|
|
|
|
133
|
$date[0] = __tle_year_to_Gregorian_year( $date[0] ); |
|
1490
|
51
|
100
|
|
|
|
103
|
|
|
1491
|
51
|
|
|
|
|
89
|
defined $date[1] and --$date[1]; |
|
1492
|
|
|
|
|
|
|
defined $date[2] or $date[2] = 1; |
|
1493
|
51
|
|
|
|
|
91
|
my $frc = pop @date; |
|
1494
|
306
|
100
|
|
|
|
548
|
|
|
1495
|
|
|
|
|
|
|
foreach ( @date ) { |
|
1496
|
|
|
|
|
|
|
defined $_ or $_ = 0; |
|
1497
|
51
|
|
|
|
|
77
|
} |
|
1498
|
51
|
100
|
|
|
|
98
|
|
|
1499
|
28
|
|
|
|
|
56
|
my $time; |
|
1500
|
|
|
|
|
|
|
if ( @zone ) { |
|
1501
|
23
|
|
|
|
|
44
|
$time = greg_time_gm( reverse @date ); |
|
1502
|
|
|
|
|
|
|
} else { |
|
1503
|
|
|
|
|
|
|
$time = greg_time_local( reverse @date ); |
|
1504
|
51
|
50
|
66
|
|
|
2035
|
} |
|
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
|
|
|
|
|
162
|
} |
|
1510
|
|
|
|
|
|
|
|
|
1511
|
|
|
|
|
|
|
return $time + $offset; |
|
1512
|
|
|
|
|
|
|
} |
|
1513
|
|
|
|
|
|
|
|
|
1514
|
|
|
|
|
|
|
} |
|
1515
|
30
|
|
|
30
|
|
99
|
|
|
1516
|
30
|
100
|
|
|
|
101
|
sub __sprintf { |
|
1517
|
|
|
|
|
|
|
my ( $tplt, @args ) = @_; |
|
1518
|
18
|
|
|
18
|
|
12149
|
defined $tplt |
|
|
18
|
|
|
|
|
255
|
|
|
|
18
|
|
|
|
|
100
|
|
|
1519
|
27
|
|
|
|
|
234
|
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
|
|
218
|
|
|
1558
|
97
|
100
|
|
|
|
396
|
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__ |