line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
=head1 NAME |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
Astro::Coord::ECI::Moon - Compute the position of the Moon. |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
=head1 SYNOPSIS |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
use Astro::Coord::ECI; |
8
|
|
|
|
|
|
|
use Astro::Coord::ECI::Moon; |
9
|
|
|
|
|
|
|
use Astro::Coord::ECI::Utils qw{deg2rad}; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
# 1600 Pennsylvania Ave, Washington DC USA |
12
|
|
|
|
|
|
|
# latitude 38.899 N, longitude 77.038 W, |
13
|
|
|
|
|
|
|
# altitude 16.68 meters above sea level |
14
|
|
|
|
|
|
|
my $lat = deg2rad (38.899); # Radians |
15
|
|
|
|
|
|
|
my $long = deg2rad (-77.038); # Radians |
16
|
|
|
|
|
|
|
my $alt = 16.68 / 1000; # Kilometers |
17
|
|
|
|
|
|
|
my $moon = Astro::Coord::ECI::Moon->new (); |
18
|
|
|
|
|
|
|
my $sta = Astro::Coord::ECI-> |
19
|
|
|
|
|
|
|
universal (time ())-> |
20
|
|
|
|
|
|
|
geodetic ($lat, $long, $alt); |
21
|
|
|
|
|
|
|
my ($time, $rise) = $sta->next_elevation ($moon); |
22
|
|
|
|
|
|
|
print "Moon @{[$rise ? 'rise' : 'set']} is ", |
23
|
|
|
|
|
|
|
scalar localtime $time, "\n"; |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
=head1 DESCRIPTION |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
This module implements the position of the Moon as a function of time, |
28
|
|
|
|
|
|
|
as described in Jean Meeus' "Astronomical Algorithms," second edition. |
29
|
|
|
|
|
|
|
It is a subclass of L, with the id, |
30
|
|
|
|
|
|
|
name, and diameter attributes initialized appropriately, and the |
31
|
|
|
|
|
|
|
time_set() method overridden to compute the position of the Moon at the |
32
|
|
|
|
|
|
|
given time. |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
=head2 Methods |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
The following methods should be considered public: |
37
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
=over |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=cut |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
package Astro::Coord::ECI::Moon; |
43
|
|
|
|
|
|
|
|
44
|
16
|
|
|
16
|
|
1731
|
use strict; |
|
16
|
|
|
|
|
33
|
|
|
16
|
|
|
|
|
491
|
|
45
|
16
|
|
|
16
|
|
91
|
use warnings; |
|
16
|
|
|
|
|
30
|
|
|
16
|
|
|
|
|
733
|
|
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
our $VERSION = '0.129_01'; |
48
|
|
|
|
|
|
|
|
49
|
16
|
|
|
16
|
|
94
|
use base qw{Astro::Coord::ECI}; |
|
16
|
|
|
|
|
32
|
|
|
16
|
|
|
|
|
1547
|
|
50
|
|
|
|
|
|
|
|
51
|
16
|
|
|
16
|
|
108
|
use Astro::Coord::ECI::Utils qw{ @CARP_NOT :mainstream }; |
|
16
|
|
|
|
|
32
|
|
|
16
|
|
|
|
|
6328
|
|
52
|
16
|
|
|
16
|
|
122
|
use Carp; |
|
16
|
|
|
|
|
35
|
|
|
16
|
|
|
|
|
941
|
|
53
|
16
|
|
|
16
|
|
102
|
use POSIX qw{floor strftime}; |
|
16
|
|
|
|
|
42
|
|
|
16
|
|
|
|
|
104
|
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# Load the periodic terms from the table. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
my %terms; |
58
|
|
|
|
|
|
|
{ # Begin local symbol block. |
59
|
|
|
|
|
|
|
my $where; |
60
|
|
|
|
|
|
|
local $_ = undef; # while (<>) ... does not localize $_. |
61
|
|
|
|
|
|
|
while () { |
62
|
|
|
|
|
|
|
chomp; |
63
|
|
|
|
|
|
|
s/ \A \s+ //smx; |
64
|
|
|
|
|
|
|
s/ \s+ \z //smx; |
65
|
|
|
|
|
|
|
next unless $_; |
66
|
|
|
|
|
|
|
next if m/ \A \s* [#] /smx; |
67
|
|
|
|
|
|
|
s/^-// and do { |
68
|
|
|
|
|
|
|
last if $_ eq 'end'; |
69
|
|
|
|
|
|
|
$where = $terms{$_} ||= []; |
70
|
|
|
|
|
|
|
next; |
71
|
|
|
|
|
|
|
}; |
72
|
|
|
|
|
|
|
s/_//g; |
73
|
|
|
|
|
|
|
push @$where, [split '\s+', $_]; |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
} # End local symbol block. |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
my %static = ( |
78
|
|
|
|
|
|
|
id => 'Moon', |
79
|
|
|
|
|
|
|
name => 'Moon', |
80
|
|
|
|
|
|
|
diameter => 3476, |
81
|
|
|
|
|
|
|
); |
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
my $weaken = eval { |
84
|
|
|
|
|
|
|
require Scalar::Util; |
85
|
|
|
|
|
|
|
Scalar::Util->can('weaken'); |
86
|
|
|
|
|
|
|
}; |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
our $Singleton = $weaken; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
my %object; # By class |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=item $moon = Astro::Coord::ECI::Moon->new (); |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
This method instantiates an object to represent the coordinates of the |
95
|
|
|
|
|
|
|
Moon. This is a subclass of Astro::Coord::ECI, with the id and name |
96
|
|
|
|
|
|
|
attributes set to 'Moon', and the diameter attribute set to 3476 km |
97
|
|
|
|
|
|
|
per Jean Meeus' "Astronomical Algorithms", 2nd Edition, Appendix I, |
98
|
|
|
|
|
|
|
page 407. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Any arguments are passed to the set() method once the object has been |
101
|
|
|
|
|
|
|
instantiated. Yes, you can override the "hard-wired" id and name in |
102
|
|
|
|
|
|
|
this way. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
If C<$Astro::Coord::ECI::Moon::Singleton> is true, you get a singleton |
105
|
|
|
|
|
|
|
object; that is, only one object is instantiated and subsequent calls to |
106
|
|
|
|
|
|
|
C just return that object. If higher-accuracy subclasses are ever |
107
|
|
|
|
|
|
|
implemented, there will be one singleton for each class. |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
The singleton logic only works if L exports |
110
|
|
|
|
|
|
|
C. If it does not, the setting of |
111
|
|
|
|
|
|
|
C<$Astro::Coord::ECI::Moon::Singleton> is silently ignored. The default |
112
|
|
|
|
|
|
|
is true if L can be loaded and exports |
113
|
|
|
|
|
|
|
C, and false otherwise. |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
=cut |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
sub new { |
118
|
13
|
|
|
13
|
1
|
1521
|
my ($class, @args) = @_; |
119
|
13
|
50
|
|
|
|
42
|
ref $class and $class = ref $class; |
120
|
13
|
100
|
66
|
|
|
88
|
if ( $Singleton && $weaken && __classisa( $class, __PACKAGE__ ) ) { |
|
|
|
66
|
|
|
|
|
121
|
11
|
|
|
|
|
23
|
my $self; |
122
|
11
|
100
|
|
|
|
45
|
if ( $self = $object{$class} ) { |
123
|
1
|
50
|
|
|
|
3
|
$self->set( @args ) if @args; |
124
|
1
|
|
|
|
|
6
|
return $self; |
125
|
|
|
|
|
|
|
} else { |
126
|
10
|
|
|
|
|
61
|
$self = $object{$class} = $class->SUPER::new (%static, @args); |
127
|
10
|
|
|
|
|
39
|
$weaken->( $object{$class} ); |
128
|
10
|
|
|
|
|
43
|
return $self; |
129
|
|
|
|
|
|
|
} |
130
|
|
|
|
|
|
|
} else { |
131
|
2
|
|
|
|
|
11
|
return $class->SUPER::new (%static, @args); |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
=item @almanac = $moon->almanac ($station, $start, $end); |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
This method produces almanac data for the Moon for the given observing |
138
|
|
|
|
|
|
|
station, between the given start and end times. The station is assumed |
139
|
|
|
|
|
|
|
to be Earth-Fixed - that is, you can't do this for something in orbit. |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
The C<$station> argument may be omitted if the C attribute has |
142
|
|
|
|
|
|
|
been set. That is, this method can also be called as |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
@almanac = $moon->almanac( $start, $end ) |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
The start time defaults to the current time setting of the $moon |
147
|
|
|
|
|
|
|
object, and the end time defaults to a day after the start time. |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
The almanac data consists of a list of list references. Each list |
150
|
|
|
|
|
|
|
reference points to a list containing the following elements: |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
[0] => time |
153
|
|
|
|
|
|
|
[1] => event (string) |
154
|
|
|
|
|
|
|
[2] => detail (integer) |
155
|
|
|
|
|
|
|
[3] => description (string) |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
The @almanac list is returned sorted by time. |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
The following events, details, and descriptions are at least |
160
|
|
|
|
|
|
|
potentially returned: |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
horizon: 0 = Moon set, 1 = Moon rise; |
163
|
|
|
|
|
|
|
transit: 1 = Moon transits meridian; |
164
|
|
|
|
|
|
|
quarter: 0 = new moon, 1 = first quarter, |
165
|
|
|
|
|
|
|
2 = full moon, 3 = last quarter. |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=cut |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
sub __almanac_event_type_iterator { |
170
|
3
|
|
|
3
|
|
10
|
my ( $self, $station ) = @_; |
171
|
|
|
|
|
|
|
|
172
|
3
|
|
|
|
|
6
|
my $inx = 0; |
173
|
|
|
|
|
|
|
|
174
|
3
|
|
|
|
|
13
|
my $horizon = $station->__get_almanac_horizon(); |
175
|
|
|
|
|
|
|
|
176
|
3
|
|
|
|
|
32
|
my @events = ( |
177
|
|
|
|
|
|
|
[ $station, next_elevation => [ $self, $horizon, 1 ], |
178
|
|
|
|
|
|
|
horizon => '__horizon_name' ], |
179
|
|
|
|
|
|
|
[ $station, next_meridian => [ $self ], |
180
|
|
|
|
|
|
|
transit => '__transit_name' ], |
181
|
|
|
|
|
|
|
[ $self, next_quarter => [], quarter => '__quarter_name' ], |
182
|
|
|
|
|
|
|
); |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
return sub { |
185
|
|
|
|
|
|
|
$inx < @events |
186
|
12
|
100
|
|
12
|
|
42
|
and return @{ $events[$inx++] }; |
|
9
|
|
|
|
|
66
|
|
187
|
3
|
|
|
|
|
17
|
return; |
188
|
3
|
|
|
|
|
20
|
}; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
16
|
|
|
16
|
|
10555
|
use Astro::Coord::ECI::Mixin qw{ almanac }; |
|
16
|
|
|
|
|
42
|
|
|
16
|
|
|
|
|
974
|
|
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
=item @almanac = $moon->almanac_hash($station, $start, $end); |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
This convenience method wraps $moon->almanac(), but returns a list of |
196
|
|
|
|
|
|
|
hash references, sort of like Astro::Coord::ECI::TLE->pass() |
197
|
|
|
|
|
|
|
does. The hashes contain the following keys: |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
{almanac} => { |
200
|
|
|
|
|
|
|
{event} => the event type; |
201
|
|
|
|
|
|
|
{detail} => the event detail (typically 0 or 1); |
202
|
|
|
|
|
|
|
{description} => the event description; |
203
|
|
|
|
|
|
|
} |
204
|
|
|
|
|
|
|
{body} => the original object ($moon); |
205
|
|
|
|
|
|
|
{station} => the observing station; |
206
|
|
|
|
|
|
|
{time} => the time the quarter occurred. |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
The {time}, {event}, {detail}, and {description} keys correspond to |
209
|
|
|
|
|
|
|
elements 0 through 3 of the list returned by almanac(). |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=cut |
212
|
|
|
|
|
|
|
|
213
|
16
|
|
|
16
|
|
114
|
use Astro::Coord::ECI::Mixin qw{ almanac_hash }; |
|
16
|
|
|
|
|
51
|
|
|
16
|
|
|
|
|
2252
|
|
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=item $coord2 = $coord->clone (); |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
If singleton objects are enabled, this override of the superclass' |
218
|
|
|
|
|
|
|
method simply returns the invocant. Otherwise it does a deep clone of an |
219
|
|
|
|
|
|
|
object, producing a different but identical object. |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
Prior to version 0.099_01 it always returned a clone. Yes, |
222
|
|
|
|
|
|
|
this is a change in long-standing functionality, but a long-standing bug |
223
|
|
|
|
|
|
|
is still a bug. |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=cut |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
sub clone { |
228
|
2
|
|
|
2
|
1
|
471
|
my ( $self ) = @_; |
229
|
2
|
100
|
66
|
|
|
13
|
$Singleton |
230
|
|
|
|
|
|
|
and $weaken |
231
|
|
|
|
|
|
|
and return $self; |
232
|
1
|
|
|
|
|
10
|
return $self->SUPER::clone(); |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=item $elevation = $moon->correct_for_refraction( $elevation ) |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
This override of the superclass' method simply returns the elevation |
238
|
|
|
|
|
|
|
passed to it. Since the Moon has no atmosphere to speak of, there should |
239
|
|
|
|
|
|
|
be no diffraction to speak of either. |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
See the L C and |
242
|
|
|
|
|
|
|
C documentation for whether this class' |
243
|
|
|
|
|
|
|
C method is actually called by those methods. |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=cut |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
sub correct_for_refraction { |
248
|
0
|
|
|
0
|
1
|
0
|
my ( undef, $elevation ) = @_; # Invocant unused |
249
|
0
|
|
|
|
|
0
|
return $elevation; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=item ($time, $quarter, $desc) = $moon->next_quarter ($want); |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
This method calculates the time of the next quarter-phase of the Moon |
255
|
|
|
|
|
|
|
after the current time setting of the $moon object. The returns are the |
256
|
|
|
|
|
|
|
time, which quarter-phase it is as a number from 0 (new moon) to |
257
|
|
|
|
|
|
|
3 (last quarter), and a string describing the phase. If called in |
258
|
|
|
|
|
|
|
scalar context, you just get the time. |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
The optional $want argument says which phase you want. |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
As a side effect, the time of the $moon object ends up set to the |
263
|
|
|
|
|
|
|
returned time. |
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
The method of calculation is successive approximation, and actually |
266
|
|
|
|
|
|
|
returns the second B the quarter. |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
=cut |
269
|
|
|
|
|
|
|
|
270
|
16
|
|
|
16
|
|
125
|
use constant NEXT_QUARTER_INCREMENT => 86400 * 6; # 6 days. |
|
16
|
|
|
|
|
40
|
|
|
16
|
|
|
|
|
1365
|
|
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
*__next_quarter_coordinate = __PACKAGE__->can( |
273
|
|
|
|
|
|
|
'phase' ); |
274
|
|
|
|
|
|
|
|
275
|
16
|
|
|
16
|
|
112
|
use Astro::Coord::ECI::Mixin qw{ next_quarter }; |
|
16
|
|
|
|
|
36
|
|
|
16
|
|
|
|
|
1374
|
|
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=item $hash_reference = $moon->next_quarter_hash($want); |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
This convenience method wraps $moon->next_quarter(), but returns the |
280
|
|
|
|
|
|
|
data in a hash reference, sort of like Astro::Coord::ECI::TLE->pass() |
281
|
|
|
|
|
|
|
does. The hash contains the following keys: |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
{body} => the original object ($moon); |
284
|
|
|
|
|
|
|
{almanac} => { |
285
|
|
|
|
|
|
|
{event} => 'quarter', |
286
|
|
|
|
|
|
|
{detail} => the quarter number (0 through 3); |
287
|
|
|
|
|
|
|
{description} => the quarter description; |
288
|
|
|
|
|
|
|
} |
289
|
|
|
|
|
|
|
{time} => the time the quarter occurred. |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
The {time}, {detail}, and {description} keys correspond to elements 0 |
292
|
|
|
|
|
|
|
through 2 of the list returned by next_quarter(). |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
=cut |
295
|
|
|
|
|
|
|
|
296
|
16
|
|
|
16
|
|
135
|
use Astro::Coord::ECI::Mixin qw{ next_quarter_hash }; |
|
16
|
|
|
|
|
29
|
|
|
16
|
|
|
|
|
15201
|
|
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=item $period = $moon->period () |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
This method returns the sidereal period of the Moon, per Appendix I |
301
|
|
|
|
|
|
|
(pg 408) of Jean Meeus' "Astronomical Algorithms," 2nd edition. |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
=cut |
304
|
|
|
|
|
|
|
|
305
|
18
|
|
|
18
|
1
|
76
|
sub period {return 2360591.5968} # 27.321662 * 86400 |
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
sub __horizon_name_tplt { |
308
|
6
|
|
|
6
|
|
12
|
my ( $self ) = @_; |
309
|
6
|
50
|
|
|
|
32
|
return $self->__object_is_self_named() ? |
310
|
|
|
|
|
|
|
[ '%s set', '%s rise' ] : |
311
|
|
|
|
|
|
|
$self->SUPER::__horizon_name_tplt(); |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
sub __quarter_name { |
315
|
4
|
|
|
4
|
|
16
|
my ( $self, $event, $tplt ) = @_; |
316
|
4
|
|
50
|
|
|
32
|
$tplt ||= [ 'New %s', 'First quarter %s', 'Full %s', 'Last quarter %s' ]; |
317
|
4
|
|
|
|
|
25
|
return $self->__event_name( $event, $tplt ); |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=item ($phase, $illum) = $moon->phase ($time); |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
This method calculates the current phase of the moon and its illuminated |
323
|
|
|
|
|
|
|
fraction. If the time is omitted, the current time of the $moon object |
324
|
|
|
|
|
|
|
is used. |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
The phase is returned as a number from C<0> to C<2 * PI> radians, with |
327
|
|
|
|
|
|
|
C<0> being New Moon, C being First Quarter, and so on. The |
328
|
|
|
|
|
|
|
illuminated fraction is a number from C<0> (New Moon) to C<1> (Full |
329
|
|
|
|
|
|
|
Moon). |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
If called in scalar context, you get the phase. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
This can be called as a class method, but if you do this the time |
334
|
|
|
|
|
|
|
must be specified. |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
Jean Meeus' "Astronomical Algorithms", 2nd Edition, Chapter 49 page |
337
|
|
|
|
|
|
|
349, defines the phases of the moon in terms of the difference between |
338
|
|
|
|
|
|
|
the geocentric longitudes of the Moon and Sun - specifically, that |
339
|
|
|
|
|
|
|
new, first quarter, full, and last quarter are the moments when this |
340
|
|
|
|
|
|
|
difference is 0, 90, 180, and 270 degrees respectively. |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
Not quite above reproach, this module simply defines the phase of the |
343
|
|
|
|
|
|
|
Moon as the difference between these two quantities, even if it is not |
344
|
|
|
|
|
|
|
a multiple of 90 degrees. This is different than the "phase angle" of |
345
|
|
|
|
|
|
|
the Moon, which Meeus defines as the elongation of the Earth from the |
346
|
|
|
|
|
|
|
Sun, as seen from the Moon. Because we take the "phase angle" as just |
347
|
|
|
|
|
|
|
pi - the phase (in radians), we introduce an error of about 0.3% in |
348
|
|
|
|
|
|
|
the illumination calculation. |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=cut |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
sub phase { |
353
|
122
|
|
|
122
|
1
|
256
|
my ($self, $time) = @_; |
354
|
|
|
|
|
|
|
|
355
|
122
|
50
|
33
|
|
|
310
|
(ref $self || $time) or croak <
|
356
|
|
|
|
|
|
|
Error - You must specify a time if you call phase() as a class method. |
357
|
|
|
|
|
|
|
eod |
358
|
|
|
|
|
|
|
|
359
|
122
|
50
|
|
|
|
225
|
$self = $self->new () unless ref $self; |
360
|
|
|
|
|
|
|
|
361
|
122
|
50
|
|
|
|
254
|
$self->universal ($time) if $time; |
362
|
|
|
|
|
|
|
|
363
|
122
|
|
|
|
|
278
|
my $sun = $self->get( 'sun' )->universal( $self->universal() ); |
364
|
|
|
|
|
|
|
|
365
|
122
|
|
|
|
|
282
|
my (undef, $longs) = $sun->ecliptic (); |
366
|
122
|
|
|
|
|
359
|
my (undef, $longm) = $self->ecliptic (); |
367
|
|
|
|
|
|
|
|
368
|
122
|
|
|
|
|
312
|
my $phase = mod2pi ($longm - $longs); |
369
|
122
|
100
|
|
|
|
516
|
return wantarray ? ($phase, (1 + cos ($self->PI - $phase)) / 2) : $phase; |
370
|
|
|
|
|
|
|
} |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
=item $moon->time_set () |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
This method sets coordinates of the object to the coordinates of the |
375
|
|
|
|
|
|
|
Moon at the object's currently-set universal time. The velocity |
376
|
|
|
|
|
|
|
components are arbitrarily set to 0, since Meeus' algorithm does not |
377
|
|
|
|
|
|
|
provide this information. The 'equinox_dynamical' attribute is set to |
378
|
|
|
|
|
|
|
the currently-set dynamical time. |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
Although there's no reason this method can't be called directly, it |
381
|
|
|
|
|
|
|
exists to take advantage of the hook in the B |
382
|
|
|
|
|
|
|
object, to allow the position of the Moon to be computed when the |
383
|
|
|
|
|
|
|
object's time is set. |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
The computation comes from Jean Meeus' "Astronomical Algorithms", 2nd |
386
|
|
|
|
|
|
|
Edition, Chapter 47, pages 337ff. Meeus gives the accuracy as 10 |
387
|
|
|
|
|
|
|
seconds of arc in latitude, and 4 seconds of arc in longitude. He |
388
|
|
|
|
|
|
|
credits the algorithm to M. Chalpront-Touze and J. Chalpront, "The |
389
|
|
|
|
|
|
|
Lunar Ephemeris ELP 2000" from I volume |
390
|
|
|
|
|
|
|
124, pp 50-62 (1983), but the formulae for the mean arguments to |
391
|
|
|
|
|
|
|
J. Chalpront, M. Chalpront-Touze, and G. Francou, I
|
392
|
|
|
|
|
|
|
ELP 2000-82B de nouvelles valeurs des parametres orbitaux de la Lune |
393
|
|
|
|
|
|
|
et du barycentre Terre-Lune>, Paris, January 1998. |
394
|
|
|
|
|
|
|
|
395
|
|
|
|
|
|
|
=cut |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
sub time_set { |
398
|
675
|
|
|
675
|
1
|
1063
|
my $self = shift; |
399
|
|
|
|
|
|
|
|
400
|
675
|
|
|
|
|
1705
|
my $time = $self->dynamical; |
401
|
|
|
|
|
|
|
|
402
|
675
|
|
|
|
|
1616
|
my $T = jcent2000 ($time); # Meeus (22.1) |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# Moon's mean longitude. |
405
|
|
|
|
|
|
|
|
406
|
675
|
|
|
|
|
2032
|
my $Lprime = mod2pi (deg2rad ( # Meeus (47.1) |
407
|
|
|
|
|
|
|
(((- ($T / 65_194_000) + |
408
|
|
|
|
|
|
|
1 / 538_841) * $T - 0.0015786) * $T + |
409
|
|
|
|
|
|
|
481267.88123421) * $T + 218.3164477)); |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
# Moon's mean elongation. |
412
|
|
|
|
|
|
|
|
413
|
675
|
|
|
|
|
1823
|
my $D = mod2pi (deg2rad (((($T / 113_065_000 + # Meeus (47.2) |
414
|
|
|
|
|
|
|
1 / 545_868) * $T - 0.0018819) * $T + |
415
|
|
|
|
|
|
|
445267.1114034) * $T + 297.8501921)); |
416
|
|
|
|
|
|
|
|
417
|
|
|
|
|
|
|
# Sun's mean anomaly. |
418
|
|
|
|
|
|
|
|
419
|
675
|
|
|
|
|
1561
|
my $M = mod2pi (deg2rad ((($T / 24_490_000 - # Meeus (47.3) |
420
|
|
|
|
|
|
|
0.000_1536) * $T + 35999.050_2909) * $T + |
421
|
|
|
|
|
|
|
357.5291092)); |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
# Moon's mean anomaly. |
424
|
|
|
|
|
|
|
|
425
|
675
|
|
|
|
|
1743
|
my $Mprime = mod2pi (deg2rad ((((- $T / 14_712_000 + # Meeus (47.4) |
426
|
|
|
|
|
|
|
1 / 69_699) * $T + 0.008_7414) * $T + |
427
|
|
|
|
|
|
|
477198.867_5055) * $T + 134.963_3964)); |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
# Moon's argument of latitude (mean distance |
430
|
|
|
|
|
|
|
# from ascending node). |
431
|
|
|
|
|
|
|
|
432
|
675
|
|
|
|
|
1608
|
my $F = mod2pi (deg2rad (((($T / 863_310_000 - # Meeus (47.5) |
433
|
|
|
|
|
|
|
1 / 3_526_000) * $T - 0.003_6539) * $T + |
434
|
|
|
|
|
|
|
483202.017_5233) * $T + 93.272_0950)); |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
# Eccentricity correction factor. |
437
|
|
|
|
|
|
|
|
438
|
675
|
|
|
|
|
1180
|
my $E = (- 0.000_0074 * $T - 0.002_516) * $T + 1; # Meeus (47.6) |
439
|
675
|
|
|
|
|
1409
|
my @efac = (1, $E, $E * $E); |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
# Compute "further arguments". |
442
|
|
|
|
|
|
|
|
443
|
675
|
|
|
|
|
1420
|
my $A1 = mod2pi (deg2rad (131.849 * $T + 119.75)); # Venus |
444
|
675
|
|
|
|
|
1421
|
my $A2 = mod2pi (deg2rad (479264.290 * $T + 53.09)); # Jupiter |
445
|
675
|
|
|
|
|
1479
|
my $A3 = mod2pi (deg2rad (481266.484 * $T + 313.45)); # undocumented |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
# Compute periodic terms for longitude (sigma l) and |
448
|
|
|
|
|
|
|
# distance (sigma r). |
449
|
|
|
|
|
|
|
|
450
|
675
|
|
|
|
|
1288
|
my ($sigmal, $sigmar) = (0, 0); |
451
|
675
|
|
|
|
|
1057
|
foreach (@{$terms{lr}}) { |
|
675
|
|
|
|
|
1634
|
|
452
|
40500
|
|
|
|
|
76272
|
my ($mulD, $mulM, $mulMprime, $mulF, $sincof, $coscof) = @$_; |
453
|
40500
|
100
|
|
|
|
68360
|
if ($mulM) { |
454
|
18900
|
|
33
|
|
|
34497
|
my $corr = $efac[abs $mulM] || confess <
|
455
|
|
|
|
|
|
|
Programming error - M multiple greater than 2. |
456
|
|
|
|
|
|
|
eod |
457
|
18900
|
|
|
|
|
29764
|
$sincof *= $corr; |
458
|
18900
|
|
|
|
|
28160
|
$coscof *= $corr; |
459
|
|
|
|
|
|
|
} |
460
|
40500
|
|
|
|
|
72681
|
my $arg = $D * $mulD + $M * $mulM + $Mprime * $mulMprime + |
461
|
|
|
|
|
|
|
$F * $mulF; |
462
|
40500
|
|
|
|
|
67933
|
$sigmal += $sincof * sin ($arg); |
463
|
40500
|
|
|
|
|
73410
|
$sigmar += $coscof * cos ($arg); |
464
|
|
|
|
|
|
|
} |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# Compute periodic terms for latitude (sigma b). |
467
|
|
|
|
|
|
|
|
468
|
675
|
|
|
|
|
1286
|
my $sigmab = 0; |
469
|
675
|
|
|
|
|
996
|
foreach (@{$terms{b}}) { |
|
675
|
|
|
|
|
1567
|
|
470
|
40500
|
|
|
|
|
73234
|
my ($mulD, $mulM, $mulMprime, $mulF, $sincof) = @$_; |
471
|
40500
|
100
|
|
|
|
67953
|
if ($mulM) { |
472
|
17550
|
|
33
|
|
|
32299
|
my $corr = $efac[abs $mulM] || confess <
|
473
|
|
|
|
|
|
|
Programming error - M multiple greater than 2. |
474
|
|
|
|
|
|
|
eod |
475
|
17550
|
|
|
|
|
29155
|
$sincof *= $corr; |
476
|
|
|
|
|
|
|
} |
477
|
40500
|
|
|
|
|
73187
|
my $arg = $D * $mulD + $M * $mulM + $Mprime * $mulMprime + |
478
|
|
|
|
|
|
|
$F * $mulF; |
479
|
40500
|
|
|
|
|
75666
|
$sigmab += $sincof * sin ($arg); |
480
|
|
|
|
|
|
|
} |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
# Add other terms |
483
|
|
|
|
|
|
|
|
484
|
675
|
|
|
|
|
1753
|
$sigmal += 3958 * sin ($A1) + 1962 * sin ($Lprime - $F) + |
485
|
|
|
|
|
|
|
318 * sin ($A2); |
486
|
675
|
|
|
|
|
2091
|
$sigmab += - 2235 * sin ($Lprime) + 382 * sin ($A3) + |
487
|
|
|
|
|
|
|
175 * sin ($A1 - $F) + 175 * sin ($A1 + $F) + |
488
|
|
|
|
|
|
|
127 * sin ($Lprime - $Mprime) - 115 * sin ($Lprime + $Mprime); |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
# Coordinates of Moon (finally!) |
491
|
|
|
|
|
|
|
|
492
|
675
|
|
|
|
|
1919
|
my $lambda = deg2rad ($sigmal / 1_000_000) + $Lprime; |
493
|
675
|
|
|
|
|
1506
|
my $beta = deg2rad ($sigmab / 1_000_000); |
494
|
675
|
|
|
|
|
1151
|
my $delta = $sigmar / 1000 + 385_000.56; |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
# Correct longitude for nutation (from Chapter 22, pg 144). |
497
|
|
|
|
|
|
|
|
498
|
675
|
|
|
|
|
2005
|
$lambda += ( $self->nutation( $time ) )[0]; |
499
|
|
|
|
|
|
|
|
500
|
675
|
|
|
|
|
1449
|
$self->ecliptic ($beta, mod2pi( $lambda ), $delta); |
501
|
|
|
|
|
|
|
## $self->set (equinox_dynamical => $time); |
502
|
675
|
|
|
|
|
2035
|
$self->equinox_dynamical ($time); |
503
|
675
|
|
|
|
|
1603
|
return $self; |
504
|
|
|
|
|
|
|
} |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
# The moon is normally positioned in inertial coordinates. |
507
|
|
|
|
|
|
|
|
508
|
12
|
|
|
12
|
|
45
|
sub __initial_inertial { return 1 } |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
1; |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=back |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
=head2 Historical Calculations |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
This class was written for the purpose of calculating whether the Moon |
517
|
|
|
|
|
|
|
was visible from given point on the Earth (or in space) at a given time |
518
|
|
|
|
|
|
|
in or reasonably close to the present. I can not say how accurate it is |
519
|
|
|
|
|
|
|
at times far from the present. |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
See |
522
|
|
|
|
|
|
|
L |
523
|
|
|
|
|
|
|
in the L documentation |
524
|
|
|
|
|
|
|
for a discussion of input and output time conversion. |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=head1 ACKNOWLEDGMENTS |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
The author wishes to acknowledge Jean Meeus, whose book "Astronomical |
529
|
|
|
|
|
|
|
Algorithms" (second edition) formed the basis for this module. |
530
|
|
|
|
|
|
|
|
531
|
|
|
|
|
|
|
=head1 SEE ALSO |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
The L |
534
|
|
|
|
|
|
|
documentation for a discussion of how the pieces/parts of this |
535
|
|
|
|
|
|
|
distribution go together and how to use them. |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
L by Brett Hamilton, which contains a |
538
|
|
|
|
|
|
|
function-based module to compute the current phase, distance and angular |
539
|
|
|
|
|
|
|
diameter of the Moon, as well as the angular diameter and distance of |
540
|
|
|
|
|
|
|
the Sun. |
541
|
|
|
|
|
|
|
|
542
|
|
|
|
|
|
|
=head1 SUPPORT |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
Support is by the author. Please file bug reports at |
545
|
|
|
|
|
|
|
L, |
546
|
|
|
|
|
|
|
L, or in |
547
|
|
|
|
|
|
|
electronic mail to the author. |
548
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
=head1 AUTHOR |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
Thomas R. Wyant, III (F) |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
Copyright (C) 2005-2023 by Thomas R. Wyant, III |
556
|
|
|
|
|
|
|
|
557
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
558
|
|
|
|
|
|
|
under the same terms as Perl 5.10.0. For more details, see the full text |
559
|
|
|
|
|
|
|
of the licenses in the directory LICENSES. |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful, but |
562
|
|
|
|
|
|
|
without any warranty; without even the implied warranty of |
563
|
|
|
|
|
|
|
merchantability or fitness for a particular purpose. |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
=cut |
566
|
|
|
|
|
|
|
|
567
|
|
|
|
|
|
|
__DATA__ |