line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# |
2
|
|
|
|
|
|
|
# Written by Travis Kent Beste |
3
|
|
|
|
|
|
|
# Mon Sep 13 10:49:23 CDT 2010 |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
package Insolation; |
6
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
22201
|
use 5.008000; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
28
|
|
8
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
60
|
|
9
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
24
|
|
10
|
|
|
|
|
|
|
|
11
|
1
|
|
|
1
|
|
389561
|
use Math::Trig; |
|
1
|
|
|
|
|
27408
|
|
|
1
|
|
|
|
|
13603
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
require Exporter; |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
# Items to export into callers namespace by default. Note: do not export |
18
|
|
|
|
|
|
|
# names by default without a very good reason. Use EXPORT_OK instead. |
19
|
|
|
|
|
|
|
# Do not simply export all your public functions/methods/constants. |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
# This allows declaration use Insolation ':all'; |
22
|
|
|
|
|
|
|
# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK |
23
|
|
|
|
|
|
|
# will save memory. |
24
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => [ qw( |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
) ] ); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
our @EXPORT = qw( |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
); |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
our $VERSION = '0.01'; |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
#----------------------------------------# |
37
|
|
|
|
|
|
|
# |
38
|
|
|
|
|
|
|
#----------------------------------------# |
39
|
|
|
|
|
|
|
sub new { |
40
|
0
|
|
|
0
|
0
|
|
my $class = shift; |
41
|
0
|
|
|
|
|
|
my %args = @_; |
42
|
|
|
|
|
|
|
|
43
|
0
|
|
|
|
|
|
my %fields = ( |
44
|
|
|
|
|
|
|
longitude => 0, |
45
|
|
|
|
|
|
|
latitude => 0, |
46
|
|
|
|
|
|
|
month => 0, |
47
|
|
|
|
|
|
|
_min_month => 0, |
48
|
|
|
|
|
|
|
_max_month => 0, |
49
|
|
|
|
|
|
|
year => 0, |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# calculated data, hold it here while they decide how to output it |
52
|
|
|
|
|
|
|
data => {}, |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
# calculated variables |
55
|
|
|
|
|
|
|
SCRATCH => { |
56
|
|
|
|
|
|
|
ECCEN => 0, # orbital parameter |
57
|
|
|
|
|
|
|
OBLIQ => 0, # orbital parameter |
58
|
|
|
|
|
|
|
OMEGVP => 0, # orbital parameter |
59
|
|
|
|
|
|
|
ITZONE => 0, # time zone |
60
|
|
|
|
|
|
|
}, |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# assign constants |
63
|
|
|
|
|
|
|
CONSTANTS => { |
64
|
|
|
|
|
|
|
PI => 4 * atan2(1, 1), # compute value of PI at run time - accurately |
65
|
|
|
|
|
|
|
TWOPI => (2 * (4 * atan2(1, 1))), # compute value of PI at run time - accurately |
66
|
|
|
|
|
|
|
RSUND => .267, # mean radius of Sun in degrees |
67
|
|
|
|
|
|
|
REFRAC => .583, # effective Sun disk increase |
68
|
|
|
|
|
|
|
EDAYZY => 365.2425, # actual length of a year - now |
69
|
|
|
|
|
|
|
DAYzMO => [31,28,31, 30,31,30, 31,31,30, 31,30,31], # days in each month - we calculate leap year below |
70
|
|
|
|
|
|
|
EARTH_WATT_M2 => 1367, # amount of energy that reaches the earth - watts/(m*m) |
71
|
|
|
|
|
|
|
}, |
72
|
|
|
|
|
|
|
); |
73
|
|
|
|
|
|
|
|
74
|
0
|
|
|
|
|
|
my $self = { |
75
|
|
|
|
|
|
|
%fields |
76
|
|
|
|
|
|
|
}; |
77
|
0
|
|
|
|
|
|
bless $self, $class; |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
#print Dumper $self; |
80
|
|
|
|
|
|
|
#exit; |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
#--------------------# |
83
|
|
|
|
|
|
|
# validate input |
84
|
|
|
|
|
|
|
#--------------------# |
85
|
|
|
|
|
|
|
|
86
|
0
|
0
|
|
|
|
|
if (defined($args{'Longitude'})) { |
87
|
0
|
|
|
|
|
|
$self->set_longitude($args{Longitude}); |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
0
|
0
|
|
|
|
|
if (defined($args{'Latitude'})) { |
91
|
0
|
|
|
|
|
|
$self->set_latitude($args{Latitude}); |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
0
|
|
|
|
|
|
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); |
95
|
0
|
0
|
|
|
|
|
if (defined($args{'Month'})) { |
96
|
0
|
|
|
|
|
|
$self->set_month($args{Month}); |
97
|
|
|
|
|
|
|
} else { |
98
|
0
|
|
|
|
|
|
$self->{month} = -1; # will end up computing for all months |
99
|
0
|
|
|
|
|
|
$self->{_min_month} = 1; |
100
|
0
|
|
|
|
|
|
$self->{_max_month} = 12; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
0
|
0
|
|
|
|
|
if (defined($args{'Year'})) { |
104
|
0
|
|
|
|
|
|
$self->set_year($args{Year}); |
105
|
|
|
|
|
|
|
} else { |
106
|
0
|
|
|
|
|
|
$self->{year} = $year + 1900; # set default year to this year |
107
|
|
|
|
|
|
|
} |
108
|
|
|
|
|
|
|
|
109
|
0
|
|
|
|
|
|
return $self; |
110
|
|
|
|
|
|
|
} |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
#----------------------------------------# |
113
|
|
|
|
|
|
|
# |
114
|
|
|
|
|
|
|
#----------------------------------------# |
115
|
|
|
|
|
|
|
sub DESTROY { |
116
|
0
|
|
|
0
|
|
|
my $self = shift; |
117
|
|
|
|
|
|
|
|
118
|
0
|
0
|
|
|
|
|
$self->SUPER::DESTROY if $self->can("SUPER::DESTROY"); |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
# Preloaded methods go here. |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
#----------------------------------------# |
124
|
|
|
|
|
|
|
# |
125
|
|
|
|
|
|
|
#----------------------------------------# |
126
|
|
|
|
|
|
|
sub set_year { |
127
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
128
|
0
|
|
|
|
|
|
my $year = shift; |
129
|
|
|
|
|
|
|
|
130
|
0
|
|
|
|
|
|
$self->{year} = sprintf("%d", $year); |
131
|
|
|
|
|
|
|
} |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
#----------------------------------------# |
134
|
|
|
|
|
|
|
# |
135
|
|
|
|
|
|
|
#----------------------------------------# |
136
|
|
|
|
|
|
|
sub set_month { |
137
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
138
|
0
|
|
|
|
|
|
my $month = shift; |
139
|
|
|
|
|
|
|
|
140
|
0
|
0
|
|
|
|
|
if ($self->isvalid_month($month)) { |
141
|
0
|
|
|
|
|
|
$self->{'month'} = sprintf("%d", $month); |
142
|
0
|
|
|
|
|
|
$self->{_min_month} = $self->{month}; |
143
|
0
|
|
|
|
|
|
$self->{_max_month} = $self->{month}; |
144
|
|
|
|
|
|
|
} else { |
145
|
0
|
|
|
|
|
|
print "error with month\n"; |
146
|
0
|
|
|
|
|
|
exit(1); |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
} |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
#----------------------------------------# |
151
|
|
|
|
|
|
|
# |
152
|
|
|
|
|
|
|
#----------------------------------------# |
153
|
|
|
|
|
|
|
sub set_longitude { |
154
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
155
|
0
|
|
|
|
|
|
my $longitude = shift; |
156
|
|
|
|
|
|
|
|
157
|
0
|
0
|
|
|
|
|
if ($self->isvalid_longitude) { |
158
|
0
|
0
|
|
|
|
|
if ($self->isvalid_longitude($longitude)) { |
159
|
0
|
|
|
|
|
|
$self->{longitude} = $longitude; |
160
|
|
|
|
|
|
|
} else { |
161
|
0
|
|
|
|
|
|
print "error with longitude\n"; |
162
|
0
|
|
|
|
|
|
exit(1); |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
#----------------------------------------# |
168
|
|
|
|
|
|
|
# |
169
|
|
|
|
|
|
|
#----------------------------------------# |
170
|
|
|
|
|
|
|
sub set_latitude { |
171
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
172
|
0
|
|
|
|
|
|
my $latitude = shift; |
173
|
|
|
|
|
|
|
|
174
|
0
|
0
|
|
|
|
|
if ($self->isvalid_latitude($latitude)) { |
175
|
0
|
|
|
|
|
|
$self->{latitude} = $latitude; |
176
|
|
|
|
|
|
|
} else { |
177
|
0
|
|
|
|
|
|
print "error with latitude\n"; |
178
|
0
|
|
|
|
|
|
exit(1); |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
} |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
#----------------------------------------# |
183
|
|
|
|
|
|
|
# |
184
|
|
|
|
|
|
|
#----------------------------------------# |
185
|
|
|
|
|
|
|
sub isvalid_year { |
186
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
187
|
0
|
|
|
|
|
|
my $year = shift; |
188
|
|
|
|
|
|
|
|
189
|
0
|
0
|
0
|
|
|
|
if ( ($year >= 1) && ($year <= 9999) ) { |
190
|
0
|
|
|
|
|
|
return 1; |
191
|
|
|
|
|
|
|
} |
192
|
|
|
|
|
|
|
|
193
|
0
|
|
|
|
|
|
return 0; |
194
|
|
|
|
|
|
|
} |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
#----------------------------------------# |
197
|
|
|
|
|
|
|
# |
198
|
|
|
|
|
|
|
#----------------------------------------# |
199
|
|
|
|
|
|
|
sub isvalid_month { |
200
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
201
|
0
|
|
|
|
|
|
my $month = shift; |
202
|
|
|
|
|
|
|
|
203
|
0
|
0
|
|
|
|
|
if ($month eq "") { |
204
|
0
|
|
|
|
|
|
return 0; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
0
|
0
|
0
|
|
|
|
if ( ($month >= 1) && ($month <= 12) ) { |
208
|
0
|
|
|
|
|
|
return 1; |
209
|
|
|
|
|
|
|
} |
210
|
|
|
|
|
|
|
|
211
|
0
|
|
|
|
|
|
return 0; |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
#----------------------------------------# |
215
|
|
|
|
|
|
|
# |
216
|
|
|
|
|
|
|
#----------------------------------------# |
217
|
|
|
|
|
|
|
sub isvalid_latitude { |
218
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
219
|
0
|
|
|
|
|
|
my $latitude = shift; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
#print "$latitude\n"; |
222
|
|
|
|
|
|
|
|
223
|
0
|
0
|
|
|
|
|
if (abs($latitude) > 90) { |
224
|
0
|
|
|
|
|
|
return 0 |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
0
|
|
|
|
|
|
return 1; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
#----------------------------------------# |
231
|
|
|
|
|
|
|
# |
232
|
|
|
|
|
|
|
#----------------------------------------# |
233
|
|
|
|
|
|
|
sub isvalid_longitude { |
234
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
235
|
0
|
|
|
|
|
|
my $longitude = shift; |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
#print "$lonitude\n"; |
238
|
|
|
|
|
|
|
|
239
|
0
|
0
|
|
|
|
|
if ($self->{longitude} > 187.5) { |
240
|
0
|
|
|
|
|
|
$self->{longitude} = $self->{longitude} - 360; |
241
|
|
|
|
|
|
|
} |
242
|
0
|
0
|
|
|
|
|
if ($self->{longitude} < -187.5) { |
243
|
0
|
|
|
|
|
|
$self->{longitude} = $self->{longitude} + 360; |
244
|
|
|
|
|
|
|
} |
245
|
0
|
0
|
|
|
|
|
if (abs($self->{longitude}) > 187.5) { |
246
|
0
|
|
|
|
|
|
return 0; |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
0
|
|
|
|
|
|
return 1; |
250
|
|
|
|
|
|
|
} |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
#----------------------------------------# |
253
|
|
|
|
|
|
|
# calculate the data for the months given |
254
|
|
|
|
|
|
|
#----------------------------------------# |
255
|
|
|
|
|
|
|
sub calculate_insolation { |
256
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
257
|
|
|
|
|
|
|
|
258
|
0
|
|
|
|
|
|
$self->{SCRATCH}->{ITZONE} = int($self->{longitude} / 15); # Determine time zone |
259
|
0
|
|
|
|
|
|
$self->ORBPAR(); # Determine orbital parameters |
260
|
|
|
|
|
|
|
|
261
|
0
|
|
|
|
|
|
for (my $MONTH = $self->{_min_month}; $MONTH <= $self->{_max_month}; $MONTH++) { |
262
|
0
|
|
|
|
|
|
my $DATMAX = $self->{CONSTANTS}->{DAYzMO}[($MONTH - 1)]; # array indexed at zero instead of 1 |
263
|
0
|
0
|
0
|
|
|
|
if ( ($MONTH == 2) and ($self->QLEAPY($self->{year})) ) { |
264
|
0
|
|
|
|
|
|
$DATMAX = 29; |
265
|
|
|
|
|
|
|
} |
266
|
0
|
|
|
|
|
|
for (my $JDATE = 1; $JDATE <= $DATMAX; $JDATE++) { |
267
|
0
|
|
|
|
|
|
my $DATE = $JDATE-1 + .5 - $self->{longitude}/360; |
268
|
0
|
|
|
|
|
|
my $DAY = $self->YMDtoD($self->{year}, $MONTH, $DATE); |
269
|
|
|
|
|
|
|
#print "DAY : $DAY\n"; |
270
|
|
|
|
|
|
|
|
271
|
0
|
|
|
|
|
|
my ($SIND, $COSD, $SUNDIS, $SUNLON, $SUNLAT, $EQTIME) = $self->ORBIT($DAY); |
272
|
|
|
|
|
|
|
#print "SIND : $SIND\n"; |
273
|
|
|
|
|
|
|
#print "COSD : $COSD\n"; |
274
|
|
|
|
|
|
|
#print "SUNDIS : $SUNDIS\n"; |
275
|
|
|
|
|
|
|
#print "SUNLON : $SUNLON\n"; |
276
|
|
|
|
|
|
|
#print "SUNLAT : $SUNLAT\n"; |
277
|
|
|
|
|
|
|
#print "EQTIME : $EQTIME\n"; |
278
|
|
|
|
|
|
|
|
279
|
0
|
|
|
|
|
|
my ($COSZT, $COSZS) = $self->COSZIJ($SIND, $COSD); |
280
|
0
|
|
|
|
|
|
my $RSmEzM = ($self->{CONSTANTS}->{REFRAC} + $self->{CONSTANTS}->{RSUND} / $SUNDIS) * $self->{CONSTANTS}->{TWOPI}/360; |
281
|
0
|
|
|
|
|
|
my $DUSK = $self->SUNSET($SIND, $COSD, $RSmEzM); |
282
|
0
|
|
|
|
|
|
my $SRINC = $self->{CONSTANTS}->{EARTH_WATT_M2} * $COSZT / $SUNDIS**2; |
283
|
|
|
|
|
|
|
|
284
|
0
|
|
|
|
|
|
my ($dawn, $dusk); |
285
|
0
|
|
|
|
|
|
my $date = sprintf("%04d-%02d-%02d", $self->{year}, $MONTH, $JDATE); |
286
|
0
|
0
|
|
|
|
|
if ($DUSK >= 999999) { |
|
|
0
|
|
|
|
|
|
287
|
|
|
|
|
|
|
#--------------------# |
288
|
|
|
|
|
|
|
# Daylight at all times at this location on this day |
289
|
|
|
|
|
|
|
#--------------------# |
290
|
|
|
|
|
|
|
|
291
|
0
|
|
|
|
|
|
$dawn = 0; |
292
|
0
|
|
|
|
|
|
$dusk = 0; |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
} elsif ($DUSK <= -999999) { |
295
|
|
|
|
|
|
|
#--------------------# |
296
|
|
|
|
|
|
|
# Nightime at all times at this location on this day |
297
|
|
|
|
|
|
|
#--------------------# |
298
|
|
|
|
|
|
|
|
299
|
0
|
|
|
|
|
|
$dawn = 0; |
300
|
0
|
|
|
|
|
|
$dusk = 0; |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
} else { |
303
|
|
|
|
|
|
|
#--------------------# |
304
|
|
|
|
|
|
|
# Daylight and nightime at this location on this day |
305
|
|
|
|
|
|
|
#--------------------# |
306
|
|
|
|
|
|
|
|
307
|
0
|
|
|
|
|
|
my $DAWN = (-$DUSK-$EQTIME) * 24 / $self->{CONSTANTS}->{TWOPI} + 12 - $self->{longitude} / 15 + $self->{SCRATCH}->{ITZONE}; |
308
|
0
|
|
|
|
|
|
$DUSK = ( $DUSK-$EQTIME) * 24 / $self->{CONSTANTS}->{TWOPI} + 12 - $self->{longitude} / 15 + $self->{SCRATCH}->{ITZONE}; |
309
|
0
|
|
|
|
|
|
my $IDAWNH = int($DAWN); |
310
|
0
|
|
|
|
|
|
my $IDUSKH = int($DUSK); |
311
|
0
|
|
|
|
|
|
my $IDAWNM = int( ($DAWN - $IDAWNH) * 60); |
312
|
0
|
|
|
|
|
|
my $IDUSKM = int( ($DUSK - $IDUSKH) * 60); |
313
|
0
|
|
|
|
|
|
$dawn = sprintf("%02d:%02d", $IDAWNH, $IDAWNM); |
314
|
0
|
|
|
|
|
|
$dusk = sprintf("%02d:%02d", $IDUSKH, $IDUSKM); |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
} |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
# set the data in the object's data collector - that way you can output it the way you'd like xml,csv,txt,etc |
319
|
0
|
|
|
|
|
|
$self->{data}->{'day'}->{$date}->{'data'} = { |
320
|
|
|
|
|
|
|
'dawn' => $dawn, |
321
|
|
|
|
|
|
|
'dusk' => $dusk, |
322
|
|
|
|
|
|
|
'srinc' => sprintf("%.45f", $SRINC), |
323
|
|
|
|
|
|
|
'coszs' => sprintf("%.45f", $COSZS), |
324
|
|
|
|
|
|
|
}; |
325
|
|
|
|
|
|
|
#printf "%10s - %5s - %5s - %.40f - %.40f\n", $date, $dawn, $dusk, $SRINC, $COSZS; |
326
|
|
|
|
|
|
|
} |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
#----------------------------------------# |
331
|
|
|
|
|
|
|
# output in xml |
332
|
|
|
|
|
|
|
#----------------------------------------# |
333
|
|
|
|
|
|
|
sub get_xml { |
334
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
# only require if they use this method |
337
|
0
|
|
|
|
|
|
my $package = 'XML::Simple'; |
338
|
0
|
|
|
|
|
|
eval { |
339
|
0
|
|
|
|
|
|
(my $pkg = $package) =~ s|::|/|g; # require need a path |
340
|
0
|
|
|
|
|
|
require "$pkg.pm"; |
341
|
0
|
|
|
|
|
|
import $package; |
342
|
|
|
|
|
|
|
}; |
343
|
0
|
0
|
|
|
|
|
die $@ if( $@ ); |
344
|
|
|
|
|
|
|
|
345
|
0
|
|
|
|
|
|
my $xs = XML::Simple->new( |
346
|
|
|
|
|
|
|
AttrIndent => 1, |
347
|
|
|
|
|
|
|
RootName => 'Insolation', |
348
|
|
|
|
|
|
|
KeyAttr => [ 'date' => 'name'], |
349
|
|
|
|
|
|
|
NoAttr => 1, |
350
|
|
|
|
|
|
|
); |
351
|
|
|
|
|
|
|
|
352
|
0
|
|
|
|
|
|
my $xml = $xs->XMLout($self->{data}); |
353
|
|
|
|
|
|
|
#print "$xml"; |
354
|
|
|
|
|
|
|
|
355
|
0
|
|
|
|
|
|
return $xml; |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
#----------------------------------------# |
359
|
|
|
|
|
|
|
# output in csv |
360
|
|
|
|
|
|
|
#----------------------------------------# |
361
|
|
|
|
|
|
|
sub get_csv { |
362
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
363
|
0
|
|
|
|
|
|
my $csv; |
364
|
|
|
|
|
|
|
|
365
|
0
|
|
|
|
|
|
foreach my $date (sort keys %{$self->{data}->{day}}) { |
|
0
|
|
|
|
|
|
|
366
|
0
|
|
|
|
|
|
my $dawn = $self->{data}->{day}->{$date}->{data}->{dawn}; |
367
|
0
|
|
|
|
|
|
my $dusk = $self->{data}->{day}->{$date}->{data}->{dusk}; |
368
|
0
|
|
|
|
|
|
my $srinc = $self->{data}->{day}->{$date}->{data}->{srinc}; |
369
|
0
|
|
|
|
|
|
my $coszs = $self->{data}->{day}->{$date}->{data}->{coszs}; |
370
|
0
|
|
|
|
|
|
$csv .= sprintf("%s,%s,%s,%5.2f,%.4f\n", $date, $dawn, $dusk, $srinc, $coszs); |
371
|
|
|
|
|
|
|
} |
372
|
|
|
|
|
|
|
|
373
|
0
|
|
|
|
|
|
return $csv; |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
#----------------------------------------# |
377
|
|
|
|
|
|
|
# get the insolation for a computed value |
378
|
|
|
|
|
|
|
#----------------------------------------# |
379
|
|
|
|
|
|
|
sub get_ym_insolation { |
380
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
381
|
0
|
|
|
|
|
|
my $ym = shift; |
382
|
0
|
|
|
|
|
|
my $year = substr($ym, 0, 4); |
383
|
0
|
|
|
|
|
|
my $month = substr($ym, 5, 2); |
384
|
0
|
|
|
|
|
|
my $total = 0; |
385
|
|
|
|
|
|
|
|
386
|
0
|
0
|
|
|
|
|
if (! defined($self->{data}->{day}->{$ym . '-01'}->{data}->{srinc})) { |
387
|
0
|
|
|
|
|
|
print "front of the month doesn't exist...\n"; |
388
|
0
|
|
|
|
|
|
return 0; |
389
|
|
|
|
|
|
|
} else { |
390
|
|
|
|
|
|
|
#print "front of the month looks good...\n"; |
391
|
|
|
|
|
|
|
} |
392
|
|
|
|
|
|
|
|
393
|
0
|
|
|
|
|
|
my $DATMAX = $self->{CONSTANTS}->{DAYzMO}[($month - 1)]; # array indexed at zero instead of 1 |
394
|
0
|
0
|
0
|
|
|
|
if ( ($month == 2) and ($self->QLEAPY($year)) ) { |
395
|
0
|
|
|
|
|
|
$DATMAX = 29; |
396
|
|
|
|
|
|
|
} |
397
|
0
|
0
|
|
|
|
|
if (! defined($self->{data}->{day}->{$ym . '-' . $DATMAX}->{data}->{srinc})) { |
398
|
0
|
|
|
|
|
|
print "end of the month doesn't exist...\n"; |
399
|
0
|
|
|
|
|
|
return 0; |
400
|
|
|
|
|
|
|
} else { |
401
|
|
|
|
|
|
|
#print "end of the month looks good...\n"; |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
# compute the month total |
405
|
0
|
|
|
|
|
|
for(my $d = 1; $d <= $DATMAX; $d++) { |
406
|
0
|
|
|
|
|
|
$total += $self->{data}->{day}->{$ym . sprintf("-%02d", $d)}->{data}->{srinc}; |
407
|
|
|
|
|
|
|
} |
408
|
|
|
|
|
|
|
|
409
|
0
|
|
|
|
|
|
return $total; |
410
|
|
|
|
|
|
|
} |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
#----------------------------------------# |
413
|
|
|
|
|
|
|
# get the insolation for a computed value |
414
|
|
|
|
|
|
|
#----------------------------------------# |
415
|
|
|
|
|
|
|
sub get_ymd_insolation { |
416
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
417
|
0
|
|
|
|
|
|
my $ymd = shift; |
418
|
|
|
|
|
|
|
|
419
|
0
|
0
|
|
|
|
|
if (! defined($self->{data}->{day}->{$ymd}->{data}->{srinc})) { |
420
|
0
|
|
|
|
|
|
return -1; |
421
|
|
|
|
|
|
|
} |
422
|
|
|
|
|
|
|
|
423
|
0
|
|
|
|
|
|
return $self->{data}->{day}->{$ymd}->{data}->{srinc}; |
424
|
|
|
|
|
|
|
} |
425
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
#----------------------------------------# |
427
|
|
|
|
|
|
|
# ORBPAR calculates the three orbital parameters as a function of |
428
|
|
|
|
|
|
|
# YEAR. The source of these calculations is: Andre L. Berger, |
429
|
|
|
|
|
|
|
# 1978, "Long-Term Variations of Daily Insolation and Quaternary |
430
|
|
|
|
|
|
|
# Climatic Changes", JAS, v.35, p.2362. Also useful is: Andre L. |
431
|
|
|
|
|
|
|
# Berger, May 1978, "A Simple Algorithm to Compute Long Term |
432
|
|
|
|
|
|
|
# Variations of Daily Insolation", published by Institut |
433
|
|
|
|
|
|
|
# D'Astronomie de Geophysique, Universite Catholique de Louvain, |
434
|
|
|
|
|
|
|
# Louvain-la Neuve, No. 18. |
435
|
|
|
|
|
|
|
# |
436
|
|
|
|
|
|
|
# Tables and equations refer to the first reference (JAS). The |
437
|
|
|
|
|
|
|
# corresponding table or equation in the second reference is |
438
|
|
|
|
|
|
|
# enclosed in parentheses. The coefficients used in this |
439
|
|
|
|
|
|
|
# subroutine are slightly more precise than those used in either |
440
|
|
|
|
|
|
|
# of the references. The generated orbital parameters are precise |
441
|
|
|
|
|
|
|
# within plus or minus 1000000 years from present. |
442
|
|
|
|
|
|
|
# |
443
|
|
|
|
|
|
|
# Input: YEAR = years A.D. are positive, B.C. are negative |
444
|
|
|
|
|
|
|
# Output: ECCEN = ECCENtricity of orbital ellipse |
445
|
|
|
|
|
|
|
# OBLIQ = latitude of Tropic of Cancer in radians |
446
|
|
|
|
|
|
|
# OMEGVP = longitude of perihelion = |
447
|
|
|
|
|
|
|
# = spatial angle from vernal equinox to perihelion |
448
|
|
|
|
|
|
|
# in radians with sun as angle vertex |
449
|
|
|
|
|
|
|
#----------------------------------------# |
450
|
|
|
|
|
|
|
sub ORBPAR { |
451
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
# Table 1 (2). Obliquity relative to mean ecliptic of date: OBLIQD |
454
|
0
|
|
|
|
|
|
my @table1 = (-2462.2214466, 31.609974, 251.9025, |
455
|
|
|
|
|
|
|
-857.3232075, 32.620504, 280.8325, |
456
|
|
|
|
|
|
|
-629.3231835, 24.172203, 128.3057, |
457
|
|
|
|
|
|
|
-414.2804924, 31.983787, 292.7252, |
458
|
|
|
|
|
|
|
-311.7632587, 44.828336, 15.3747, |
459
|
|
|
|
|
|
|
308.9408604, 30.973257, 263.7951, |
460
|
|
|
|
|
|
|
-162.5533601, 43.668246, 308.4258, |
461
|
|
|
|
|
|
|
-116.1077911, 32.246691, 240.0099, |
462
|
|
|
|
|
|
|
101.1189923, 30.599444, 222.9725, |
463
|
|
|
|
|
|
|
-67.6856209, 42.681324, 268.7809, |
464
|
|
|
|
|
|
|
24.9079067, 43.836462, 316.7998, |
465
|
|
|
|
|
|
|
22.5811241, 47.439436, 319.6024, |
466
|
|
|
|
|
|
|
-21.1648355, 63.219948, 143.8050, |
467
|
|
|
|
|
|
|
-15.6549876, 64.230478, 172.7351, |
468
|
|
|
|
|
|
|
15.3936813, 1.010530, 28.9300, |
469
|
|
|
|
|
|
|
14.6660938, 7.437771, 123.5968, |
470
|
|
|
|
|
|
|
-11.7273029, 55.782177, 20.2082, |
471
|
|
|
|
|
|
|
10.2742696, .373813, 40.8226, |
472
|
|
|
|
|
|
|
6.4914588, 13.218362, 123.4722, |
473
|
|
|
|
|
|
|
5.8539148, 62.583231, 155.6977, |
474
|
|
|
|
|
|
|
-5.4872205, 63.593761, 184.6277, |
475
|
|
|
|
|
|
|
-5.4290191, 76.438310, 267.2772, |
476
|
|
|
|
|
|
|
5.1609570, 45.815258, 55.0196, |
477
|
|
|
|
|
|
|
5.0786314, 8.448301, 152.5268, |
478
|
|
|
|
|
|
|
-4.0735782, 56.792707, 49.1382, |
479
|
|
|
|
|
|
|
3.7227167, 49.747842, 204.6609, |
480
|
|
|
|
|
|
|
3.3971932, 12.058272, 56.5233, |
481
|
|
|
|
|
|
|
-2.8347004, 75.278220, 200.3284, |
482
|
|
|
|
|
|
|
-2.6550721, 65.241008, 201.6651, |
483
|
|
|
|
|
|
|
-2.5717867, 64.604291, 213.5577, |
484
|
|
|
|
|
|
|
-2.4712188, 1.647247, 17.0374, |
485
|
|
|
|
|
|
|
2.4625410, 7.811584, 164.4194, |
486
|
|
|
|
|
|
|
2.2464112, 12.207832, 94.5422, |
487
|
|
|
|
|
|
|
-2.0755511, 63.856665, 131.9124, |
488
|
|
|
|
|
|
|
-1.9713669, 56.155990, 61.0309, |
489
|
|
|
|
|
|
|
-1.8813061, 77.448840, 296.2073, |
490
|
|
|
|
|
|
|
-1.8468785, 6.801054, 135.4894, |
491
|
|
|
|
|
|
|
1.8186742, 62.209418, 114.8750, |
492
|
|
|
|
|
|
|
1.7601888, 20.656133, 247.0691, |
493
|
|
|
|
|
|
|
-1.5428851, 48.344406, 256.6114, |
494
|
|
|
|
|
|
|
1.4738838, 55.145460, 32.1008, |
495
|
|
|
|
|
|
|
-1.4593669, 69.000539, 143.6804, |
496
|
|
|
|
|
|
|
1.4192259, 11.071350, 16.8784, |
497
|
|
|
|
|
|
|
-1.1818980, 74.291298, 160.6835, |
498
|
|
|
|
|
|
|
1.1756474, 11.047742, 27.5932, |
499
|
|
|
|
|
|
|
-1.1316126, 0.636717, 348.1074, |
500
|
|
|
|
|
|
|
1.0896928, 12.844549, 82.6496 |
501
|
|
|
|
|
|
|
); |
502
|
|
|
|
|
|
|
#print Dumper \@table1; |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
# Table 4 (1). Fundamental elements of the ecliptic: ECCEN sin(pi) |
505
|
0
|
|
|
|
|
|
my @table4 = ( .01860798, 4.207205, 28.620089, |
506
|
|
|
|
|
|
|
.01627522, 7.346091, 193.788772, |
507
|
|
|
|
|
|
|
-.01300660, 17.857263, 308.307024, |
508
|
|
|
|
|
|
|
.00988829, 17.220546, 320.199637, |
509
|
|
|
|
|
|
|
-.00336700, 16.846733, 279.376984, |
510
|
|
|
|
|
|
|
.00333077, 5.199079, 87.195000, |
511
|
|
|
|
|
|
|
-.00235400, 18.231076, 349.129677, |
512
|
|
|
|
|
|
|
.00140015, 26.216758, 128.443387, |
513
|
|
|
|
|
|
|
.00100700, 6.359169, 154.143880, |
514
|
|
|
|
|
|
|
.00085700, 16.210016, 291.269597, |
515
|
|
|
|
|
|
|
.00064990, 3.065181, 114.860583, |
516
|
|
|
|
|
|
|
.00059900, 16.583829, 332.092251, |
517
|
|
|
|
|
|
|
.00037800, 18.493980, 296.414411, |
518
|
|
|
|
|
|
|
-.00033700, 6.190953, 145.769910, |
519
|
|
|
|
|
|
|
.00027600, 18.867793, 337.237063, |
520
|
|
|
|
|
|
|
.00018200, 17.425567, 152.092288, |
521
|
|
|
|
|
|
|
-.00017400, 6.186001, 126.839891, |
522
|
|
|
|
|
|
|
-.00012400, 18.417441, 210.667199, |
523
|
|
|
|
|
|
|
.00001250, 0.667863, 72.108838 |
524
|
|
|
|
|
|
|
); |
525
|
|
|
|
|
|
|
#print Dumper \@table4; |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
# Table 5 (3). General precession in longitude: psi |
528
|
0
|
|
|
|
|
|
my @table5 = ( 7391.0225890, 31.609974, 251.9025, |
529
|
|
|
|
|
|
|
2555.1526947, 32.620504, 280.8325, |
530
|
|
|
|
|
|
|
2022.7629188, 24.172203, 128.3057, |
531
|
|
|
|
|
|
|
-1973.6517951, 0.636717, 348.1074, |
532
|
|
|
|
|
|
|
1240.2321818, 31.983787, 292.7252, |
533
|
|
|
|
|
|
|
953.8679112, 3.138886, 165.1686, |
534
|
|
|
|
|
|
|
-931.7537108, 30.973257, 263.7951, |
535
|
|
|
|
|
|
|
872.3795383, 44.828336, 15.3747, |
536
|
|
|
|
|
|
|
606.3544732, 0.991874, 58.5749, |
537
|
|
|
|
|
|
|
-496.0274038, 0.373813, 40.8226, |
538
|
|
|
|
|
|
|
456.9608039, 43.668246, 308.4258, |
539
|
|
|
|
|
|
|
346.9462320, 32.246691, 240.0099, |
540
|
|
|
|
|
|
|
-305.8412902, 30.599444, 222.9725, |
541
|
|
|
|
|
|
|
249.6173246, 2.147012, 106.5937, |
542
|
|
|
|
|
|
|
-199.1027200, 10.511172, 114.5182, |
543
|
|
|
|
|
|
|
191.0560889, 42.681324, 268.7809, |
544
|
|
|
|
|
|
|
-175.2936572, 13.650058, 279.6869, |
545
|
|
|
|
|
|
|
165.9068833, 0.986922, 39.6448, |
546
|
|
|
|
|
|
|
161.1285917, 9.874455, 126.4108, |
547
|
|
|
|
|
|
|
139.7878093, 13.013341, 291.5795, |
548
|
|
|
|
|
|
|
-133.5228399, 0.262904, 307.2848, |
549
|
|
|
|
|
|
|
117.0673811, 0.004952, 18.9300, |
550
|
|
|
|
|
|
|
104.6907281, 1.142024, 273.7596, |
551
|
|
|
|
|
|
|
95.3227476, 63.219948, 143.8050, |
552
|
|
|
|
|
|
|
86.7824524, 0.205021, 191.8927, |
553
|
|
|
|
|
|
|
86.0857729, 2.151964, 125.5237, |
554
|
|
|
|
|
|
|
70.5893698, 64.230478, 172.7351, |
555
|
|
|
|
|
|
|
-69.9719343, 43.836462, 316.7998, |
556
|
|
|
|
|
|
|
-62.5817473, 47.439436, 319.6024, |
557
|
|
|
|
|
|
|
61.5450059, 1.384343, 69.7526, |
558
|
|
|
|
|
|
|
-57.9364011, 7.437771, 123.5968, |
559
|
|
|
|
|
|
|
57.1899832, 18.829299, 217.6432, |
560
|
|
|
|
|
|
|
-57.0236109, 9.500642, 85.5882, |
561
|
|
|
|
|
|
|
-54.2119253, 0.431696, 156.2147, |
562
|
|
|
|
|
|
|
53.2834147, 1.160090, 66.9489, |
563
|
|
|
|
|
|
|
52.1223575, 55.782177, 20.2082, |
564
|
|
|
|
|
|
|
-49.0059908, 12.639528, 250.7568, |
565
|
|
|
|
|
|
|
-48.3118757, 1.155138, 48.0188, |
566
|
|
|
|
|
|
|
-45.4191685, 0.168216, 8.3739, |
567
|
|
|
|
|
|
|
-42.2357920, 1.647247, 17.0374, |
568
|
|
|
|
|
|
|
-34.7971099, 10.884985, 155.3409, |
569
|
|
|
|
|
|
|
34.4623613, 5.610937, 94.1709, |
570
|
|
|
|
|
|
|
-33.8356643, 12.658184, 221.1120, |
571
|
|
|
|
|
|
|
33.6689362, 1.010530, 28.9300, |
572
|
|
|
|
|
|
|
-31.2521586, 1.983748, 117.1498, |
573
|
|
|
|
|
|
|
-30.8798701, 14.023871, 320.5095, |
574
|
|
|
|
|
|
|
28.4640769, 0.560178, 262.3602, |
575
|
|
|
|
|
|
|
-27.1960802, 1.273434, 336.2148, |
576
|
|
|
|
|
|
|
27.0860736, 12.021467, 233.0046, |
577
|
|
|
|
|
|
|
-26.3437456, 62.583231, 155.6977, |
578
|
|
|
|
|
|
|
24.7253740, 63.593761, 184.6277, |
579
|
|
|
|
|
|
|
24.6732126, 76.438310, 267.2772, |
580
|
|
|
|
|
|
|
24.4272733, 4.280910, 78.9281, |
581
|
|
|
|
|
|
|
24.0127327, 13.218362, 123.4722, |
582
|
|
|
|
|
|
|
21.7150294, 17.818769, 188.7132, |
583
|
|
|
|
|
|
|
-21.5375347, 8.359495, 180.1364, |
584
|
|
|
|
|
|
|
18.1148363, 56.792707, 49.1382, |
585
|
|
|
|
|
|
|
-16.9603104, 8.448301, 152.5268, |
586
|
|
|
|
|
|
|
-16.1765215, 1.978796, 98.2198, |
587
|
|
|
|
|
|
|
15.5567653, 8.863925, 97.4808, |
588
|
|
|
|
|
|
|
15.4846529, 0.186365, 221.5376, |
589
|
|
|
|
|
|
|
15.2150632, 8.996212, 168.2438, |
590
|
|
|
|
|
|
|
14.5047426, 6.771027, 161.1199, |
591
|
|
|
|
|
|
|
-14.3873316, 45.815258, 55.0196, |
592
|
|
|
|
|
|
|
13.1351419, 12.002811, 262.6495, |
593
|
|
|
|
|
|
|
12.8776311, 75.278220, 200.3284, |
594
|
|
|
|
|
|
|
11.9867234, 65.241008, 201.6651, |
595
|
|
|
|
|
|
|
11.9385578, 18.870667, 294.6547, |
596
|
|
|
|
|
|
|
11.7030822, 22.009553, 99.8233, |
597
|
|
|
|
|
|
|
11.6018181, 64.604291, 213.5577, |
598
|
|
|
|
|
|
|
-11.2617293, 11.498094, 154.1631, |
599
|
|
|
|
|
|
|
-10.4664199, 0.578834, 232.7153, |
600
|
|
|
|
|
|
|
10.4333970, 9.237738, 138.3034, |
601
|
|
|
|
|
|
|
-10.2377466, 49.747842, 204.6609, |
602
|
|
|
|
|
|
|
10.1934446, 2.147012, 106.5938, |
603
|
|
|
|
|
|
|
-10.1280191, 1.196895, 250.4676, |
604
|
|
|
|
|
|
|
10.0289441, 2.133898, 332.3345, |
605
|
|
|
|
|
|
|
-10.0034259, 0.173168, 27.3039 |
606
|
|
|
|
|
|
|
); |
607
|
|
|
|
|
|
|
|
608
|
0
|
|
|
|
|
|
my $YM1950 = $self->{year} - 1950; |
609
|
|
|
|
|
|
|
#print "YM1950 : $YM1950\n"; |
610
|
|
|
|
|
|
|
|
611
|
0
|
|
|
|
|
|
my $PIz180 = $self->{CONSTANTS}->{TWOPI} / 360; |
612
|
|
|
|
|
|
|
#print "PIz180 : $PIz180\n"; |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
#--------------------# |
615
|
|
|
|
|
|
|
# Obliquity from Table 1 (2): |
616
|
|
|
|
|
|
|
#--------------------# |
617
|
|
|
|
|
|
|
# OBLIQ = 23.320556 (degrees) Equation 5.5 (15) |
618
|
|
|
|
|
|
|
# OBLIQD = OBLIQ + sum[A cos(ft+delta)] Equation 1 (5) |
619
|
0
|
|
|
|
|
|
my $sumc = 0; |
620
|
0
|
|
|
|
|
|
for (my $i = 0; $i < 47; $i++) { |
621
|
|
|
|
|
|
|
#printf "%2d => 1=%18.8f 2=%18.8f 3=%18.8f\n", $i, $table1[(($i*3)+0)], $table1[(($i*3)+1)], $table1[(($i*3)+2)]; |
622
|
0
|
|
|
|
|
|
my $arg = $PIz180 * ($YM1950 * $table1[(($i*3)+1)] / 3600 + $table1[(($i*3)+2)]); |
623
|
0
|
|
|
|
|
|
$sumc = $sumc + $table1[(($i*3)+0)] * cos($arg); |
624
|
|
|
|
|
|
|
#print "$arg - $sumc\n"; |
625
|
|
|
|
|
|
|
} |
626
|
0
|
|
|
|
|
|
my $OBLIQD = 23.320556 + $sumc/3600; |
627
|
|
|
|
|
|
|
#print "OBLIQD : $OBLIQD\n"; |
628
|
0
|
|
|
|
|
|
$self->{SCRATCH}->{OBLIQ} = $OBLIQD * $PIz180; |
629
|
|
|
|
|
|
|
#print "OBLIQ : " . $self->{SCRATCH}->{OBLIQ} . "\n"; |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
#--------------------# |
632
|
|
|
|
|
|
|
# Eccentricity from Table 4 (1): |
633
|
|
|
|
|
|
|
#--------------------# |
634
|
|
|
|
|
|
|
# ECCEN sin(pi) = sum[M sin(gt+beta)] Equation 4 (1) |
635
|
|
|
|
|
|
|
# ECCEN cos(pi) = sum[M cos(gt+beta)] Equation 4 (1) |
636
|
|
|
|
|
|
|
# ECCEN = ECCEN sqrt[sin(pi)^2 + cos(pi)^2] |
637
|
0
|
|
|
|
|
|
my $ESINPI = 0; |
638
|
0
|
|
|
|
|
|
my $ECOSPI = 0; |
639
|
0
|
|
|
|
|
|
for (my $i = 0; $i < 19; $i++) { |
640
|
|
|
|
|
|
|
#printf "%2d => 1=%18.8f 2=%18.8f 3=%18.8f\n", $i, $table4[(($i*3)+0)], $table4[(($i*3)+1)], $table4[(($i*3)+2)]; |
641
|
0
|
|
|
|
|
|
my $arg = $PIz180 * ($YM1950 * $table4[(($i*3)+1)] / 3600 + $table4[(($i*3)+2)]); |
642
|
0
|
|
|
|
|
|
$ESINPI = $ESINPI + $table4[(($i*3)+0)] * sin($arg); |
643
|
0
|
|
|
|
|
|
$ECOSPI = $ECOSPI + $table4[(($i*3)+0)] * cos($arg); |
644
|
|
|
|
|
|
|
#print "ESINPI : $ESINPI\n"; |
645
|
|
|
|
|
|
|
#print "ECOSPI : $ECOSPI\n"; |
646
|
|
|
|
|
|
|
} |
647
|
0
|
|
|
|
|
|
$self->{SCRATCH}->{ECCEN} = sqrt(($ESINPI * $ESINPI) + ($ECOSPI * $ECOSPI)); |
648
|
|
|
|
|
|
|
#print "ECCEN : " . $self->{SCRATCH}->{ECCEN} . "\n"; |
649
|
|
|
|
|
|
|
|
650
|
|
|
|
|
|
|
#--------------------# |
651
|
|
|
|
|
|
|
# Perihelion from Equation 4,6,7 (9) and Table 4,5 (1,3): |
652
|
|
|
|
|
|
|
#--------------------# |
653
|
|
|
|
|
|
|
# PSI# = 50.439273 (seconds of degree) Equation 7.5 (16) |
654
|
|
|
|
|
|
|
# ZETA = 3.392506 (degrees) Equation 7.5 (17) |
655
|
|
|
|
|
|
|
# PSI = PSI# t + ZETA + sum[F sin(ft+delta)] Equation 7 (9) |
656
|
|
|
|
|
|
|
# PIE = atan[ECCEN sin(pi) / ECCEN cos(pi)] |
657
|
|
|
|
|
|
|
# OMEGVP = PIE + PSI + 3.14159 Equation 6 (4.5) |
658
|
|
|
|
|
|
|
|
659
|
0
|
|
|
|
|
|
my $PIE = atan2($ESINPI, $ECOSPI); |
660
|
|
|
|
|
|
|
#print "PIE : $PIE\n"; |
661
|
0
|
|
|
|
|
|
my $FSINFD = 0; |
662
|
0
|
|
|
|
|
|
for(my $i = 0; $i < 78; $i++) { |
663
|
|
|
|
|
|
|
#printf "%2d => 1=%18.8f 2=%18.8f 3=%18.8f\n", $i, $table5[(($i*3)+0)], $table5[(($i*3)+1)], $table5[(($i*3)+2)]; |
664
|
0
|
|
|
|
|
|
my $arg = $PIz180 * ($YM1950 * $table5[(($i*3)+1)] / 3600 + $table5[(($i*3)+2)]); |
665
|
0
|
|
|
|
|
|
$FSINFD = $FSINFD + $table5[(($i*3)+0)] * sin($arg); |
666
|
|
|
|
|
|
|
} |
667
|
|
|
|
|
|
|
#print "FSINFD : $FSINFD\n"; |
668
|
0
|
|
|
|
|
|
my $PSI = $PIz180 * (3.392506 + ($YM1950 * 50.439273 + $FSINFD) / 3600); |
669
|
|
|
|
|
|
|
#print "PSI : $PSI\n"; |
670
|
0
|
|
|
|
|
|
my $a = ($PIE + $PSI + .5 * $self->{CONSTANTS}->{TWOPI}) ; |
671
|
0
|
|
|
|
|
|
my $b = $self->{CONSTANTS}->{TWOPI}; |
672
|
0
|
|
|
|
|
|
my $c = $a / $b; |
673
|
0
|
|
|
|
|
|
my $d = modulo($a, $b); |
674
|
|
|
|
|
|
|
#printf "%25.20f\n", $a; |
675
|
|
|
|
|
|
|
#printf "%25.20f\n", $b; |
676
|
|
|
|
|
|
|
#printf "%25.20f\n", $c; |
677
|
|
|
|
|
|
|
#printf "%25.20f\n", $d; |
678
|
0
|
|
|
|
|
|
$self->{SCRATCH}->{OMEGVP} = modulo(($PIE + $PSI + .5 * $self->{CONSTANTS}->{TWOPI}), $self->{CONSTANTS}->{TWOPI}); |
679
|
|
|
|
|
|
|
#print "OMEGVP : " . $self->{SCRATCH}->{OMEGVP} . "\n"; |
680
|
|
|
|
|
|
|
} |
681
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
#----------------------------------------# |
683
|
|
|
|
|
|
|
# ORBIT receives orbital parameters and time of year, and returns |
684
|
|
|
|
|
|
|
# distance from Sun and Sun's position. |
685
|
|
|
|
|
|
|
# Reference for following caculations is: V.M.Blanco and |
686
|
|
|
|
|
|
|
# S.W.McCuskey, 1961, "Basic Physics of the Solar System", pages |
687
|
|
|
|
|
|
|
# 135 - 151. Existence of Moon and heavenly bodies other than |
688
|
|
|
|
|
|
|
# Earth and Sun are ignored. Earth is assumed to be spherical. |
689
|
|
|
|
|
|
|
# |
690
|
|
|
|
|
|
|
# Program author: Gary L. Russell 2002/09/25 |
691
|
|
|
|
|
|
|
# Angles, longitude and latitude are measured in radians. |
692
|
|
|
|
|
|
|
# |
693
|
|
|
|
|
|
|
# Input: ECCEN = ECCENtricity of the orbital ellipse |
694
|
|
|
|
|
|
|
# OBLIQ = latitude of Tropic of Cancer |
695
|
|
|
|
|
|
|
# OMEGVP = longitude of perihelion (sometimes Pi is added) = |
696
|
|
|
|
|
|
|
# = spatial angle from vernal equinox to perihelion |
697
|
|
|
|
|
|
|
# with Sun as angle vertex |
698
|
|
|
|
|
|
|
# DAY = days measured since 2000 January 1, hour 0 |
699
|
|
|
|
|
|
|
# |
700
|
|
|
|
|
|
|
# Constants: EDAYzY = tropical year = Earth days per year = 365.2425 |
701
|
|
|
|
|
|
|
# VE200D = days from 2000 January 1, hour 0 till vernal |
702
|
|
|
|
|
|
|
# equinox of year 2000 = 31 + 29 + 19 + 7.5/24 |
703
|
|
|
|
|
|
|
# |
704
|
|
|
|
|
|
|
# Intermediate quantities: |
705
|
|
|
|
|
|
|
# BSEMI = semi minor axis in units of semi major axis |
706
|
|
|
|
|
|
|
# PERIHE = perihelion in days since 2000 January 1, hour 0 |
707
|
|
|
|
|
|
|
# in its annual revolution about Sun |
708
|
|
|
|
|
|
|
# TA = true anomaly = spatial angle from perihelion to |
709
|
|
|
|
|
|
|
# current location with Sun as angle vertex |
710
|
|
|
|
|
|
|
# EA = ECCENtric anomaly = spatial angle measured along |
711
|
|
|
|
|
|
|
# ECCENtric circle (that circumscribes Earth's orbit) |
712
|
|
|
|
|
|
|
# from perihelion to point above (or below) Earth's |
713
|
|
|
|
|
|
|
# absisca (where absisca is directed from center of |
714
|
|
|
|
|
|
|
# ECCENtric circle to perihelion) |
715
|
|
|
|
|
|
|
# MA = mean anomaly = temporal angle from perihelion to |
716
|
|
|
|
|
|
|
# current time in units of 2*Pi per tropical year |
717
|
|
|
|
|
|
|
# TAofVE = TA(VE) = true anomaly of vernal equinox = - OMEGVP |
718
|
|
|
|
|
|
|
# EAofVE = EA(VE) = ECCENtric anomaly of vernal equinox |
719
|
|
|
|
|
|
|
# MAofVE = MA(VE) = mean anomaly of vernal equinox |
720
|
|
|
|
|
|
|
# SLNORO = longitude of Sun in Earth's nonrotating reference frame |
721
|
|
|
|
|
|
|
# VEQLON = longitude of Greenwich Meridion in Earth's nonrotating |
722
|
|
|
|
|
|
|
# reference frame at vernal equinox |
723
|
|
|
|
|
|
|
# ROTATE = change in longitude in Earth's nonrotating reference |
724
|
|
|
|
|
|
|
# frame from point's location on vernal equinox to its |
725
|
|
|
|
|
|
|
# current location where point is fixed on rotating Earth |
726
|
|
|
|
|
|
|
# SLMEAN = longitude of fictitious mean Sun in Earth's rotating |
727
|
|
|
|
|
|
|
# reference frame (normal longitude and latitude) |
728
|
|
|
|
|
|
|
# |
729
|
|
|
|
|
|
|
# Output: DIST = distance to Sun in units of semi major axis |
730
|
|
|
|
|
|
|
# SIND = sine of declination angle = sin(SUNLAT) |
731
|
|
|
|
|
|
|
# COSD = cosine of the declination angle = cos(SUNLAT) |
732
|
|
|
|
|
|
|
# SUNLON = longitude of point on Earth directly beneath Sun |
733
|
|
|
|
|
|
|
# SUNLAT = latitude of point on Earth directly beneath Sun |
734
|
|
|
|
|
|
|
# EQTIME = Equation of Time = longitude of fictitious mean Sun minus SUNLON |
735
|
|
|
|
|
|
|
# |
736
|
|
|
|
|
|
|
# From the above reference: |
737
|
|
|
|
|
|
|
# (4-54): [1 - ECCEN*cos(EA)]*[1 + ECCEN*cos(TA)] = (1 - ECCEN^2) |
738
|
|
|
|
|
|
|
# (4-55): tan(TA/2) = sqrt[(1+ECCEN)/(1-ECCEN)]*tan(EA/2) |
739
|
|
|
|
|
|
|
# Yield: tan(EA) = sin(TA)*sqrt(1-ECCEN^2) / [cos(TA) + ECCEN] |
740
|
|
|
|
|
|
|
# or: tan(TA) = sin(EA)*sqrt(1-ECCEN^2) / [cos(EA) - ECCEN] |
741
|
|
|
|
|
|
|
# |
742
|
|
|
|
|
|
|
# Use C540, Only: EDAYzY,VE200D,CONSTANTS}->{TWOPI |
743
|
|
|
|
|
|
|
# Implicit Real*8 (A-H,M-Z) |
744
|
|
|
|
|
|
|
#----------------------------------------# |
745
|
|
|
|
|
|
|
sub ORBIT { |
746
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
747
|
0
|
|
|
|
|
|
my $DAY = shift; |
748
|
|
|
|
|
|
|
|
749
|
0
|
|
|
|
|
|
my $EDAYzY = 365.2425; |
750
|
0
|
|
|
|
|
|
my $VE200D = 79.3125; |
751
|
|
|
|
|
|
|
|
752
|
|
|
|
|
|
|
# Determine EAofVE from geometry: tan(EA) = b*sin(TA) / [e+cos(TA)] |
753
|
|
|
|
|
|
|
# Determine MAofVE from Kepler's equation: MA = EA - e*sin(EA) |
754
|
|
|
|
|
|
|
# Determine MA knowing time from vernal equinox to current day |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
#printf "DAY : %.10f\n", $DAY; |
757
|
|
|
|
|
|
|
#printf "ECCEN : %.10f\n", $self->{SCRATCH}->{ECCEN}; |
758
|
|
|
|
|
|
|
#printf "OMEGVP : %.10f\n", $self->{SCRATCH}->{OMEGVP}; |
759
|
|
|
|
|
|
|
|
760
|
0
|
|
|
|
|
|
my $BSEMI = sqrt(1 - ($self->{SCRATCH}->{ECCEN} * $self->{SCRATCH}->{ECCEN})); |
761
|
|
|
|
|
|
|
#printf "BSEMI : %.10f\n", $BSEMI; |
762
|
|
|
|
|
|
|
|
763
|
0
|
|
|
|
|
|
my $TAofVE = -($self->{SCRATCH}->{OMEGVP}); |
764
|
|
|
|
|
|
|
#printf "TAofVE : %.10f\n", $TAofVE; |
765
|
|
|
|
|
|
|
|
766
|
0
|
|
|
|
|
|
my $EAofVE = atan2( ($BSEMI * sin($TAofVE)), ($self->{SCRATCH}->{ECCEN} + cos($TAofVE)) ); |
767
|
|
|
|
|
|
|
#printf "EAofVE : %.10f\n", $EAofVE; |
768
|
|
|
|
|
|
|
|
769
|
0
|
|
|
|
|
|
my $MAofVE = $EAofVE - $self->{SCRATCH}->{ECCEN} * sin($EAofVE); |
770
|
|
|
|
|
|
|
#printf "MAofVE : %.10f\n", $MAofVE; |
771
|
|
|
|
|
|
|
|
772
|
0
|
|
|
|
|
|
my $MA = modulo(($self->{CONSTANTS}->{TWOPI} * ($DAY - $VE200D) / $EDAYzY + $MAofVE), $self->{CONSTANTS}->{TWOPI}); |
773
|
|
|
|
|
|
|
#printf "MA : $MA\n"; |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
# Numerically invert Kepler's equation: MA = EA - e*sin(EA) |
776
|
0
|
|
|
|
|
|
my $dEA = 1; |
777
|
0
|
|
|
|
|
|
my $i = 0; |
778
|
0
|
|
|
|
|
|
my $EA = $MA + ($self->{SCRATCH}->{ECCEN} * ( sin($MA)) + ($self->{SCRATCH}->{ECCEN} * sin( 2 * $MA ) / 2) ); |
779
|
0
|
|
|
|
|
|
while (abs($dEA) > 0.0000000001){ |
780
|
0
|
|
|
|
|
|
$dEA = ($MA - $EA + $self->{SCRATCH}->{ECCEN} * sin($EA)) / (1 - $self->{SCRATCH}->{ECCEN} * cos($EA)); |
781
|
0
|
|
|
|
|
|
$EA += $dEA; |
782
|
|
|
|
|
|
|
#printf "computing $i times ($EA, $dEA - %25.20f)\n", (abs($dEA)); |
783
|
0
|
|
|
|
|
|
$i++; |
784
|
|
|
|
|
|
|
} |
785
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
# |
787
|
|
|
|
|
|
|
# Calculate distance to Sun and true anomaly |
788
|
|
|
|
|
|
|
# |
789
|
0
|
|
|
|
|
|
my $SUNDIS = 1 - $self->{SCRATCH}->{ECCEN} * cos($EA); |
790
|
0
|
|
|
|
|
|
my $TA = atan2( ($BSEMI * sin($EA)), (cos($EA) - $self->{SCRATCH}->{ECCEN})); |
791
|
|
|
|
|
|
|
|
792
|
|
|
|
|
|
|
# |
793
|
|
|
|
|
|
|
# Change reference frame to be nonrotating reference frame, angles |
794
|
|
|
|
|
|
|
# fixed according to stars, with Earth at center and positive x |
795
|
|
|
|
|
|
|
# axis be ray from Earth to Sun were Earth at vernal equinox, and |
796
|
|
|
|
|
|
|
# x-y plane be Earth's equatorial plane. Distance from current Sun |
797
|
|
|
|
|
|
|
# to this x axis is SUNDIS sin(TA-TAofVE). At vernal equinox, Sun |
798
|
|
|
|
|
|
|
# is located at (SUNDIS,0,0). At other times, Sun is located at: |
799
|
|
|
|
|
|
|
# |
800
|
|
|
|
|
|
|
# SUN = (SUNDIS cos(TA-TAofVE), |
801
|
|
|
|
|
|
|
# SUNDIS sin(TA-TAofVE) cos(OBLIQ), |
802
|
|
|
|
|
|
|
# SUNDIS sin(TA-TAofVE) sin(OBLIQ)) |
803
|
|
|
|
|
|
|
# |
804
|
0
|
|
|
|
|
|
my $SIND = sin($TA - $TAofVE) * sin($self->{SCRATCH}->{OBLIQ}); |
805
|
0
|
|
|
|
|
|
my $COSD = sqrt(1 - ($SIND * $SIND)); |
806
|
0
|
|
|
|
|
|
my $SUNX = cos($TA - $TAofVE); |
807
|
0
|
|
|
|
|
|
my $SUNY = sin($TA - $TAofVE) * cos($self->{SCRATCH}->{OBLIQ}); |
808
|
0
|
|
|
|
|
|
my $SLNORO = atan2($SUNY, $SUNX); |
809
|
|
|
|
|
|
|
|
810
|
|
|
|
|
|
|
# |
811
|
|
|
|
|
|
|
# Determine Sun location in Earth's rotating reference frame |
812
|
|
|
|
|
|
|
# (normal longitude and latitude) |
813
|
|
|
|
|
|
|
# |
814
|
0
|
|
|
|
|
|
my $VEQLON = $self->{CONSTANTS}->{TWOPI} * $VE200D - $self->{CONSTANTS}->{TWOPI} / 2 + $MAofVE - $TAofVE; # modulo 2*Pi |
815
|
0
|
|
|
|
|
|
my $ROTATE = $self->{CONSTANTS}->{TWOPI} * ($DAY - $VE200D) * ($EDAYzY + 1) / $EDAYzY; |
816
|
0
|
|
|
|
|
|
my $SUNLON = modulo(($SLNORO - $ROTATE - $VEQLON), $self->{CONSTANTS}->{TWOPI}); |
817
|
0
|
0
|
|
|
|
|
if ($SUNLON > ($self->{CONSTANTS}->{TWOPI} / 2)) { |
818
|
0
|
|
|
|
|
|
$SUNLON = $SUNLON - $self->{CONSTANTS}->{TWOPI}; |
819
|
|
|
|
|
|
|
} |
820
|
0
|
|
|
|
|
|
my $SUNLAT = asin(sin($TA - $TAofVE) * sin($self->{SCRATCH}->{OBLIQ})); |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
# |
823
|
|
|
|
|
|
|
# Determine longitude of fictitious mean Sun |
824
|
|
|
|
|
|
|
# Calculate Equation of Time |
825
|
|
|
|
|
|
|
# |
826
|
0
|
|
|
|
|
|
my $SLMEAN = $self->{CONSTANTS}->{TWOPI} / 2 - $self->{CONSTANTS}->{TWOPI} * ($DAY - int($DAY)); |
827
|
0
|
|
|
|
|
|
my $EQTIME = modulo($SLMEAN - $SUNLON, $self->{CONSTANTS}->{TWOPI}); |
828
|
0
|
0
|
|
|
|
|
if ($EQTIME > $self->{CONSTANTS}->{TWOPI}/2) { |
829
|
0
|
|
|
|
|
|
$EQTIME = $EQTIME - $self->{CONSTANTS}->{TWOPI}; |
830
|
|
|
|
|
|
|
} |
831
|
|
|
|
|
|
|
|
832
|
0
|
|
|
|
|
|
return ($SIND, $COSD, $SUNDIS, $SUNLON, $SUNLAT, $EQTIME); |
833
|
|
|
|
|
|
|
} |
834
|
|
|
|
|
|
|
|
835
|
|
|
|
|
|
|
#----------------------------------------# |
836
|
|
|
|
|
|
|
# SUNSET |
837
|
|
|
|
|
|
|
# Input: RLAT = latitude (degrees) |
838
|
|
|
|
|
|
|
# SIND,COSD = sine and cosine of the declination angle |
839
|
|
|
|
|
|
|
# RSmEzM = (Sun Radius - Earth Radius) / (distance to Sun) |
840
|
|
|
|
|
|
|
# |
841
|
|
|
|
|
|
|
# Output: DUSK = time of DUSK (temporal radians) at mean local time |
842
|
|
|
|
|
|
|
# |
843
|
|
|
|
|
|
|
#----------------------------------------# |
844
|
|
|
|
|
|
|
sub SUNSET { |
845
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
846
|
0
|
|
|
|
|
|
my $RLAT = $self->{latitude};; |
847
|
0
|
|
|
|
|
|
my $SIND = shift; |
848
|
0
|
|
|
|
|
|
my $COSD = shift; |
849
|
0
|
|
|
|
|
|
my $RSmEzM = shift; |
850
|
|
|
|
|
|
|
|
851
|
0
|
|
|
|
|
|
my $DUSK = 0; |
852
|
|
|
|
|
|
|
|
853
|
0
|
|
|
|
|
|
my $SINJ = sin( $self->{CONSTANTS}->{TWOPI} * $self->{latitude} / 360); |
854
|
0
|
|
|
|
|
|
my $COSJ = cos( $self->{CONSTANTS}->{TWOPI} * $self->{latitude} / 360); |
855
|
0
|
|
|
|
|
|
my $SJSD = $SINJ * $SIND; |
856
|
0
|
|
|
|
|
|
my $CJCD = $COSJ * $COSD; |
857
|
|
|
|
|
|
|
|
858
|
|
|
|
|
|
|
# Constant nightime at this latitude |
859
|
0
|
0
|
|
|
|
|
if (($SJSD + $RSmEzM + $CJCD) <= 0) { |
860
|
0
|
|
|
|
|
|
$DUSK = -999999; |
861
|
0
|
|
|
|
|
|
return $DUSK; |
862
|
|
|
|
|
|
|
} |
863
|
|
|
|
|
|
|
|
864
|
|
|
|
|
|
|
# Constant daylight at this latitude |
865
|
0
|
0
|
|
|
|
|
if (($SJSD + $RSmEzM - $CJCD) >= 0) { |
866
|
0
|
|
|
|
|
|
$DUSK = 999999; |
867
|
0
|
|
|
|
|
|
return $DUSK; |
868
|
|
|
|
|
|
|
} |
869
|
|
|
|
|
|
|
|
870
|
|
|
|
|
|
|
# Compute DUSK (at local time) |
871
|
0
|
|
|
|
|
|
my $CDUSK = -($SJSD + $RSmEzM) / $CJCD; # cosine of DUSK |
872
|
0
|
|
|
|
|
|
$DUSK = acos($CDUSK); |
873
|
|
|
|
|
|
|
|
874
|
0
|
|
|
|
|
|
return $DUSK; |
875
|
|
|
|
|
|
|
} |
876
|
|
|
|
|
|
|
|
877
|
|
|
|
|
|
|
#----------------------------------------# |
878
|
|
|
|
|
|
|
# For a given JYEAR (A.D.), JMONTH and DATE (between 0 and 31), |
879
|
|
|
|
|
|
|
# calculate number of DAYs measured from 2000 January 1, hour 0. |
880
|
|
|
|
|
|
|
#----------------------------------------# |
881
|
|
|
|
|
|
|
sub YMDtoD { |
882
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
883
|
0
|
|
|
|
|
|
my $JYEAR = shift; |
884
|
0
|
|
|
|
|
|
my $JMONTH = shift; |
885
|
0
|
|
|
|
|
|
my $DATE = shift; |
886
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
#print "year : $JYEAR\n"; |
888
|
|
|
|
|
|
|
#print "month : $JMONTH\n"; |
889
|
|
|
|
|
|
|
#print "date : $DATE\n"; |
890
|
|
|
|
|
|
|
|
891
|
0
|
|
|
|
|
|
my $JDAY4C = 365 * 400 + 97; # number of days in 4 centuries |
892
|
0
|
|
|
|
|
|
my $JDAY1C = 365 * 100 + 24; # number of days in 1 century |
893
|
0
|
|
|
|
|
|
my $JDAY4Y = 365 * 4 + 1; # number of days in 4 years |
894
|
0
|
|
|
|
|
|
my $JDAY1Y = 365; # number of days in 1 year |
895
|
|
|
|
|
|
|
|
896
|
0
|
|
|
|
|
|
my @JDSUMN = (0,31,59, 90,120,151, 181,212,243, 273,304,334); |
897
|
0
|
|
|
|
|
|
my @JDSUML = (0,31,60, 91,121,152, 182,213,244, 274,305,335); |
898
|
|
|
|
|
|
|
|
899
|
0
|
|
|
|
|
|
my $N4CENT = int( ($JYEAR-2000) / 400); |
900
|
0
|
|
|
|
|
|
my $IYR4C = $JYEAR - 2000 - ($N4CENT * 400); |
901
|
0
|
|
|
|
|
|
my $N1CENT = int($IYR4C / 100); |
902
|
0
|
|
|
|
|
|
my $IYR1C = $IYR4C - $N1CENT * 100; |
903
|
0
|
|
|
|
|
|
my $N4YEAR = int($IYR1C / 4); |
904
|
0
|
|
|
|
|
|
my $IYR4Y = $IYR1C - $N4YEAR * 4; |
905
|
0
|
|
|
|
|
|
my $N1YEAR = $IYR4Y; |
906
|
0
|
|
|
|
|
|
my $DAY = $N4CENT * $JDAY4C; |
907
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
#print "N4CENT : $N4CENT\n"; |
909
|
|
|
|
|
|
|
#print "IYR4C : $IYR4C\n"; |
910
|
|
|
|
|
|
|
#print "N1CENT : $N1CENT\n"; |
911
|
|
|
|
|
|
|
#print "IYR1C : $IYR1C\n"; |
912
|
|
|
|
|
|
|
#print "N4YEAR : $N4YEAR\n"; |
913
|
|
|
|
|
|
|
#print "IYR4Y : $IYR4Y\n"; |
914
|
|
|
|
|
|
|
#print "N1YEAR : $N1YEAR\n"; |
915
|
|
|
|
|
|
|
#print "DAY : $DAY\n"; |
916
|
|
|
|
|
|
|
|
917
|
0
|
0
|
|
|
|
|
if ($N1CENT > 0) { |
918
|
0
|
|
|
|
|
|
$DAY = $DAY + $JDAY1C + 1 + ($N1CENT - 1) * $JDAY1C; |
919
|
|
|
|
|
|
|
#print "1. $DAY\n"; |
920
|
0
|
0
|
|
|
|
|
if ($N4YEAR > 0) { |
921
|
|
|
|
|
|
|
#print "2. $DAY\n"; |
922
|
0
|
|
|
|
|
|
$DAY = $DAY + $JDAY4Y-1 + ($N4YEAR - 1) * $JDAY4Y; |
923
|
0
|
0
|
|
|
|
|
if ($N1YEAR > 0) { |
924
|
|
|
|
|
|
|
#print "3. $DAY\n"; |
925
|
0
|
|
|
|
|
|
$DAY = $DAY + $JDAY1Y+1 + ($N1YEAR - 1) * $JDAY1Y; |
926
|
|
|
|
|
|
|
#print "4. $DAY\n"; |
927
|
0
|
|
|
|
|
|
$DAY = $DAY + $JDSUMN[($JMONTH - 1)] + $DATE; |
928
|
|
|
|
|
|
|
#print "5. $DAY\n"; |
929
|
0
|
|
|
|
|
|
return $DAY; |
930
|
|
|
|
|
|
|
} else { |
931
|
0
|
|
|
|
|
|
$DAY = $DAY + $JDSUML[($JMONTH - 1)] + $DATE; |
932
|
|
|
|
|
|
|
#print "6. $DAY\n"; |
933
|
0
|
|
|
|
|
|
return $DAY; |
934
|
|
|
|
|
|
|
} |
935
|
|
|
|
|
|
|
} else { |
936
|
|
|
|
|
|
|
#print "day : -> $DAY\n"; |
937
|
|
|
|
|
|
|
#print "month : -> $JMONTH\n"; |
938
|
|
|
|
|
|
|
#print "date : -> $DATE\n"; |
939
|
|
|
|
|
|
|
#print "index : -> " . $JDSUML[$JMONTH] . "\n"; |
940
|
0
|
|
|
|
|
|
$DAY = $DAY + $JDSUML[($JMONTH - 1)] + $DATE; |
941
|
|
|
|
|
|
|
#print "7. - $DAY\n"; |
942
|
0
|
|
|
|
|
|
return $DAY; |
943
|
|
|
|
|
|
|
} |
944
|
|
|
|
|
|
|
} else { |
945
|
|
|
|
|
|
|
|
946
|
0
|
|
|
|
|
|
$DAY = $DAY + ($N4YEAR * $JDAY4Y); |
947
|
|
|
|
|
|
|
#print "8. $DAY\n"; |
948
|
|
|
|
|
|
|
|
949
|
0
|
0
|
|
|
|
|
if ($N1YEAR > 0) { |
950
|
0
|
|
|
|
|
|
$DAY = $DAY + ($JDAY1Y + 1) + (($N1YEAR - 1) * $JDAY1Y); |
951
|
|
|
|
|
|
|
#print "9. $DAY\n"; |
952
|
|
|
|
|
|
|
|
953
|
|
|
|
|
|
|
#print "JMONTH " . $JMONTH . "\n"; |
954
|
|
|
|
|
|
|
#print "JDSUMN " . $JDSUMN[($JMONTH - 1)] . "\n"; |
955
|
|
|
|
|
|
|
|
956
|
0
|
|
|
|
|
|
$DAY = $DAY + $JDSUMN[($JMONTH - 1)] + $DATE; |
957
|
|
|
|
|
|
|
#print "10. $DAY\n"; |
958
|
|
|
|
|
|
|
|
959
|
0
|
|
|
|
|
|
return $DAY; |
960
|
|
|
|
|
|
|
} else { |
961
|
0
|
|
|
|
|
|
$DAY = $DAY + $JDSUML[($JMONTH - 1)] + $DATE; |
962
|
|
|
|
|
|
|
#print "11. $DAY\n"; |
963
|
0
|
|
|
|
|
|
return $DAY; |
964
|
|
|
|
|
|
|
} |
965
|
|
|
|
|
|
|
} |
966
|
|
|
|
|
|
|
} |
967
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
#----------------------------------------# |
969
|
|
|
|
|
|
|
# For a given DAY measured from 2000 January 1, hour 0, determine |
970
|
|
|
|
|
|
|
# the JYEAR (A.D.), JMONTH and DATE (between 0 and 31). |
971
|
|
|
|
|
|
|
#----------------------------------------# |
972
|
|
|
|
|
|
|
sub DtoYMD { |
973
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
974
|
0
|
|
|
|
|
|
my $DAY = shift; |
975
|
|
|
|
|
|
|
|
976
|
0
|
|
|
|
|
|
my $JDAY4C = 365*400 + 97; # number of days in 4 centuries |
977
|
0
|
|
|
|
|
|
my $JDAY1C = 365*100 + 24; # number of days in 1 century |
978
|
0
|
|
|
|
|
|
my $JDAY4Y = 365* 4 + 1, # number of days in 4 years |
979
|
|
|
|
|
|
|
my $JDAY1Y = 365; # number of days in 1 year |
980
|
|
|
|
|
|
|
|
981
|
0
|
|
|
|
|
|
my @JDSUMN = (0,31,59, 90,120,151, 181,212,243, 273,304,334); |
982
|
0
|
|
|
|
|
|
my @JDSUML = (0,31,60, 91,121,152, 182,213,244, 274,305,335); |
983
|
|
|
|
|
|
|
|
984
|
|
|
|
|
|
|
#my $N4CENT = int($DAY / $JDAY4C); |
985
|
|
|
|
|
|
|
#my $DAY4C = $DAY - $N4CENT * $JDAY4C; |
986
|
|
|
|
|
|
|
#my $N1CENT = ($DAY4C - 1) / $JDAY1C; |
987
|
|
|
|
|
|
|
|
988
|
|
|
|
|
|
|
# Second to fourth of every fourth century: 21??, 22??, 23??, etc. |
989
|
|
|
|
|
|
|
#if ($N1CENT > 0) { |
990
|
|
|
|
|
|
|
# $DAY1C = $DAY4C - $N1CENT * $JDAY1C - 1; |
991
|
|
|
|
|
|
|
# $N4YEAR = ($DAY1C + 1) / $JDAY4Y; |
992
|
|
|
|
|
|
|
|
993
|
|
|
|
|
|
|
# if ($N4YEAR > 0) { |
994
|
|
|
|
|
|
|
# # Subsequent four years of every second to fourth century when |
995
|
|
|
|
|
|
|
# # there is a leap year: 2104-2107, 2108-2111 ... 2204-2207, etc. |
996
|
|
|
|
|
|
|
# $DAY4Y = $DAY1C - $N4YEAR * $JDAY4Y + 1; |
997
|
|
|
|
|
|
|
# $N1YEAR = ($DAY4Y - 1) / $JDAY1Y; |
998
|
|
|
|
|
|
|
# if ($N1YEAR > 0) { |
999
|
|
|
|
|
|
|
# # Current year is not a leap frog year |
1000
|
|
|
|
|
|
|
# $DAY1Y = $DAY4Y - $N1YEAR * $JDAY1Y - 1; |
1001
|
|
|
|
|
|
|
# } |
1002
|
|
|
|
|
|
|
#} |
1003
|
|
|
|
|
|
|
|
1004
|
|
|
|
|
|
|
# First of every fourth century: 16??, 20??, 24??, etc. |
1005
|
|
|
|
|
|
|
#$DAY1C = $DAY4C; |
1006
|
|
|
|
|
|
|
#$N4YEAR = $DAY1C / JDAY4Y; |
1007
|
|
|
|
|
|
|
#$DAY4Y = $DAY1C - $N4YEAR * $JDAY4Y; |
1008
|
|
|
|
|
|
|
#$N1YEAR = ($DAY4Y - 1) / $JDAY1Y; |
1009
|
|
|
|
|
|
|
#if (N1YEAR > 0) { |
1010
|
|
|
|
|
|
|
# # Current year is not a leap frog year |
1011
|
|
|
|
|
|
|
# $DAY1Y = $DAY4Y - $N1YEAR * $JDAY1Y - 1; |
1012
|
|
|
|
|
|
|
#} |
1013
|
|
|
|
|
|
|
|
1014
|
|
|
|
|
|
|
# GoTo 100 |
1015
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
# First four years of every second to fourth century when there is |
1017
|
|
|
|
|
|
|
# no leap year: 2100-2103, 2200-2203, 2300-2303, etc. |
1018
|
|
|
|
|
|
|
# DAY4Y = DAY1C |
1019
|
|
|
|
|
|
|
# N1YEAR = DAY4Y/JDAY1Y |
1020
|
|
|
|
|
|
|
# DAY1Y = DAY4Y - N1YEAR*JDAY1Y |
1021
|
|
|
|
|
|
|
# GoTo 210 |
1022
|
|
|
|
|
|
|
|
1023
|
|
|
|
|
|
|
# |
1024
|
|
|
|
|
|
|
# Current year is a leap frog year |
1025
|
|
|
|
|
|
|
# |
1026
|
|
|
|
|
|
|
#100 DAY1Y = DAY4Y |
1027
|
|
|
|
|
|
|
# Do 120 M=1,11 |
1028
|
|
|
|
|
|
|
#120 If (DAY1Y < JDSUML(M+1)) GoTo 130 |
1029
|
|
|
|
|
|
|
#C M=12 |
1030
|
|
|
|
|
|
|
#130 JYEAR = 2000 + N4CENT*400 + N1CENT*100 + N4YEAR*4 + N1YEAR |
1031
|
|
|
|
|
|
|
# JMONTH = M |
1032
|
|
|
|
|
|
|
# DATE = DAY1Y - JDSUML(M) |
1033
|
|
|
|
|
|
|
# Return |
1034
|
|
|
|
|
|
|
|
1035
|
|
|
|
|
|
|
# |
1036
|
|
|
|
|
|
|
# Current year is not a leap frog year |
1037
|
|
|
|
|
|
|
# |
1038
|
|
|
|
|
|
|
#210 Do 220 M=1,11 |
1039
|
|
|
|
|
|
|
#220 If (DAY1Y < JDSUMN(M+1)) GoTo 230 |
1040
|
|
|
|
|
|
|
#C M=12 |
1041
|
|
|
|
|
|
|
#230 JYEAR = 2000 + N4CENT*400 + N1CENT*100 + N4YEAR*4 + N1YEAR |
1042
|
|
|
|
|
|
|
# JMONTH = M |
1043
|
|
|
|
|
|
|
# DATE = DAY1Y - JDSUMN(M) |
1044
|
|
|
|
|
|
|
# Return |
1045
|
|
|
|
|
|
|
# End |
1046
|
|
|
|
|
|
|
} |
1047
|
|
|
|
|
|
|
|
1048
|
|
|
|
|
|
|
#----------------------------------------# |
1049
|
|
|
|
|
|
|
# For a given year, VERNAL calculates an approximate time of vernal |
1050
|
|
|
|
|
|
|
# equinox in days measured from 2000 January 1, hour 0. |
1051
|
|
|
|
|
|
|
|
1052
|
|
|
|
|
|
|
# VERNAL assumes that vernal equinoxes from one year to the next |
1053
|
|
|
|
|
|
|
# are separated by exactly 365.2425 days, a tropical year |
1054
|
|
|
|
|
|
|
# [Explanatory Supplement to The Astronomical Ephemeris]. If the |
1055
|
|
|
|
|
|
|
# tropical year is 365.2422 days, as indicated by other references, |
1056
|
|
|
|
|
|
|
# then the time of the vernal equinox will be off by 2.88 hours in |
1057
|
|
|
|
|
|
|
# 400 years. |
1058
|
|
|
|
|
|
|
|
1059
|
|
|
|
|
|
|
# Time of vernal equinox for year 2000 A.D. is March 20, 7:36 GMT |
1060
|
|
|
|
|
|
|
# [NASA Reference Publication 1349, Oct. 1994]. VERNAL assumes |
1061
|
|
|
|
|
|
|
# that vernal equinox for year 2000 will be on March 20, 7:30, or |
1062
|
|
|
|
|
|
|
# 79.3125 days from 2000 January 1, hour 0. Vernal equinoxes for |
1063
|
|
|
|
|
|
|
# other years returned by VERNAL are also measured in days from |
1064
|
|
|
|
|
|
|
# 2000 January 1, hour 0. 79.3125 = 31 + 29 + 19 + 7.5/24. |
1065
|
|
|
|
|
|
|
sub vernal { |
1066
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1067
|
0
|
|
|
|
|
|
my $JYEAR = shift; |
1068
|
|
|
|
|
|
|
|
1069
|
0
|
|
|
|
|
|
my $EDAYzY = 365.2425; |
1070
|
0
|
|
|
|
|
|
my $VE200D = 79.3125; |
1071
|
0
|
|
|
|
|
|
my $VERNAL = $VE200D + ($JYEAR - 2000) * $EDAYzY; |
1072
|
|
|
|
|
|
|
} |
1073
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
#----------------------------------------# |
1075
|
|
|
|
|
|
|
# QLEAPY |
1076
|
|
|
|
|
|
|
# Determine whether the given JYEAR is a Leap Year or not. |
1077
|
|
|
|
|
|
|
#----------------------------------------# |
1078
|
|
|
|
|
|
|
sub QLEAPY { |
1079
|
0
|
|
|
0
|
0
|
|
my $year = shift; |
1080
|
|
|
|
|
|
|
|
1081
|
0
|
0
|
|
|
|
|
if(!($year%4)) { |
1082
|
0
|
0
|
|
|
|
|
if($year%100) { |
1083
|
0
|
|
|
|
|
|
return(0); # if leap year |
1084
|
|
|
|
|
|
|
} else { |
1085
|
0
|
0
|
|
|
|
|
if(!($year%400)) { |
1086
|
0
|
|
|
|
|
|
return(0); |
1087
|
|
|
|
|
|
|
} |
1088
|
0
|
|
|
|
|
|
return(1) |
1089
|
|
|
|
|
|
|
} |
1090
|
|
|
|
|
|
|
} |
1091
|
|
|
|
|
|
|
|
1092
|
0
|
|
|
|
|
|
return 1; # if it is not leap year |
1093
|
|
|
|
|
|
|
} |
1094
|
|
|
|
|
|
|
|
1095
|
|
|
|
|
|
|
#----------------------------------------# |
1096
|
|
|
|
|
|
|
# COSZIJ calculates the daily average cosine of the zenith angle |
1097
|
|
|
|
|
|
|
# weighted by time and weighted by sunlight. |
1098
|
|
|
|
|
|
|
# |
1099
|
|
|
|
|
|
|
# Input: RLAT = latitude (degrees) |
1100
|
|
|
|
|
|
|
# SIND,COSD = sine and cosine of the declination angle |
1101
|
|
|
|
|
|
|
# |
1102
|
|
|
|
|
|
|
# Output: COSZT = sum(cosZ*dT) / sum(dT) |
1103
|
|
|
|
|
|
|
# COSZS = sum(cosZ*cosZ*dT) / sum(cosZ*dT) |
1104
|
|
|
|
|
|
|
# |
1105
|
|
|
|
|
|
|
# Intern: DAWN = time of DAWN (temporal radians) at mean local time |
1106
|
|
|
|
|
|
|
# DUSK = time of DUSK (temporal radians) at mean local time |
1107
|
|
|
|
|
|
|
#----------------------------------------# |
1108
|
|
|
|
|
|
|
sub COSZIJ { |
1109
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
1110
|
0
|
|
|
|
|
|
my $SIND = shift; |
1111
|
0
|
|
|
|
|
|
my $COSD = shift; |
1112
|
|
|
|
|
|
|
|
1113
|
0
|
|
|
|
|
|
my $DUSK = 0; |
1114
|
0
|
|
|
|
|
|
my $CDUSK = 0; |
1115
|
0
|
|
|
|
|
|
my $SDUSK = 0; |
1116
|
0
|
|
|
|
|
|
my $DAWN = 0; |
1117
|
0
|
|
|
|
|
|
my $S2DUSK = 0; |
1118
|
0
|
|
|
|
|
|
my $SDAWN = 0; |
1119
|
0
|
|
|
|
|
|
my $S2DAWN = 0; |
1120
|
0
|
|
|
|
|
|
my $COSZT = 0; |
1121
|
0
|
|
|
|
|
|
my $COSZS = 0; |
1122
|
0
|
|
|
|
|
|
my $ECOSZ = 0; |
1123
|
0
|
|
|
|
|
|
my $QCOSZ = 0; |
1124
|
|
|
|
|
|
|
|
1125
|
0
|
|
|
|
|
|
my $SINJ = sin($self->{CONSTANTS}->{TWOPI} * $self->{latitude} / 360); |
1126
|
|
|
|
|
|
|
#print "sinj : $SINJ\n"; |
1127
|
0
|
|
|
|
|
|
my $COSJ = cos($self->{CONSTANTS}->{TWOPI} * $self->{latitude} / 360); |
1128
|
|
|
|
|
|
|
#print "cosj : $COSJ\n"; |
1129
|
|
|
|
|
|
|
|
1130
|
0
|
|
|
|
|
|
my $SJSD = $SINJ * $SIND; |
1131
|
0
|
|
|
|
|
|
my $CJCD = $COSJ * $COSD; |
1132
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
# Constant nightime at this latitude |
1134
|
0
|
0
|
|
|
|
|
if ( ($SJSD + $CJCD) <= 0) { |
1135
|
0
|
|
|
|
|
|
$DAWN = 999999; |
1136
|
0
|
|
|
|
|
|
$DUSK = 999999; |
1137
|
0
|
|
|
|
|
|
$COSZT = 0; |
1138
|
0
|
|
|
|
|
|
$COSZS = 0; |
1139
|
|
|
|
|
|
|
} |
1140
|
|
|
|
|
|
|
|
1141
|
|
|
|
|
|
|
# Constant daylight at this latitude |
1142
|
0
|
0
|
|
|
|
|
if ( ($SJSD - $CJCD) >= 0) { |
1143
|
0
|
|
|
|
|
|
$DAWN = -999999; |
1144
|
0
|
|
|
|
|
|
$DUSK = -999999; |
1145
|
0
|
|
|
|
|
|
$ECOSZ = $SJSD * $self->{CONSTANTS}->{TWOPI}; |
1146
|
0
|
|
|
|
|
|
$QCOSZ = $SJSD * $ECOSZ + .5 * $CJCD * $CJCD * $self->{CONSTANTS}->{TWOPI}; |
1147
|
0
|
|
|
|
|
|
$COSZT = $SJSD; # = ECOSZ/$self->{CONSTANTS}->{TWOPI} |
1148
|
0
|
|
|
|
|
|
$COSZS = $QCOSZ / $ECOSZ; |
1149
|
|
|
|
|
|
|
} |
1150
|
|
|
|
|
|
|
|
1151
|
|
|
|
|
|
|
# Compute DAWN and DUSK (at local time) and their sines |
1152
|
0
|
|
|
|
|
|
$CDUSK = - ($SJSD / $CJCD); |
1153
|
0
|
|
|
|
|
|
$DUSK = acos($CDUSK); |
1154
|
0
|
|
|
|
|
|
$SDUSK = sqrt( $CJCD * $CJCD - $SJSD * $SJSD) / $CJCD; |
1155
|
0
|
|
|
|
|
|
$S2DUSK = 2 * $SDUSK * $CDUSK; |
1156
|
0
|
|
|
|
|
|
$DAWN = -$DUSK; |
1157
|
0
|
|
|
|
|
|
$SDAWN = -$SDUSK; |
1158
|
0
|
|
|
|
|
|
$S2DAWN = -$S2DUSK; |
1159
|
|
|
|
|
|
|
|
1160
|
|
|
|
|
|
|
# Nightime at initial and final times with daylight in between |
1161
|
0
|
|
|
|
|
|
$ECOSZ = $SJSD * ($DUSK-$DAWN) + $CJCD * ($SDUSK - $SDAWN); |
1162
|
0
|
|
|
|
|
|
$QCOSZ = $SJSD * $ECOSZ + $CJCD * ($SJSD * ($SDUSK - $SDAWN) +.5 * $CJCD * ($DUSK - $DAWN + .5 * ($S2DUSK-$S2DAWN) ) ); |
1163
|
0
|
|
|
|
|
|
$COSZT = $ECOSZ / $self->{CONSTANTS}->{TWOPI}; |
1164
|
0
|
|
|
|
|
|
$COSZS = $QCOSZ / $ECOSZ; |
1165
|
|
|
|
|
|
|
|
1166
|
0
|
|
|
|
|
|
return ($COSZT, $COSZS); |
1167
|
|
|
|
|
|
|
} |
1168
|
|
|
|
|
|
|
|
1169
|
|
|
|
|
|
|
# my own div because we're getting nowhere with '%' |
1170
|
|
|
|
|
|
|
# this is such a hack |
1171
|
|
|
|
|
|
|
# a = 49.2 |
1172
|
|
|
|
|
|
|
# b = 6.23 |
1173
|
|
|
|
|
|
|
# a/b = 7.89 |
1174
|
|
|
|
|
|
|
# d = 7 |
1175
|
|
|
|
|
|
|
# e = 89 |
1176
|
|
|
|
|
|
|
# return 49.2 - (7 * 6.23) |
1177
|
|
|
|
|
|
|
sub modulo { |
1178
|
0
|
|
|
0
|
0
|
|
my $a = shift; |
1179
|
0
|
|
|
|
|
|
my $b = shift; |
1180
|
0
|
|
|
|
|
|
my ($d, $e) = split(/\./, ($a/$b), 2); |
1181
|
|
|
|
|
|
|
|
1182
|
0
|
|
|
|
|
|
return ($a - ($d * $b)); |
1183
|
|
|
|
|
|
|
} |
1184
|
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
# my own round up because the one that I found on perlmonks.org |
1186
|
|
|
|
|
|
|
# didn't work well |
1187
|
|
|
|
|
|
|
sub _roundup { |
1188
|
0
|
|
|
0
|
|
|
my $a = shift; |
1189
|
0
|
|
|
|
|
|
my ($b, $c) = split(/\./, $a, 2); |
1190
|
|
|
|
|
|
|
|
1191
|
0
|
0
|
|
|
|
|
if (($a-$b) >= .5) { |
1192
|
0
|
|
|
|
|
|
return ($b+1); |
1193
|
|
|
|
|
|
|
} |
1194
|
|
|
|
|
|
|
|
1195
|
0
|
|
|
|
|
|
return $b; |
1196
|
|
|
|
|
|
|
} |
1197
|
|
|
|
|
|
|
|
1198
|
|
|
|
|
|
|
1; |
1199
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
__END__ |