| 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__ |