| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Astro::Coord::ECI::VSOP87D; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 13 |  |  | 13 |  | 26015 | use 5.008; | 
|  | 13 |  |  |  |  | 51 |  | 
| 4 |  |  |  |  |  |  |  | 
| 5 | 13 |  |  | 13 |  | 157 | use strict; | 
|  | 13 |  |  |  |  | 27 |  | 
|  | 13 |  |  |  |  | 392 |  | 
| 6 | 13 |  |  | 13 |  | 101 | use warnings; | 
|  | 13 |  |  |  |  | 28 |  | 
|  | 13 |  |  |  |  | 433 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 13 |  |  | 13 |  | 8716 | use utf8; | 
|  | 13 |  |  |  |  | 201 |  | 
|  | 13 |  |  |  |  | 66 |  | 
| 9 |  |  |  |  |  |  |  | 
| 10 | 13 |  |  |  |  | 1282 | use Astro::Coord::ECI::Utils qw{ | 
| 11 |  |  |  |  |  |  | AU PI SECSPERDAY TWOPI | 
| 12 |  |  |  |  |  |  | asin deg2rad jcent2000 julianday | 
| 13 |  |  |  |  |  |  | load_module looks_like_number mod2pi | 
| 14 |  |  |  |  |  |  | rad2deg rad2dms rad2hms tan | 
| 15 | 13 |  |  | 13 |  | 1410 | }; | 
|  | 13 |  |  |  |  | 20794 |  | 
| 16 | 13 |  |  | 13 |  | 87 | use Exporter qw{ import }; | 
|  | 13 |  |  |  |  | 31 |  | 
|  | 13 |  |  |  |  | 361 |  | 
| 17 | 13 |  |  | 13 |  | 82 | use Carp; | 
|  | 13 |  |  |  |  | 37 |  | 
|  | 13 |  |  |  |  | 642 |  | 
| 18 | 13 |  |  | 13 |  | 73 | use POSIX qw{ floor }; | 
|  | 13 |  |  |  |  | 26 |  | 
|  | 13 |  |  |  |  | 126 |  | 
| 19 | 13 |  |  | 13 |  | 9911 | use Storable qw{ dclone }; | 
|  | 13 |  |  |  |  | 44416 |  | 
|  | 13 |  |  |  |  | 1023 |  | 
| 20 |  |  |  |  |  |  |  | 
| 21 | 13 |  |  | 13 |  | 112 | use constant DAYS_PER_JULIAN_MILENNIUM	=> 365250; | 
|  | 13 |  |  |  |  | 31 |  | 
|  | 13 |  |  |  |  | 970 |  | 
| 22 | 13 |  |  | 13 |  | 82 | use constant CODE_REF	=> ref sub {}; | 
|  | 13 |  |  |  |  | 33 |  | 
|  | 13 |  |  |  |  | 740 |  | 
| 23 | 13 |  |  | 13 |  | 100 | use constant HASH_REF	=> ref {}; | 
|  | 13 |  |  |  |  | 66 |  | 
|  | 13 |  |  |  |  | 794 |  | 
| 24 | 13 |  |  | 13 |  | 84 | use constant SUN_CLASS	=> __PACKAGE__ . '::Sun'; | 
|  | 13 |  |  |  |  | 26 |  | 
|  | 13 |  |  |  |  | 815 |  | 
| 25 |  |  |  |  |  |  |  | 
| 26 |  |  |  |  |  |  | BEGIN { | 
| 27 | 13 | 100 |  | 13 |  | 6441 | __PACKAGE__->can( 'DEBUG' ) | 
| 28 |  |  |  |  |  |  | or constant->import( DEBUG => 0 ); | 
| 29 |  |  |  |  |  |  | } | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  | our $VERSION = '0.006'; | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | my @basic_export = qw{ | 
| 34 |  |  |  |  |  |  | SUN_CLASS | 
| 35 |  |  |  |  |  |  | model_cutoff_definition | 
| 36 |  |  |  |  |  |  | nutation obliquity order period time_set year | 
| 37 |  |  |  |  |  |  | __default | 
| 38 |  |  |  |  |  |  | __get_attr | 
| 39 |  |  |  |  |  |  | __mutate_model_cutoff __mutate_nutation_cutoff | 
| 40 |  |  |  |  |  |  | }; | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | our @EXPORT_OK = ( | 
| 43 |  |  |  |  |  |  | @basic_export, | 
| 44 |  |  |  |  |  |  | qw{ | 
| 45 |  |  |  |  |  |  | geometric_longitude | 
| 46 |  |  |  |  |  |  | synodic_period | 
| 47 |  |  |  |  |  |  | __angle_subtended_from_earth | 
| 48 |  |  |  |  |  |  | __longitude_from_sun | 
| 49 |  |  |  |  |  |  | __model | 
| 50 |  |  |  |  |  |  | }, | 
| 51 |  |  |  |  |  |  | ); | 
| 52 |  |  |  |  |  |  | our %EXPORT_TAGS = ( | 
| 53 |  |  |  |  |  |  | mixin	=> [ @basic_export, qw{ | 
| 54 |  |  |  |  |  |  | synodic_period | 
| 55 |  |  |  |  |  |  | __angle_subtended_from_earth | 
| 56 |  |  |  |  |  |  | __longitude_from_sun | 
| 57 |  |  |  |  |  |  | __model | 
| 58 |  |  |  |  |  |  | } ], | 
| 59 |  |  |  |  |  |  | sun		=> [ @basic_export, qw{ geometric_longitude } ], | 
| 60 |  |  |  |  |  |  | ); | 
| 61 |  |  |  |  |  |  |  | 
| 62 |  |  |  |  |  |  | # We want to ensure SUN_CLASS is loaded because it is our default Sun | 
| 63 |  |  |  |  |  |  | # implementation, but we do it at run time to avoid a circular | 
| 64 |  |  |  |  |  |  | # dependency. | 
| 65 |  |  |  |  |  |  | load_module( SUN_CLASS ); | 
| 66 |  |  |  |  |  |  |  | 
| 67 |  |  |  |  |  |  | sub time_set { | 
| 68 | 3011 |  |  | 3011 | 1 | 193284 | my ( $self ) = @_; | 
| 69 | 3011 |  |  |  |  | 8319 | my $attr = $self->__get_attr(); | 
| 70 | 3011 |  |  |  |  | 8254 | my $time = $self->dynamical(); | 
| 71 | 3011 |  |  |  |  | 70701 | my $cutoff = $self->get( 'model_cutoff' ); | 
| 72 | 3011 |  |  |  |  | 7989 | my $cutoff_def = $self->model_cutoff_definition(); | 
| 73 | 3011 |  |  |  |  | 6848 | my $sun = $self->get( 'sun' ); | 
| 74 |  |  |  |  |  |  |  | 
| 75 | 3011 |  |  |  |  | 5292 | DEBUG | 
| 76 |  |  |  |  |  |  | and printf <<'EOD', ref $self, ref $sun, julianday( $time ); | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | self = %s | 
| 79 |  |  |  |  |  |  | Sun = %s | 
| 80 |  |  |  |  |  |  | JDE = %.5f | 
| 81 |  |  |  |  |  |  | EOD | 
| 82 |  |  |  |  |  |  |  | 
| 83 | 3011 |  |  |  |  | 7929 | my $T = jcent2000( $time ); | 
| 84 |  |  |  |  |  |  |  | 
| 85 | 3011 |  |  |  |  | 20085 | my ( $Lb, $Bb, $Rb ) = $self->__model( $time, | 
| 86 |  |  |  |  |  |  | model_cutoff_definition	=> $cutoff_def, | 
| 87 |  |  |  |  |  |  | ); | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 3011 |  |  |  |  | 6958 | my ( $Le, $Be, $Re ); | 
| 90 |  |  |  |  |  |  |  | 
| 91 | 3011 | 50 |  |  |  | 15217 | if ( $sun->isa( SUN_CLASS ) ) { | 
| 92 |  |  |  |  |  |  | # We call __model as a subroutine because the Earth's model | 
| 93 |  |  |  |  |  |  | # parameters are hung on the Sun, but if we call it as a method | 
| 94 |  |  |  |  |  |  | # we get the Sun's model, which always returns zeroes. | 
| 95 | 3011 |  |  |  |  | 8318 | ( $Le, $Be, $Re ) = __model( SUN_CLASS, $time, | 
| 96 |  |  |  |  |  |  | model_cutoff_definition	=> $sun->model_cutoff_definition( $cutoff ), | 
| 97 |  |  |  |  |  |  | ); | 
| 98 |  |  |  |  |  |  | } else { | 
| 99 | 0 |  |  |  |  | 0 | confess sprintf 'TODO Sun class %s not supported', ref $sun; | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 3011 |  |  |  |  | 5903 | DEBUG | 
| 103 |  |  |  |  |  |  | and printf <<'EOD', rad2deg( $Le ), rad2deg( $Be ), $Re; | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | Heliocentric position of the Earth: | 
| 106 |  |  |  |  |  |  | L0 = %.5f | 
| 107 |  |  |  |  |  |  | B0 = %.5f | 
| 108 |  |  |  |  |  |  | R0 = %.6f | 
| 109 |  |  |  |  |  |  | EOD | 
| 110 |  |  |  |  |  |  |  | 
| 111 | 3011 |  |  |  |  | 6096 | my ( $lambda, $beta, $Delta, $long_sym ); | 
| 112 | 3011 | 100 |  |  |  | 7481 | if ( $Rb ) {		# Not the Sun | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 1719 |  |  |  |  | 2812 | my $last_tau = 0; | 
| 115 |  |  |  |  |  |  |  | 
| 116 | 1719 |  |  |  |  | 3038 | while ( 1 ) { | 
| 117 | 3789 |  |  |  |  | 6006 | DEBUG | 
| 118 |  |  |  |  |  |  | and printf <<'EOD', $self->get( 'name' ), | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | Heliocentric position of %s: | 
| 121 |  |  |  |  |  |  | L = %.5f | 
| 122 |  |  |  |  |  |  | B = %.5f | 
| 123 |  |  |  |  |  |  | R = %.6f | 
| 124 |  |  |  |  |  |  | EOD | 
| 125 |  |  |  |  |  |  | rad2deg( $Lb ), rad2deg( $Bb ), $Rb; | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | # Meeus 33.1 | 
| 128 | 3789 |  |  |  |  | 8094 | my $cosBb = cos( $Bb ); | 
| 129 | 3789 |  |  |  |  | 6410 | my $cosBe = cos( $Be ); | 
| 130 | 3789 |  |  |  |  | 9290 | my $x = $Rb * $cosBb * cos( $Lb ) - $Re * $cosBe * cos( $Le ); | 
| 131 | 3789 |  |  |  |  | 9775 | my $y = $Rb * $cosBb * sin( $Lb ) - $Re * $cosBe * sin( $Le ); | 
| 132 | 3789 |  |  |  |  | 7419 | my $z = $Rb * sin( $Bb )          - $Re * sin( $Be ); | 
| 133 |  |  |  |  |  |  |  | 
| 134 | 3789 |  |  |  |  | 7277 | $Delta = sqrt( $x * $x + $y * $y + $z * $z ); | 
| 135 |  |  |  |  |  |  |  | 
| 136 | 13 |  |  | 13 |  | 109 | use constant TAU_FACTOR => 0.005_775_518_3 * SECSPERDAY; | 
|  | 13 |  |  |  |  | 30 |  | 
|  | 13 |  |  |  |  | 3487 |  | 
| 137 | 3789 |  |  |  |  | 6069 | my $tau = TAU_FACTOR * $Delta; | 
| 138 |  |  |  |  |  |  |  | 
| 139 | 3789 |  |  |  |  | 5555 | DEBUG | 
| 140 |  |  |  |  |  |  | and printf <<'EOD', $x, $y, $z, $Delta, $tau / SECSPERDAY; | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | x = %+.6f | 
| 143 |  |  |  |  |  |  | y = %+.6f | 
| 144 |  |  |  |  |  |  | z = %+.6f | 
| 145 |  |  |  |  |  |  | 𝚫 = %.6f | 
| 146 |  |  |  |  |  |  | 𝛕 = %.7f day | 
| 147 |  |  |  |  |  |  | EOD | 
| 148 | 3789 | 100 |  |  |  | 45072 | if ( ( my $check = sprintf '%.1f', $tau ) ne $last_tau ) { | 
| 149 | 2070 |  |  |  |  | 4007 | $last_tau = $check; | 
| 150 |  |  |  |  |  |  |  | 
| 151 | 2070 |  |  |  |  | 3410 | DEBUG | 
| 152 |  |  |  |  |  |  | and printf <<'EOD', julianday( $time - $tau ); | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | new JDE = %.5f | 
| 155 |  |  |  |  |  |  | EOD | 
| 156 | 2070 |  |  |  |  | 7258 | ( $Lb, $Bb, $Rb ) = $self->__model( | 
| 157 |  |  |  |  |  |  | $time - $tau, | 
| 158 |  |  |  |  |  |  | model_cutoff_definition	=> $cutoff_def, | 
| 159 |  |  |  |  |  |  | ); | 
| 160 |  |  |  |  |  |  | } else { | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | # Meeus 33.2 | 
| 163 | 1719 |  |  |  |  | 6938 | $lambda = atan2( $y, $x ); | 
| 164 | 1719 | 100 |  |  |  | 6012 | $lambda < 0 | 
| 165 |  |  |  |  |  |  | and $lambda += TWOPI; | 
| 166 | 1719 |  |  |  |  | 4227 | $beta = atan2( $z, sqrt( $x * $x + $y * $y ) ); | 
| 167 | 1719 |  |  |  |  | 3205 | $long_sym = '𝛌'; | 
| 168 | 1719 |  |  |  |  | 4195 | last; | 
| 169 |  |  |  |  |  |  | } | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | DEBUG | 
| 173 | 1719 |  |  |  |  | 2516 | and printf <<'EOD', $long_sym, | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | Geocentric ecliptic position: | 
| 176 |  |  |  |  |  |  | %s = %.5f | 
| 177 |  |  |  |  |  |  | = %s | 
| 178 |  |  |  |  |  |  | 𝛃 = %.5f | 
| 179 |  |  |  |  |  |  | = %s | 
| 180 |  |  |  |  |  |  | EOD | 
| 181 |  |  |  |  |  |  | rad2deg( $lambda ), rad2dms( $lambda ), | 
| 182 |  |  |  |  |  |  | rad2deg( $beta ), rad2dms( $beta ); | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | # Meeus corrects for aberration here for a planet, before | 
| 185 |  |  |  |  |  |  | # conversion to FK5. It makes no real difference to the answer, | 
| 186 |  |  |  |  |  |  | # since everything done to the latitude and longitude before | 
| 187 |  |  |  |  |  |  | # conversion to equatorial is just addition and subtraction, and | 
| 188 |  |  |  |  |  |  | # therefore commutes. But it plays merry Hell with the poor slob | 
| 189 |  |  |  |  |  |  | # who is trying to verify code versus the worked examples, | 
| 190 |  |  |  |  |  |  | # because it changes the intermediate results. Sigh. | 
| 191 |  |  |  |  |  |  |  | 
| 192 |  |  |  |  |  |  | # Aberration per Meeus 23.2 | 
| 193 | 13 |  |  |  |  | 84 | use constant CONSTANT_OF_ABERRATION	=> deg2rad( | 
| 194 | 13 |  |  | 13 |  | 102 | 20.49552 / 3600 ); | 
|  | 13 |  |  |  |  | 27 |  | 
| 195 |  |  |  |  |  |  | # Longitude of the Sun | 
| 196 | 1719 |  |  |  |  | 4956 | my $Ls = mod2pi( $Le + PI ); | 
| 197 |  |  |  |  |  |  | # Eccentricity of the Earth's orbit | 
| 198 | 1719 |  |  |  |  | 8867 | my $e = ( - 0.000_000_126_7 * $T - 0.000_042_037 ) * $T + | 
| 199 |  |  |  |  |  |  | 0.016_708_634; | 
| 200 |  |  |  |  |  |  | # Longitude of perihelion of the Earth's orbit | 
| 201 | 1719 |  |  |  |  | 5988 | my $pi = deg2rad( ( 0.000_46 * $T + 1.71946 ) * $T + 102.93735 ); | 
| 202 |  |  |  |  |  |  |  | 
| 203 | 1719 |  |  |  |  | 10336 | my $delta_lambda = ( ( $e * cos( $pi - $lambda ) - cos( $Ls - | 
| 204 |  |  |  |  |  |  | $lambda ) ) / cos $beta ) * CONSTANT_OF_ABERRATION; | 
| 205 | 1719 |  |  |  |  | 5144 | my $delta_beta = - sin( $beta ) * ( sin( $Ls - $lambda ) - $e * | 
| 206 |  |  |  |  |  |  | sin( $pi - $lambda ) ) * CONSTANT_OF_ABERRATION; | 
| 207 | 1719 |  |  |  |  | 2602 | $lambda += $delta_lambda; | 
| 208 | 1719 |  |  |  |  | 2879 | $beta += $delta_beta; | 
| 209 |  |  |  |  |  |  |  | 
| 210 | 1719 |  |  |  |  | 2990 | DEBUG | 
| 211 |  |  |  |  |  |  | and printf <<'EOD', | 
| 212 |  |  |  |  |  |  |  | 
| 213 |  |  |  |  |  |  | Planetary aberration: | 
| 214 |  |  |  |  |  |  | 𝚫𝛌 = %s | 
| 215 |  |  |  |  |  |  | 𝛌 = %.5f | 
| 216 |  |  |  |  |  |  | = %s | 
| 217 |  |  |  |  |  |  | 𝚫𝛃 = %s | 
| 218 |  |  |  |  |  |  | 𝛃 = %.5f | 
| 219 |  |  |  |  |  |  | = %s | 
| 220 |  |  |  |  |  |  | EOD | 
| 221 |  |  |  |  |  |  | rad2dms( $delta_lambda), rad2deg( $lambda ), rad2dms( $lambda ), | 
| 222 |  |  |  |  |  |  | rad2dms( $delta_beta), rad2deg( $beta ), rad2dms( $beta ); | 
| 223 |  |  |  |  |  |  |  | 
| 224 |  |  |  |  |  |  | } else {			# The Sun | 
| 225 | 1292 |  |  |  |  | 3686 | ( $lambda, $beta, $Delta ) = ( mod2pi( $Le + PI ), - $Be, $Re ); | 
| 226 | 1292 |  |  |  |  | 6656 | $long_sym = '☉'; | 
| 227 |  |  |  |  |  |  |  | 
| 228 | 1292 |  |  |  |  | 2074 | DEBUG | 
| 229 |  |  |  |  |  |  | and printf <<'EOD', $long_sym, | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | Geocentric ecliptic position: | 
| 232 |  |  |  |  |  |  | %s = %.5f | 
| 233 |  |  |  |  |  |  | = %s | 
| 234 |  |  |  |  |  |  | 𝛃 = %.5f | 
| 235 |  |  |  |  |  |  | = %s | 
| 236 |  |  |  |  |  |  | EOD | 
| 237 |  |  |  |  |  |  | rad2deg( $lambda ), rad2dms( $lambda ), | 
| 238 |  |  |  |  |  |  | rad2deg( $beta ), rad2dms( $beta ); | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  |  | 
| 241 | 3011 |  |  |  |  | 4918 | DEBUG | 
| 242 |  |  |  |  |  |  | and printf <<'EOD', | 
| 243 |  |  |  |  |  |  |  | 
| 244 |  |  |  |  |  |  | %s = %.6f | 
| 245 |  |  |  |  |  |  | 𝛃 = %.6f | 
| 246 |  |  |  |  |  |  | = %s | 
| 247 |  |  |  |  |  |  | 𝚫 = %.6f | 
| 248 |  |  |  |  |  |  | EOD | 
| 249 |  |  |  |  |  |  | $long_sym, rad2deg( $lambda ), | 
| 250 |  |  |  |  |  |  | rad2deg( $beta ), rad2dms( $beta ), $Delta; | 
| 251 |  |  |  |  |  |  |  | 
| 252 |  |  |  |  |  |  | # Convert to FK5. Meeus says (ch. 25 pg 166) this is necessary for | 
| 253 |  |  |  |  |  |  | # high accuracy (though not if using his truncated VSOP87D), though | 
| 254 |  |  |  |  |  |  | # Bretagnon & Francou seem to say it is needed only for VSOP87 and | 
| 255 |  |  |  |  |  |  | # VSOP87E. | 
| 256 |  |  |  |  |  |  |  | 
| 257 |  |  |  |  |  |  | # Meeus 32.3 | 
| 258 |  |  |  |  |  |  | { | 
| 259 | 13 |  |  | 13 |  | 3597 | use constant DYN_TO_FK5_LINEAR	=> deg2rad( 1.397 ); | 
|  | 13 |  |  |  |  | 43 |  | 
|  | 13 |  |  |  |  | 56 |  | 
|  | 3011 |  |  |  |  | 5011 |  | 
| 260 | 13 |  |  | 13 |  | 918 | use constant DYN_TO_FK5_QUADRATIC	=> deg2rad( 0.00031 ); | 
|  | 13 |  |  |  |  | 30 |  | 
|  | 13 |  |  |  |  | 51 |  | 
| 261 | 13 |  |  | 13 |  | 1018 | use constant DYN_TO_FK5_DELTA_LON	=> - deg2rad( 0.09033 / 3600 ); | 
|  | 13 |  |  |  |  | 27 |  | 
|  | 13 |  |  |  |  | 56 |  | 
| 262 | 13 |  |  | 13 |  | 966 | use constant DYN_TO_FK5_FACTOR	=> deg2rad( 0.03916 / 3600 ); | 
|  | 13 |  |  |  |  | 23 |  | 
|  | 13 |  |  |  |  | 46 |  | 
| 263 | 3011 |  |  |  |  | 6764 | my $lambda_prime = $lambda - ( DYN_TO_FK5_LINEAR + | 
| 264 |  |  |  |  |  |  | DYN_TO_FK5_QUADRATIC * $T ) * $T; | 
| 265 | 3011 |  |  |  |  | 7232 | my $factor = DYN_TO_FK5_FACTOR * ( cos( $lambda_prime ) - sin( | 
| 266 |  |  |  |  |  |  | $lambda_prime ) ); | 
| 267 | 3011 |  |  |  |  | 9534 | my $delta_lambda = DYN_TO_FK5_DELTA_LON + $factor * tan( $beta ); | 
| 268 | 3011 |  |  |  |  | 12738 | my $delta_beta = $factor; | 
| 269 |  |  |  |  |  |  |  | 
| 270 | 3011 |  |  |  |  | 4676 | DEBUG and printf <<'EOD', | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | Conversion to FK5 | 
| 273 |  |  |  |  |  |  | 𝛌' = %.2f | 
| 274 |  |  |  |  |  |  | 𝚫%s = %s | 
| 275 |  |  |  |  |  |  | 𝚫𝛃 = %s | 
| 276 |  |  |  |  |  |  | EOD | 
| 277 |  |  |  |  |  |  | rad2deg( $lambda_prime ), $long_sym, | 
| 278 |  |  |  |  |  |  | rad2dms( $delta_lambda, 5 ), rad2dms( $delta_beta ); | 
| 279 |  |  |  |  |  |  |  | 
| 280 | 3011 |  |  |  |  | 4783 | $lambda += $delta_lambda; | 
| 281 | 3011 |  |  |  |  | 4610 | $beta += $delta_beta; | 
| 282 |  |  |  |  |  |  |  | 
| 283 | 3011 |  |  |  |  | 4482 | DEBUG and printf <<'EOD', | 
| 284 |  |  |  |  |  |  |  | 
| 285 |  |  |  |  |  |  | %s = %.5f | 
| 286 |  |  |  |  |  |  | = %s | 
| 287 |  |  |  |  |  |  | 𝛃 = %.5f | 
| 288 |  |  |  |  |  |  | = %s | 
| 289 |  |  |  |  |  |  | EOD | 
| 290 |  |  |  |  |  |  | $long_sym, rad2deg( $lambda ), rad2dms( $lambda ), | 
| 291 |  |  |  |  |  |  | rad2deg( $beta ), rad2dms( $beta ), | 
| 292 |  |  |  |  |  |  | ; | 
| 293 |  |  |  |  |  |  |  | 
| 294 | 3011 |  |  |  |  | 7577 | $attr->{geometric_longitude} = $lambda; | 
| 295 |  |  |  |  |  |  | } | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | # Nutation | 
| 298 | 3011 |  |  |  |  | 10999 | my ( $delta_psi, $delta_eps ) = $self->nutation( $time ); | 
| 299 | 3011 |  |  |  |  | 14852 | $lambda += $delta_psi; | 
| 300 |  |  |  |  |  |  |  | 
| 301 | 3011 |  |  |  |  | 4502 | DEBUG | 
| 302 |  |  |  |  |  |  | and printf <<"EOD", | 
| 303 |  |  |  |  |  |  |  | 
| 304 |  |  |  |  |  |  | Nutation: | 
| 305 |  |  |  |  |  |  | 𝚫𝛙 = %s | 
| 306 |  |  |  |  |  |  | 𝚫𝛆 = %s | 
| 307 |  |  |  |  |  |  | 𝛌 = %.5f | 
| 308 |  |  |  |  |  |  | = %s | 
| 309 |  |  |  |  |  |  | EOD | 
| 310 |  |  |  |  |  |  | rad2dms( $delta_psi ), rad2dms( $delta_eps ), | 
| 311 |  |  |  |  |  |  | rad2deg( $lambda ), rad2dms( $lambda ); | 
| 312 |  |  |  |  |  |  |  | 
| 313 |  |  |  |  |  |  | # Obliquity per Meeus 22.3 | 
| 314 | 3011 |  |  |  |  | 9208 | my $epsilon = $self->obliquity( $time ); | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | # Meeus 25.10 | 
| 317 |  |  |  |  |  |  |  | 
| 318 | 3011 | 100 |  |  |  | 7687 | unless ( $Rb ) {	# The Sun. | 
| 319 |  |  |  |  |  |  | # Aberration | 
| 320 | 13 |  |  | 13 |  | 3781 | use constant SOLAR_ABERRATION_FACTOR => - deg2rad( 20.4898 / 3600 ); | 
|  | 13 |  |  |  |  | 26 |  | 
|  | 13 |  |  |  |  | 74 |  | 
| 321 | 1292 |  |  |  |  | 2637 | my $delta_lambda = SOLAR_ABERRATION_FACTOR / $Delta; | 
| 322 | 1292 |  |  |  |  | 2027 | $lambda += $delta_lambda; | 
| 323 |  |  |  |  |  |  |  | 
| 324 | 1292 |  |  |  |  | 1963 | DEBUG | 
| 325 |  |  |  |  |  |  | and printf <<'EOD', | 
| 326 |  |  |  |  |  |  |  | 
| 327 |  |  |  |  |  |  | Solar aberration: | 
| 328 |  |  |  |  |  |  | 𝚫𝛌 = %s | 
| 329 |  |  |  |  |  |  | 𝛌 = %.5f | 
| 330 |  |  |  |  |  |  | = %s | 
| 331 |  |  |  |  |  |  | EOD | 
| 332 |  |  |  |  |  |  | rad2dms( $delta_lambda), rad2deg( $lambda ), rad2dms( $lambda ); | 
| 333 |  |  |  |  |  |  | } | 
| 334 |  |  |  |  |  |  |  | 
| 335 |  |  |  |  |  |  | DEBUG | 
| 336 | 3011 |  |  |  |  | 4195 | and printf <<'EOD', $long_sym, | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | Final ecliptic coordinates: | 
| 339 |  |  |  |  |  |  | %s = %.5f | 
| 340 |  |  |  |  |  |  | = %s | 
| 341 |  |  |  |  |  |  | 𝛃 = %.5f | 
| 342 |  |  |  |  |  |  | = %s | 
| 343 |  |  |  |  |  |  | EOD | 
| 344 |  |  |  |  |  |  | rad2deg( $lambda ), rad2dms( $lambda ), | 
| 345 |  |  |  |  |  |  | rad2deg( $beta ), rad2dms( $beta ); | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | # NOTE: I could have stored the ecliptic coordinates at this point, | 
| 348 |  |  |  |  |  |  | # and trusted Astro::Coord::ECI to get the conversion right, but | 
| 349 |  |  |  |  |  |  | # since I was at this point not confident in that code, I decided to | 
| 350 |  |  |  |  |  |  | # see Meeus' example all the way to the end. | 
| 351 |  |  |  |  |  |  | # | 
| 352 |  |  |  |  |  |  | # Once nutation and obliquity became methods, it turns out that my | 
| 353 |  |  |  |  |  |  | # conversion between ecliptic and equatorial is equivalent to Meeus' | 
| 354 |  |  |  |  |  |  | # to his accuracy in the examples. That is, if I store equatorial, I | 
| 355 |  |  |  |  |  |  | # get back exactuly the same ecliptic coordiantes as I calculated | 
| 356 |  |  |  |  |  |  | # above. If I store ecliptic, I get back the same equatorial that | 
| 357 |  |  |  |  |  |  | # Meeus calculated. | 
| 358 |  |  |  |  |  |  | # | 
| 359 |  |  |  |  |  |  | # My current intent is to keep the code as-is, simply because for | 
| 360 |  |  |  |  |  |  | # verification purposes I want it to be as close as possible to | 
| 361 |  |  |  |  |  |  | # Meeus' example. | 
| 362 |  |  |  |  |  |  |  | 
| 363 | 3011 |  |  |  |  | 6050 | my $sin_eps = sin $epsilon; | 
| 364 | 3011 |  |  |  |  | 5156 | my $cos_eps = cos $epsilon; | 
| 365 | 3011 |  |  |  |  | 5214 | my $sin_lam = sin $lambda; | 
| 366 | 3011 |  |  |  |  | 4520 | my $sin_bet = sin $beta; | 
| 367 | 3011 |  |  |  |  | 4891 | my $cos_bet = cos $beta; | 
| 368 | 3011 |  |  |  |  | 14039 | my $alpha = mod2pi( atan2( | 
| 369 |  |  |  |  |  |  | $sin_lam * $cos_eps - $sin_bet / $cos_bet * $sin_eps, | 
| 370 |  |  |  |  |  |  | cos $lambda ) );	# Meeus 13.3 | 
| 371 | 3011 |  |  |  |  | 18700 | my $delta = asin( $sin_bet * $cos_eps + $cos_bet * $sin_eps * | 
| 372 |  |  |  |  |  |  | $sin_lam );	# Meeus 13.4 | 
| 373 |  |  |  |  |  |  |  | 
| 374 | 3011 |  |  |  |  | 13033 | DEBUG | 
| 375 |  |  |  |  |  |  | and printf <<'EOD', | 
| 376 |  |  |  |  |  |  |  | 
| 377 |  |  |  |  |  |  | Equatorial coordinates: | 
| 378 |  |  |  |  |  |  | 𝛂 = %.6f | 
| 379 |  |  |  |  |  |  | = %s | 
| 380 |  |  |  |  |  |  | 𝛅 = %.6f | 
| 381 |  |  |  |  |  |  | = %s | 
| 382 |  |  |  |  |  |  | EOD | 
| 383 |  |  |  |  |  |  | rad2deg( $alpha ), rad2hms( $alpha ), | 
| 384 |  |  |  |  |  |  | rad2deg( $delta ), rad2dms( $delta, 2 ); | 
| 385 |  |  |  |  |  |  |  | 
| 386 | 3011 |  |  |  |  | 14964 | $self->equatorial( $alpha, $delta, $Delta * AU ); | 
| 387 | 3011 |  |  |  |  | 527719 | $self->equinox_dynamical( $time ); | 
| 388 |  |  |  |  |  |  |  | 
| 389 | 3011 |  |  |  |  | 18951 | if ( DEBUG ) { | 
| 390 |  |  |  |  |  |  | my ( $lat, $lon ) = $self->ecliptic(); | 
| 391 |  |  |  |  |  |  | printf <<'EOD', $long_sym, | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | Check -- recovered ecliptic coordinates: | 
| 394 |  |  |  |  |  |  | %s = %.5f | 
| 395 |  |  |  |  |  |  | = %s | 
| 396 |  |  |  |  |  |  | 𝛃 = %.5f | 
| 397 |  |  |  |  |  |  | = %s | 
| 398 |  |  |  |  |  |  | EOD | 
| 399 |  |  |  |  |  |  | rad2deg( $lon ), rad2dms( $lon ), | 
| 400 |  |  |  |  |  |  | rad2deg( $lat ), rad2dms( $lat ); | 
| 401 |  |  |  |  |  |  | } | 
| 402 |  |  |  |  |  |  |  | 
| 403 | 3011 |  |  |  |  | 8462 | return $self; | 
| 404 |  |  |  |  |  |  | } | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | { | 
| 407 |  |  |  |  |  |  | my $earth; | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  | # NOTE that the sign of the angle returned is the same as the sign | 
| 410 |  |  |  |  |  |  | # of the result of __longitude_from_sun(). | 
| 411 |  |  |  |  |  |  | sub __angle_subtended_from_earth { | 
| 412 | 246 |  |  | 246 |  | 588 | my ( $self, $time ) = @_; | 
| 413 | 246 | 100 |  |  |  | 630 | defined $time | 
| 414 |  |  |  |  |  |  | or $time = $self->universal(); | 
| 415 | 246 |  | 66 |  |  | 751 | $earth ||= Astro::Coord::ECI->new()->eci( 0, 0, 0 ); | 
| 416 | 246 |  |  |  |  | 977 | $self->universal( $time ); | 
| 417 | 246 |  |  |  |  | 3061 | my $sun = $self->get( 'sun' )->universal( $time ); | 
| 418 | 246 |  |  |  |  | 2958 | $earth->universal( $time ); | 
| 419 | 246 |  |  |  |  | 8346 | my $lon = $self->__longitude_from_sun(); | 
| 420 | 246 |  |  |  |  | 1494 | my $angle = $earth->angle( $self, $sun ); | 
| 421 | 246 | 100 |  |  |  | 84775 | return $lon < 0 ? -$angle : $angle; | 
| 422 |  |  |  |  |  |  | } | 
| 423 |  |  |  |  |  |  | } | 
| 424 |  |  |  |  |  |  |  | 
| 425 |  |  |  |  |  |  | sub geometric_longitude { | 
| 426 | 1 |  |  | 1 | 1 | 14 | my ( $self ) = @_; | 
| 427 | 1 |  |  |  |  | 3 | my $attr = $self->__get_attr(); | 
| 428 |  |  |  |  |  |  | defined $attr->{geometric_longitude} | 
| 429 | 1 | 50 |  |  |  | 5 | and return $attr->{geometric_longitude}; | 
| 430 | 0 |  |  |  |  | 0 | croak 'Geometric longitude undefined -- time not set'; | 
| 431 |  |  |  |  |  |  | } | 
| 432 |  |  |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | sub __longitude_from_sun { | 
| 434 | 1248 |  |  | 1248 |  | 2907 | my ( $self, $time, $offset ) = @_; | 
| 435 | 1248 |  | 100 |  |  | 4297 | $offset ||= 0; | 
| 436 |  |  |  |  |  |  |  | 
| 437 | 1248 | 100 |  |  |  | 2912 | if ( defined $time ) { | 
| 438 | 1002 |  |  |  |  | 2793 | $self->universal( $time ); | 
| 439 |  |  |  |  |  |  | } else { | 
| 440 | 246 |  |  |  |  | 555 | $time = $self->universal(); | 
| 441 |  |  |  |  |  |  | } | 
| 442 |  |  |  |  |  |  |  | 
| 443 | 1248 |  |  |  |  | 17483 | my $sun = $self->get( 'sun' )->universal( $time ); | 
| 444 |  |  |  |  |  |  |  | 
| 445 | 1248 |  |  |  |  | 16476 | my ( undef, $lon_b ) = $self->ecliptic(); | 
| 446 | 1248 |  |  |  |  | 85127 | my ( undef, $lon_s ) = $sun->ecliptic(); | 
| 447 |  |  |  |  |  |  |  | 
| 448 | 1248 |  |  |  |  | 68096 | return mod2pi( $lon_b - $lon_s - $offset + PI ) - PI; | 
| 449 |  |  |  |  |  |  | } | 
| 450 |  |  |  |  |  |  |  | 
| 451 | 0 |  |  |  |  | 0 | BEGIN { | 
| 452 | 13 |  |  | 13 |  | 25501 | my @model = ( | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | # The following are from the IAU SOFA module src/nut80.c, from | 
| 455 |  |  |  |  |  |  | # http://www.iausofa.org/2018_0130_C/sofa_c-20180130.tar.gz | 
| 456 |  |  |  |  |  |  | # The only edit is the change from curly to square brackets and | 
| 457 |  |  |  |  |  |  | # the brute-force conversion of C comments to Perl comments. The | 
| 458 |  |  |  |  |  |  | # columns are: | 
| 459 |  |  |  |  |  |  | #   IAU:  nl nlp  nf  nd nom  sp spt  ce cet | 
| 460 |  |  |  |  |  |  | # Meeus:  M'   M   F   D   𝛀 | 
| 461 |  |  |  |  |  |  | # /* 1-10 */ | 
| 462 |  |  |  |  |  |  | [  0,  0,  0,  0,  1, -171996.0, -174.2,  92025.0,    8.9 ], | 
| 463 |  |  |  |  |  |  | [  0,  0,  0,  0,  2,    2062.0,    0.2,   -895.0,    0.5 ], | 
| 464 |  |  |  |  |  |  | [ -2,  0,  2,  0,  1,      46.0,    0.0,    -24.0,    0.0 ], | 
| 465 |  |  |  |  |  |  | [  2,  0, -2,  0,  0,      11.0,    0.0,      0.0,    0.0 ], | 
| 466 |  |  |  |  |  |  | [ -2,  0,  2,  0,  2,      -3.0,    0.0,      1.0,    0.0 ], | 
| 467 |  |  |  |  |  |  | [  1, -1,  0, -1,  0,      -3.0,    0.0,      0.0,    0.0 ], | 
| 468 |  |  |  |  |  |  | [  0, -2,  2, -2,  1,      -2.0,    0.0,      1.0,    0.0 ], | 
| 469 |  |  |  |  |  |  | [  2,  0, -2,  0,  1,       1.0,    0.0,      0.0,    0.0 ], | 
| 470 |  |  |  |  |  |  | [  0,  0,  2, -2,  2,  -13187.0,   -1.6,   5736.0,   -3.1 ], | 
| 471 |  |  |  |  |  |  | [  0,  1,  0,  0,  0,    1426.0,   -3.4,     54.0,   -0.1 ], | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  | # /* 11-20 */ | 
| 474 |  |  |  |  |  |  | [  0,  1,  2, -2,  2,    -517.0,    1.2,    224.0,   -0.6 ], | 
| 475 |  |  |  |  |  |  | [  0, -1,  2, -2,  2,     217.0,   -0.5,    -95.0,    0.3 ], | 
| 476 |  |  |  |  |  |  | [  0,  0,  2, -2,  1,     129.0,    0.1,    -70.0,    0.0 ], | 
| 477 |  |  |  |  |  |  | [  2,  0,  0, -2,  0,      48.0,    0.0,      1.0,    0.0 ], | 
| 478 |  |  |  |  |  |  | [  0,  0,  2, -2,  0,     -22.0,    0.0,      0.0,    0.0 ], | 
| 479 |  |  |  |  |  |  | [  0,  2,  0,  0,  0,      17.0,   -0.1,      0.0,    0.0 ], | 
| 480 |  |  |  |  |  |  | [  0,  1,  0,  0,  1,     -15.0,    0.0,      9.0,    0.0 ], | 
| 481 |  |  |  |  |  |  | [  0,  2,  2, -2,  2,     -16.0,    0.1,      7.0,    0.0 ], | 
| 482 |  |  |  |  |  |  | [  0, -1,  0,  0,  1,     -12.0,    0.0,      6.0,    0.0 ], | 
| 483 |  |  |  |  |  |  | [ -2,  0,  0,  2,  1,      -6.0,    0.0,      3.0,    0.0 ], | 
| 484 |  |  |  |  |  |  |  | 
| 485 |  |  |  |  |  |  | # /* 21-30 */ | 
| 486 |  |  |  |  |  |  | [  0, -1,  2, -2,  1,      -5.0,    0.0,      3.0,    0.0 ], | 
| 487 |  |  |  |  |  |  | [  2,  0,  0, -2,  1,       4.0,    0.0,     -2.0,    0.0 ], | 
| 488 |  |  |  |  |  |  | [  0,  1,  2, -2,  1,       4.0,    0.0,     -2.0,    0.0 ], | 
| 489 |  |  |  |  |  |  | [  1,  0,  0, -1,  0,      -4.0,    0.0,      0.0,    0.0 ], | 
| 490 |  |  |  |  |  |  | [  2,  1,  0, -2,  0,       1.0,    0.0,      0.0,    0.0 ], | 
| 491 |  |  |  |  |  |  | [  0,  0, -2,  2,  1,       1.0,    0.0,      0.0,    0.0 ], | 
| 492 |  |  |  |  |  |  | [  0,  1, -2,  2,  0,      -1.0,    0.0,      0.0,    0.0 ], | 
| 493 |  |  |  |  |  |  | [  0,  1,  0,  0,  2,       1.0,    0.0,      0.0,    0.0 ], | 
| 494 |  |  |  |  |  |  | [ -1,  0,  0,  1,  1,       1.0,    0.0,      0.0,    0.0 ], | 
| 495 |  |  |  |  |  |  | [  0,  1,  2, -2,  0,      -1.0,    0.0,      0.0,    0.0 ], | 
| 496 |  |  |  |  |  |  |  | 
| 497 |  |  |  |  |  |  | # /* 31-40 */ | 
| 498 |  |  |  |  |  |  | [  0,  0,  2,  0,  2,   -2274.0,   -0.2,    977.0,   -0.5 ], | 
| 499 |  |  |  |  |  |  | [  1,  0,  0,  0,  0,     712.0,    0.1,     -7.0,    0.0 ], | 
| 500 |  |  |  |  |  |  | [  0,  0,  2,  0,  1,    -386.0,   -0.4,    200.0,    0.0 ], | 
| 501 |  |  |  |  |  |  | [  1,  0,  2,  0,  2,    -301.0,    0.0,    129.0,   -0.1 ], | 
| 502 |  |  |  |  |  |  | [  1,  0,  0, -2,  0,    -158.0,    0.0,     -1.0,    0.0 ], | 
| 503 |  |  |  |  |  |  | [ -1,  0,  2,  0,  2,     123.0,    0.0,    -53.0,    0.0 ], | 
| 504 |  |  |  |  |  |  | [  0,  0,  0,  2,  0,      63.0,    0.0,     -2.0,    0.0 ], | 
| 505 |  |  |  |  |  |  | [  1,  0,  0,  0,  1,      63.0,    0.1,    -33.0,    0.0 ], | 
| 506 |  |  |  |  |  |  | [ -1,  0,  0,  0,  1,     -58.0,   -0.1,     32.0,    0.0 ], | 
| 507 |  |  |  |  |  |  | [ -1,  0,  2,  2,  2,     -59.0,    0.0,     26.0,    0.0 ], | 
| 508 |  |  |  |  |  |  |  | 
| 509 |  |  |  |  |  |  | # /* 41-50 */ | 
| 510 |  |  |  |  |  |  | [  1,  0,  2,  0,  1,     -51.0,    0.0,     27.0,    0.0 ], | 
| 511 |  |  |  |  |  |  | [  0,  0,  2,  2,  2,     -38.0,    0.0,     16.0,    0.0 ], | 
| 512 |  |  |  |  |  |  | [  2,  0,  0,  0,  0,      29.0,    0.0,     -1.0,    0.0 ], | 
| 513 |  |  |  |  |  |  | [  1,  0,  2, -2,  2,      29.0,    0.0,    -12.0,    0.0 ], | 
| 514 |  |  |  |  |  |  | [  2,  0,  2,  0,  2,     -31.0,    0.0,     13.0,    0.0 ], | 
| 515 |  |  |  |  |  |  | [  0,  0,  2,  0,  0,      26.0,    0.0,     -1.0,    0.0 ], | 
| 516 |  |  |  |  |  |  | [ -1,  0,  2,  0,  1,      21.0,    0.0,    -10.0,    0.0 ], | 
| 517 |  |  |  |  |  |  | [ -1,  0,  0,  2,  1,      16.0,    0.0,     -8.0,    0.0 ], | 
| 518 |  |  |  |  |  |  | [  1,  0,  0, -2,  1,     -13.0,    0.0,      7.0,    0.0 ], | 
| 519 |  |  |  |  |  |  | [ -1,  0,  2,  2,  1,     -10.0,    0.0,      5.0,    0.0 ], | 
| 520 |  |  |  |  |  |  |  | 
| 521 |  |  |  |  |  |  | # /* 51-60 */ | 
| 522 |  |  |  |  |  |  | [  1,  1,  0, -2,  0,      -7.0,    0.0,      0.0,    0.0 ], | 
| 523 |  |  |  |  |  |  | [  0,  1,  2,  0,  2,       7.0,    0.0,     -3.0,    0.0 ], | 
| 524 |  |  |  |  |  |  | [  0, -1,  2,  0,  2,      -7.0,    0.0,      3.0,    0.0 ], | 
| 525 |  |  |  |  |  |  | [  1,  0,  2,  2,  2,      -8.0,    0.0,      3.0,    0.0 ], | 
| 526 |  |  |  |  |  |  | [  1,  0,  0,  2,  0,       6.0,    0.0,      0.0,    0.0 ], | 
| 527 |  |  |  |  |  |  | [  2,  0,  2, -2,  2,       6.0,    0.0,     -3.0,    0.0 ], | 
| 528 |  |  |  |  |  |  | [  0,  0,  0,  2,  1,      -6.0,    0.0,      3.0,    0.0 ], | 
| 529 |  |  |  |  |  |  | [  0,  0,  2,  2,  1,      -7.0,    0.0,      3.0,    0.0 ], | 
| 530 |  |  |  |  |  |  | [  1,  0,  2, -2,  1,       6.0,    0.0,     -3.0,    0.0 ], | 
| 531 |  |  |  |  |  |  | [  0,  0,  0, -2,  1,      -5.0,    0.0,      3.0,    0.0 ], | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | # /* 61-70 */ | 
| 534 |  |  |  |  |  |  | [  1, -1,  0,  0,  0,       5.0,    0.0,      0.0,    0.0 ], | 
| 535 |  |  |  |  |  |  | [  2,  0,  2,  0,  1,      -5.0,    0.0,      3.0,    0.0 ], | 
| 536 |  |  |  |  |  |  | [  0,  1,  0, -2,  0,      -4.0,    0.0,      0.0,    0.0 ], | 
| 537 |  |  |  |  |  |  | [  1,  0, -2,  0,  0,       4.0,    0.0,      0.0,    0.0 ], | 
| 538 |  |  |  |  |  |  | [  0,  0,  0,  1,  0,      -4.0,    0.0,      0.0,    0.0 ], | 
| 539 |  |  |  |  |  |  | [  1,  1,  0,  0,  0,      -3.0,    0.0,      0.0,    0.0 ], | 
| 540 |  |  |  |  |  |  | [  1,  0,  2,  0,  0,       3.0,    0.0,      0.0,    0.0 ], | 
| 541 |  |  |  |  |  |  | [  1, -1,  2,  0,  2,      -3.0,    0.0,      1.0,    0.0 ], | 
| 542 |  |  |  |  |  |  | [ -1, -1,  2,  2,  2,      -3.0,    0.0,      1.0,    0.0 ], | 
| 543 |  |  |  |  |  |  | [ -2,  0,  0,  0,  1,      -2.0,    0.0,      1.0,    0.0 ], | 
| 544 |  |  |  |  |  |  |  | 
| 545 |  |  |  |  |  |  | # /* 71-80 */ | 
| 546 |  |  |  |  |  |  | [  3,  0,  2,  0,  2,      -3.0,    0.0,      1.0,    0.0 ], | 
| 547 |  |  |  |  |  |  | [  0, -1,  2,  2,  2,      -3.0,    0.0,      1.0,    0.0 ], | 
| 548 |  |  |  |  |  |  | [  1,  1,  2,  0,  2,       2.0,    0.0,     -1.0,    0.0 ], | 
| 549 |  |  |  |  |  |  | [ -1,  0,  2, -2,  1,      -2.0,    0.0,      1.0,    0.0 ], | 
| 550 |  |  |  |  |  |  | [  2,  0,  0,  0,  1,       2.0,    0.0,     -1.0,    0.0 ], | 
| 551 |  |  |  |  |  |  | [  1,  0,  0,  0,  2,      -2.0,    0.0,      1.0,    0.0 ], | 
| 552 |  |  |  |  |  |  | [  3,  0,  0,  0,  0,       2.0,    0.0,      0.0,    0.0 ], | 
| 553 |  |  |  |  |  |  | [  0,  0,  2,  1,  2,       2.0,    0.0,     -1.0,    0.0 ], | 
| 554 |  |  |  |  |  |  | [ -1,  0,  0,  0,  2,       1.0,    0.0,     -1.0,    0.0 ], | 
| 555 |  |  |  |  |  |  | [  1,  0,  0, -4,  0,      -1.0,    0.0,      0.0,    0.0 ], | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | # /* 81-90 */ | 
| 558 |  |  |  |  |  |  | [ -2,  0,  2,  2,  2,       1.0,    0.0,     -1.0,    0.0 ], | 
| 559 |  |  |  |  |  |  | [ -1,  0,  2,  4,  2,      -2.0,    0.0,      1.0,    0.0 ], | 
| 560 |  |  |  |  |  |  | [  2,  0,  0, -4,  0,      -1.0,    0.0,      0.0,    0.0 ], | 
| 561 |  |  |  |  |  |  | [  1,  1,  2, -2,  2,       1.0,    0.0,     -1.0,    0.0 ], | 
| 562 |  |  |  |  |  |  | [  1,  0,  2,  2,  1,      -1.0,    0.0,      1.0,    0.0 ], | 
| 563 |  |  |  |  |  |  | [ -2,  0,  2,  4,  2,      -1.0,    0.0,      1.0,    0.0 ], | 
| 564 |  |  |  |  |  |  | [ -1,  0,  4,  0,  2,       1.0,    0.0,      0.0,    0.0 ], | 
| 565 |  |  |  |  |  |  | [  1, -1,  0, -2,  0,       1.0,    0.0,      0.0,    0.0 ], | 
| 566 |  |  |  |  |  |  | [  2,  0,  2, -2,  1,       1.0,    0.0,     -1.0,    0.0 ], | 
| 567 |  |  |  |  |  |  | [  2,  0,  2,  2,  2,      -1.0,    0.0,      0.0,    0.0 ], | 
| 568 |  |  |  |  |  |  |  | 
| 569 |  |  |  |  |  |  | # /* 91-100 */ | 
| 570 |  |  |  |  |  |  | [  1,  0,  0,  2,  1,      -1.0,    0.0,      0.0,    0.0 ], | 
| 571 |  |  |  |  |  |  | [  0,  0,  4, -2,  2,       1.0,    0.0,      0.0,    0.0 ], | 
| 572 |  |  |  |  |  |  | [  3,  0,  2, -2,  2,       1.0,    0.0,      0.0,    0.0 ], | 
| 573 |  |  |  |  |  |  | [  1,  0,  2, -2,  0,      -1.0,    0.0,      0.0,    0.0 ], | 
| 574 |  |  |  |  |  |  | [  0,  1,  2,  0,  1,       1.0,    0.0,      0.0,    0.0 ], | 
| 575 |  |  |  |  |  |  | [ -1, -1,  0,  2,  1,       1.0,    0.0,      0.0,    0.0 ], | 
| 576 |  |  |  |  |  |  | [  0,  0, -2,  0,  1,      -1.0,    0.0,      0.0,    0.0 ], | 
| 577 |  |  |  |  |  |  | [  0,  0,  2, -1,  2,      -1.0,    0.0,      0.0,    0.0 ], | 
| 578 |  |  |  |  |  |  | [  0,  1,  0,  2,  0,      -1.0,    0.0,      0.0,    0.0 ], | 
| 579 |  |  |  |  |  |  | [  1,  0, -2, -2,  0,      -1.0,    0.0,      0.0,    0.0 ], | 
| 580 |  |  |  |  |  |  |  | 
| 581 |  |  |  |  |  |  | # /* 101-106 */ | 
| 582 |  |  |  |  |  |  | [  0, -1,  2,  0,  1,      -1.0,    0.0,      0.0,    0.0 ], | 
| 583 |  |  |  |  |  |  | [  1,  1,  0, -2,  1,      -1.0,    0.0,      0.0,    0.0 ], | 
| 584 |  |  |  |  |  |  | [  1,  0, -2,  2,  0,      -1.0,    0.0,      0.0,    0.0 ], | 
| 585 |  |  |  |  |  |  | [  2,  0,  0,  2,  0,       1.0,    0.0,      0.0,    0.0 ], | 
| 586 |  |  |  |  |  |  | [  0,  0,  2,  4,  2,      -1.0,    0.0,      0.0,    0.0 ], | 
| 587 |  |  |  |  |  |  | [  0,  1,  0,  1,  0,       1.0,    0.0,      0.0,    0.0 ] | 
| 588 |  |  |  |  |  |  | ); | 
| 589 |  |  |  |  |  |  |  | 
| 590 |  |  |  |  |  |  | # UNDOCUMENTED AND UNSUPPORTED -- MAY BE CHANGED OR REVOKED WITHOUT | 
| 591 |  |  |  |  |  |  | # WARNING | 
| 592 |  |  |  |  |  |  | # Return the IAU 1980 nutation model, as array references. Each | 
| 593 |  |  |  |  |  |  | # reference contains one term of the model, in the following order | 
| 594 |  |  |  |  |  |  | # in Meeus' terminology: | 
| 595 |  |  |  |  |  |  | # M' M F D Omega delta psi( const, T ), delta # epsilon( const, T ). | 
| 596 |  |  |  |  |  |  | sub __iau1980_nutation_model { | 
| 597 | 0 |  |  | 0 |  | 0 | return @model; | 
| 598 |  |  |  |  |  |  | } | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | # This may be a premature optimization, BUT the nutation in | 
| 601 |  |  |  |  |  |  | # longitude and obliquity are used different places, so I anticipate | 
| 602 |  |  |  |  |  |  | # this getting called twice by time_set(), with the same time. So: | 
| 603 | 13 |  |  |  |  | 60 | my $memoize_args = ''; | 
| 604 | 13 |  |  |  |  | 20730 | my @memoize_result; | 
| 605 |  |  |  |  |  |  |  | 
| 606 |  |  |  |  |  |  | sub nutation { | 
| 607 | 8281 |  |  | 8281 | 1 | 17050 | my ( $self, $time, $cutoff ) = @_; | 
| 608 |  |  |  |  |  |  |  | 
| 609 | 8281 | 50 |  |  |  | 17947 | defined $time | 
| 610 |  |  |  |  |  |  | or $time = $self->dynamical(); | 
| 611 |  |  |  |  |  |  |  | 
| 612 | 8281 |  | 50 |  |  | 33274 | $cutoff ||= 0;	# milli arc seconds. Meeus is 3 | 
| 613 |  |  |  |  |  |  |  | 
| 614 |  |  |  |  |  |  | { | 
| 615 | 8281 | 100 |  |  |  | 11553 | ( my $memo = "$time $cutoff" ) eq $memoize_args | 
|  | 8281 |  |  |  |  | 66548 |  | 
| 616 |  |  |  |  |  |  | and return @memoize_result; | 
| 617 | 1943 |  |  |  |  | 4058 | $memoize_args = $memo; | 
| 618 |  |  |  |  |  |  | } | 
| 619 |  |  |  |  |  |  |  | 
| 620 | 1943 |  |  |  |  | 5508 | my $T = jcent2000( $time );	# Meeus 22.1 | 
| 621 |  |  |  |  |  |  |  | 
| 622 |  |  |  |  |  |  | # All this from Meeus Ch 22 pp 143ff, expressed in degrees | 
| 623 |  |  |  |  |  |  |  | 
| 624 |  |  |  |  |  |  | # Mean elongation of Moon from Sun | 
| 625 | 1943 |  |  |  |  | 13156 | my $D = ( ( $T / 189_474 - 0.001_914_2 ) * $T + 445_267.111_480 | 
| 626 |  |  |  |  |  |  | ) * $T + 297.850_36; | 
| 627 |  |  |  |  |  |  |  | 
| 628 |  |  |  |  |  |  | # Mean anomaly of the Sun (Earth) | 
| 629 | 1943 |  |  |  |  | 4972 | my $M = ( ( - $T / 300_000 - 0.000_160_3 ) * $T + 35_999.050_340 | 
| 630 |  |  |  |  |  |  | ) * $T + 357.527_72; | 
| 631 |  |  |  |  |  |  |  | 
| 632 |  |  |  |  |  |  | # Mean anomaly of the Moon | 
| 633 | 1943 |  |  |  |  | 4174 | my $M_prime = ( ( $T / 56_250 + 0.008_697_2 ) * $T + 477_198.867_398 | 
| 634 |  |  |  |  |  |  | ) * $T + 134.962_98; | 
| 635 |  |  |  |  |  |  |  | 
| 636 |  |  |  |  |  |  | # Moon's argument of latitude | 
| 637 | 1943 |  |  |  |  | 4374 | my $F = ( ( $T / 327_270 - 0.003_682_5 ) * $T + 483_202.017_538 | 
| 638 |  |  |  |  |  |  | ) * $T + 93.271_91; | 
| 639 |  |  |  |  |  |  |  | 
| 640 |  |  |  |  |  |  | # Logitude of the ascending node of the Moon's mean orbit on the | 
| 641 |  |  |  |  |  |  | # ecliptic, measured from the equinox of the date | 
| 642 | 1943 |  |  |  |  | 4110 | my $Omega = ( ( $T / 450_000 + 0.002_070_8 ) * $T - 1_934.136_261 | 
| 643 |  |  |  |  |  |  | ) * $T + 125.044_52; | 
| 644 |  |  |  |  |  |  |  | 
| 645 |  |  |  |  |  |  | # Convert to radians, and store in same order as model source | 
| 646 | 1943 |  |  |  |  | 4894 | my @arg = map { deg2rad( $_ ) } ( $M_prime, $M, $F, $D, $Omega ); | 
|  | 9715 |  |  |  |  | 31810 |  | 
| 647 |  |  |  |  |  |  |  | 
| 648 | 1943 |  |  |  |  | 10775 | my ( $delta_psi, $delta_eps ) = ( 0, 0 ); | 
| 649 | 1943 |  |  |  |  | 4534 | foreach my $row ( @model ) { | 
| 650 | 205958 |  |  |  |  | 379675 | my $a = $arg[0] * $row->[0] + $arg[1] * $row->[1] + | 
| 651 |  |  |  |  |  |  | $arg[2] * $row->[2] + $arg[3] * $row->[3] + | 
| 652 |  |  |  |  |  |  | $arg[4] * $row->[4]; | 
| 653 | 205958 | 50 | 33 |  |  | 537693 | if ( $row->[5] && $cutoff <= abs $row->[5] ) { | 
| 654 | 205958 |  |  |  |  | 339675 | $delta_psi += ( $row->[6] * $T + $row->[5] ) * sin $a; | 
| 655 |  |  |  |  |  |  | } | 
| 656 | 205958 | 100 | 66 |  |  | 488645 | if ( $row->[7] && $cutoff <= abs $row->[7] ) { | 
| 657 | 124352 |  |  |  |  | 227368 | $delta_eps += ( $row->[8] * $T + $row->[7] ) * cos $a; | 
| 658 |  |  |  |  |  |  | } | 
| 659 |  |  |  |  |  |  |  | 
| 660 |  |  |  |  |  |  | } | 
| 661 |  |  |  |  |  |  |  | 
| 662 |  |  |  |  |  |  | # The model computes nutations in milli arc seconds, but we | 
| 663 |  |  |  |  |  |  | # return them in radians | 
| 664 |  |  |  |  |  |  | return ( @memoize_result = | 
| 665 | 1943 |  |  |  |  | 4355 | map { deg2rad( $_ / 3600_0000 ) } $delta_psi, $delta_eps ); | 
|  | 3886 |  |  |  |  | 16266 |  | 
| 666 |  |  |  |  |  |  | } | 
| 667 |  |  |  |  |  |  |  | 
| 668 |  |  |  |  |  |  | } | 
| 669 |  |  |  |  |  |  |  | 
| 670 |  |  |  |  |  |  | sub __get_attr { | 
| 671 | 15180 |  |  | 15180 |  | 24604 | my ( $self ) = @_; | 
| 672 | 15180 | 50 |  |  |  | 34227 | my $ref = ref $self | 
| 673 |  |  |  |  |  |  | or confess 'Can not call as static method'; | 
| 674 | 15180 |  | 100 |  |  | 44936 | return $self->{$ref} ||= { | 
| 675 |  |  |  |  |  |  | model_cutoff_definition	=> dclone( $self->__model_definition( | 
| 676 |  |  |  |  |  |  | 'default_model_cutoff' ) ), | 
| 677 |  |  |  |  |  |  | }; | 
| 678 |  |  |  |  |  |  | } | 
| 679 |  |  |  |  |  |  |  | 
| 680 |  |  |  |  |  |  | sub __default { | 
| 681 | 27 |  |  | 27 |  | 80 | my ( $class, $arg ) = @_; | 
| 682 |  |  |  |  |  |  |  | 
| 683 | 27 |  |  |  |  | 123 | my $name = $class->__model_definition( 'body' ); | 
| 684 |  |  |  |  |  |  | defined $arg->{id} | 
| 685 | 27 | 50 |  |  |  | 249 | or $arg->{id} = $name; | 
| 686 |  |  |  |  |  |  | defined $arg->{name} | 
| 687 | 27 | 50 |  |  |  | 113 | or $arg->{name} = $name; | 
| 688 |  |  |  |  |  |  |  | 
| 689 |  |  |  |  |  |  | defined $arg->{diameter} | 
| 690 | 27 | 50 |  |  |  | 241 | or $arg->{diameter} = $class->__model_definition( 'diameter' ); | 
| 691 |  |  |  |  |  |  |  | 
| 692 |  |  |  |  |  |  | defined $arg->{model_cutoff} | 
| 693 | 27 | 100 |  |  |  | 184 | or $arg->{model_cutoff} = 'Meeus'; | 
| 694 |  |  |  |  |  |  |  | 
| 695 |  |  |  |  |  |  | defined $arg->{nutation_cutoff} | 
| 696 | 27 | 50 |  |  |  | 92 | or $arg->{nutation_cutoff} = 3; | 
| 697 |  |  |  |  |  |  |  | 
| 698 | 27 |  |  |  |  | 139 | return; | 
| 699 |  |  |  |  |  |  | } | 
| 700 |  |  |  |  |  |  |  | 
| 701 |  |  |  |  |  |  | sub __mutate_model_cutoff { | 
| 702 | 28 |  |  | 28 |  | 99 | my ( $self, $name, $val ) = @_; | 
| 703 | 28 | 50 |  |  |  | 92 | defined $val | 
| 704 |  |  |  |  |  |  | or croak "model cutoff must be defined"; | 
| 705 | 28 | 50 |  |  |  | 164 | $self->model_cutoff_definition( $val ) | 
| 706 |  |  |  |  |  |  | or croak "model cutoff '$val' is unknown"; | 
| 707 | 28 |  |  |  |  | 116 | $self->__get_attr()->{$name} = $val; | 
| 708 | 28 |  |  |  |  | 113 | return $self; | 
| 709 |  |  |  |  |  |  | } | 
| 710 |  |  |  |  |  |  |  | 
| 711 |  |  |  |  |  |  | sub model_cutoff_definition { | 
| 712 | 6066 |  |  | 6066 | 1 | 13499 | my ( $self, $name, @arg ) = @_; | 
| 713 | 6066 | 100 |  |  |  | 16741 | defined $name | 
| 714 |  |  |  |  |  |  | or $name = $self->get( 'model_cutoff' ); | 
| 715 | 6066 |  |  |  |  | 13325 | my $attr = $self->__get_attr(); | 
| 716 | 6066 | 100 |  |  |  | 14062 | if ( @arg ) { | 
| 717 | 6 | 100 |  |  |  | 20 | if ( defined( my $val = $arg[0] ) ) { | 
| 718 | 4 | 100 |  |  |  | 16 | unless ( ref $val ) { | 
| 719 | 2 | 50 | 33 |  |  | 63 | looks_like_number( $val ) | 
| 720 |  |  |  |  |  |  | and $val !~ m/ \A Inf (?: inity )? | NaN \z /smx | 
| 721 |  |  |  |  |  |  | or croak 'Scalar model cutoff definition must be a number'; | 
| 722 | 2 |  |  |  |  | 6 | my $num = $val; | 
| 723 |  |  |  |  |  |  | $val = sub { | 
| 724 | 2 |  |  | 2 |  | 7 | my ( @model ) = @_; | 
| 725 | 2 |  |  |  |  | 4 | my %cutoff; | 
| 726 | 2 |  |  |  |  | 6 | foreach my $series ( @model ) { | 
| 727 | 34 |  |  |  |  | 48 | my $count = 0; | 
| 728 | 34 |  |  |  |  | 48 | foreach my $term ( @{ $series->{terms} } ) { | 
|  | 34 |  |  |  |  | 51 |  | 
| 729 | 308 | 100 |  |  |  | 639 | last if $term->[0] < $num; | 
| 730 | 274 |  |  |  |  | 346 | $count++; | 
| 731 |  |  |  |  |  |  | } | 
| 732 |  |  |  |  |  |  | $count | 
| 733 | 34 | 100 |  |  |  | 76 | and $cutoff{$series->{series}} = $count; | 
| 734 |  |  |  |  |  |  | } | 
| 735 | 2 |  |  |  |  | 9 | return \%cutoff; | 
| 736 | 2 |  |  |  |  | 16 | }; | 
| 737 |  |  |  |  |  |  | } | 
| 738 | 4 | 100 |  |  |  | 19 | if ( CODE_REF eq ref $val ) { | 
| 739 |  |  |  |  |  |  | $val = $val->( | 
| 740 | 9 |  |  |  |  | 19 | map { @{ $_ } } @{ | 
|  | 9 |  |  |  |  | 34 |  | 
| 741 | 3 |  |  |  |  | 9 | $self->__model_definition( 'model' ) } | 
|  | 3 |  |  |  |  | 13 |  | 
| 742 |  |  |  |  |  |  | ); | 
| 743 | 3 |  |  |  |  | 1025 | $val->{name} = $name; | 
| 744 |  |  |  |  |  |  | } | 
| 745 | 4 | 50 |  |  |  | 21 | HASH_REF eq ref $val | 
| 746 |  |  |  |  |  |  | or croak 'The model cutoff definition value must be a hash ref'; | 
| 747 |  |  |  |  |  |  | my $terms = $self->__model_definition( | 
| 748 | 4 |  |  |  |  | 21 | 'default_model_cutoff')->{none}; | 
| 749 | 4 |  |  |  |  | 30 | foreach my $name ( keys %{ $val } ) { | 
|  | 4 |  |  |  |  | 36 |  | 
| 750 | 44 | 100 |  |  |  | 84 | 'name' eq $name | 
| 751 |  |  |  |  |  |  | and next; | 
| 752 | 40 | 50 |  |  |  | 75 | exists $terms->{$name} | 
| 753 |  |  |  |  |  |  | or croak "Series '$name' not in this model"; | 
| 754 | 40 | 50 |  |  |  | 139 | $val->{$name} > $terms->{$name} | 
| 755 |  |  |  |  |  |  | and croak "Series '$name' has only $terms->{$name} terms"; | 
| 756 |  |  |  |  |  |  | } | 
| 757 | 4 |  |  |  |  | 21 | $attr->{model_cutoff_definition}{$name} = $val; | 
| 758 |  |  |  |  |  |  | } else { | 
| 759 | 2 | 50 |  |  |  | 10 | $self->__model_definition( 'default_model_cutoff' )->{$name} | 
| 760 |  |  |  |  |  |  | and croak "You may not delete model cutoff definition '$name'"; | 
| 761 | 2 |  |  |  |  | 19 | delete $attr->{model_cutoff_definition}{$name}; | 
| 762 |  |  |  |  |  |  | } | 
| 763 | 6 |  |  |  |  | 33 | return $self; | 
| 764 |  |  |  |  |  |  | } else { | 
| 765 | 6060 |  |  |  |  | 18194 | return $attr->{model_cutoff_definition}{$name}; | 
| 766 |  |  |  |  |  |  | } | 
| 767 |  |  |  |  |  |  | } | 
| 768 |  |  |  |  |  |  |  | 
| 769 |  |  |  |  |  |  | # Static method | 
| 770 |  |  |  |  |  |  | # Given dynamical time in seconds, return Ecliptic position in Ecliptic | 
| 771 |  |  |  |  |  |  | # longitude (radians), Ecliptic latitude (radians), Range (AU) and | 
| 772 |  |  |  |  |  |  | # AU and velocity in AU/day | 
| 773 |  |  |  |  |  |  | sub __model { | 
| 774 | 6834 |  |  | 6834 |  | 281233 | my ( $self, $time, %arg ) = @_; | 
| 775 |  |  |  |  |  |  |  | 
| 776 |  |  |  |  |  |  | DEBUG | 
| 777 |  |  |  |  |  |  | and printf <<'EOD', | 
| 778 |  |  |  |  |  |  |  | 
| 779 |  |  |  |  |  |  | __model: | 
| 780 |  |  |  |  |  |  | invocant: %s | 
| 781 |  |  |  |  |  |  | model_cutoff: %s | 
| 782 |  |  |  |  |  |  | EOD | 
| 783 |  |  |  |  |  |  | $self, | 
| 784 |  |  |  |  |  |  | ( $arg{model_cutoff_definition} ? | 
| 785 | 6834 |  |  |  |  | 10476 | ( $arg{model_cutoff_definition}{name} || '' ) : | 
| 786 |  |  |  |  |  |  | '' ), | 
| 787 |  |  |  |  |  |  | ; | 
| 788 |  |  |  |  |  |  |  | 
| 789 | 6834 |  |  |  |  | 17921 | my $jm = jcent2000( $time ) / 10;	# Meeus 32.1 | 
| 790 | 6834 |  |  |  |  | 38913 | my @p_vec; | 
| 791 |  |  |  |  |  |  | my @v_vec; | 
| 792 | 6834 |  |  |  |  | 11287 | foreach my $coord ( @{ $self->__model_definition( 'model' ) } ) { | 
|  | 6834 |  |  |  |  | 26357 |  | 
| 793 | 20502 |  |  |  |  | 45500 | my $dT = my $exponent = 0; | 
| 794 | 20502 |  |  |  |  | 29050 | my $T = 1; | 
| 795 | 20502 |  |  |  |  | 29652 | my $pos = my $vel = 0; | 
| 796 | 20502 |  |  |  |  | 28462 | foreach my $series ( @{ $coord } ) { | 
|  | 20502 |  |  |  |  | 36893 |  | 
| 797 |  |  |  |  |  |  | my $limit = $arg{model_cutoff_definition} ? | 
| 798 |  |  |  |  |  |  | ( $arg{model_cutoff_definition}{$series->{series}} || 0 ) : | 
| 799 | 118569 | 100 | 100 |  |  | 384387 | @{ $series->{terms} } | 
|  | 560 | 100 |  |  |  | 1578 |  | 
| 800 |  |  |  |  |  |  | or next; | 
| 801 | 100172 |  |  |  |  | 130223 | --$limit; | 
| 802 | 100172 |  |  |  |  | 164419 | foreach my $inx ( 0 .. $limit ) { | 
| 803 | 1828592 |  |  |  |  | 2577590 | my $term = $series->{terms}[$inx]; | 
| 804 | 1828592 |  |  |  |  | 2715088 | my $u = $term->[1] + $term->[2] * $jm; | 
| 805 | 1828592 |  |  |  |  | 2553702 | my $cos_u = cos $u; | 
| 806 | 1828592 |  |  |  |  | 2554375 | $pos += $term->[0] * $cos_u * $T; | 
| 807 | 1828592 |  |  |  |  | 2469931 | my $sin_u = sin $u; | 
| 808 | 1828592 |  |  |  |  | 3471996 | $vel += $dT * $exponent * $term->[0] * $cos_u - | 
| 809 |  |  |  |  |  |  | $T * $term->[0] * $term->[2] * $sin_u; | 
| 810 |  |  |  |  |  |  | } | 
| 811 | 100172 |  |  |  |  | 133763 | $dT = $T; | 
| 812 | 100172 |  |  |  |  | 128971 | $exponent++; | 
| 813 | 100172 |  |  |  |  | 150252 | $T *= $jm; | 
| 814 |  |  |  |  |  |  | } | 
| 815 | 20502 |  |  |  |  | 34981 | $vel /= DAYS_PER_JULIAN_MILENNIUM;	# units/millennium -> units/day | 
| 816 | 20502 |  |  |  |  | 37635 | push @p_vec, $pos; | 
| 817 | 20502 |  |  |  |  | 37239 | push @v_vec, $vel; | 
| 818 |  |  |  |  |  |  | } | 
| 819 | 6834 |  |  |  |  | 3846698 | $p_vec[0] = mod2pi( $p_vec[0] ); | 
| 820 | 6834 |  |  |  |  | 86901 | return ( @p_vec, @v_vec ); | 
| 821 |  |  |  |  |  |  | } | 
| 822 |  |  |  |  |  |  |  | 
| 823 |  |  |  |  |  |  | sub __mutate_nutation_cutoff { | 
| 824 | 27 |  |  | 27 |  | 99 | my ( $self, undef, $val ) = @_; | 
| 825 | 27 | 50 |  |  |  | 93 | defined $val | 
| 826 |  |  |  |  |  |  | or croak 'Nutation cutoff must be defined'; | 
| 827 | 27 | 50 | 33 |  |  | 210 | looks_like_number( $val ) | 
| 828 |  |  |  |  |  |  | and $val >= 0 | 
| 829 |  |  |  |  |  |  | or croak 'Nutation cutoff must be a non-negative number'; | 
| 830 | 27 |  |  |  |  | 101 | $self->__get_attr()->{nutation_cutoff} = $val; | 
| 831 | 27 |  |  |  |  | 137 | return $self; | 
| 832 |  |  |  |  |  |  | } | 
| 833 |  |  |  |  |  |  |  | 
| 834 |  |  |  |  |  |  | sub obliquity { | 
| 835 | 5270 |  |  | 5270 | 1 | 154609 | my ( $self, $time ) = @_; | 
| 836 |  |  |  |  |  |  |  | 
| 837 | 5270 | 100 |  |  |  | 14082 | defined $time | 
| 838 |  |  |  |  |  |  | or $time = $self->dynamical(); | 
| 839 |  |  |  |  |  |  | # Obliquity per Meeus 22.3 | 
| 840 | 5270 |  |  |  |  | 27768 | my $U = jcent2000( $time ) / 100; | 
| 841 | 5270 |  |  |  |  | 29730 | my $epsilon_0 = 0; | 
| 842 | 5270 |  |  |  |  | 24063 | $epsilon_0 = $epsilon_0 * $U + $_ for qw{ 2.45 5.79 27.87 7.12 | 
| 843 |  |  |  |  |  |  | -39.05 -249.67 -51.38 1999.25 -1.55 -4680.93 84381.448 }; | 
| 844 | 5270 |  |  |  |  | 13102 | $epsilon_0 = deg2rad( $epsilon_0 / 3600 ); | 
| 845 | 5270 |  |  |  |  | 24405 | my ( undef, $delta_eps ) = $self->nutation( $time ); | 
| 846 | 5270 |  |  |  |  | 10855 | my $epsilon = $epsilon_0 + $delta_eps; | 
| 847 |  |  |  |  |  |  |  | 
| 848 | 5270 |  |  |  |  | 7749 | DEBUG | 
| 849 |  |  |  |  |  |  | and printf <<"EOD", | 
| 850 |  |  |  |  |  |  |  | 
| 851 |  |  |  |  |  |  | Obliquity: | 
| 852 |  |  |  |  |  |  | 𝛆₀ = %.7f | 
| 853 |  |  |  |  |  |  | 𝛆 = %.7f | 
| 854 |  |  |  |  |  |  | = %s | 
| 855 |  |  |  |  |  |  | EOD | 
| 856 |  |  |  |  |  |  | rad2deg( $epsilon_0 ), rad2deg( $epsilon ), rad2dms( $epsilon ); | 
| 857 |  |  |  |  |  |  |  | 
| 858 | 5270 |  |  |  |  | 10522 | return $epsilon; | 
| 859 |  |  |  |  |  |  | } | 
| 860 |  |  |  |  |  |  |  | 
| 861 |  |  |  |  |  |  | sub order { | 
| 862 | 0 |  |  | 0 | 1 | 0 | my ( $self ) = @_; | 
| 863 | 0 |  |  |  |  | 0 | return $self->__model_definition( 'order' ); | 
| 864 |  |  |  |  |  |  | } | 
| 865 |  |  |  |  |  |  |  | 
| 866 |  |  |  |  |  |  | # Calculate the period of the orbit. The orbital velocity in radians per | 
| 867 |  |  |  |  |  |  | # Julian centutu is just the inverse of the coefficient of the constant | 
| 868 |  |  |  |  |  |  | # L1 term -- that is, the A value of the one with B and C both zero, | 
| 869 |  |  |  |  |  |  | # since that computes as A * cos( B + C * tau ) = A * cos( 0 ) = A | 
| 870 |  |  |  |  |  |  | # | 
| 871 |  |  |  |  |  |  | # So the tropical year is just TWOPI divided by this. But we want the | 
| 872 |  |  |  |  |  |  | # Siderial year, so we have to add in the precession. I found no | 
| 873 |  |  |  |  |  |  | # reference for this, but to a first approximation the additional angle | 
| 874 |  |  |  |  |  |  | # to travel is the solar year times the rate of precession, and the | 
| 875 |  |  |  |  |  |  | # siderial year is the solar year plus the additional distance divided | 
| 876 |  |  |  |  |  |  | # by the orbital velocity. | 
| 877 |  |  |  |  |  |  | # | 
| 878 |  |  |  |  |  |  | # But this calculation is done when the model parameters are built, and | 
| 879 |  |  |  |  |  |  | # period() just returns the resultant value. | 
| 880 |  |  |  |  |  |  | # | 
| 881 |  |  |  |  |  |  | # The rate of pecession is the IAU 2003 value, taken from fapa03.c. | 
| 882 |  |  |  |  |  |  |  | 
| 883 |  |  |  |  |  |  | sub period { | 
| 884 | 24 |  |  | 24 | 1 | 2503 | my ( $self ) = @_; | 
| 885 | 24 |  |  |  |  | 108 | return $self->__model_definition( 'sidereal_period' ); | 
| 886 |  |  |  |  |  |  | } | 
| 887 |  |  |  |  |  |  |  | 
| 888 |  |  |  |  |  |  | # https://in-the-sky.org/article.php?term=synodic_period | 
| 889 |  |  |  |  |  |  | # Note that I chose NOT to precalculate this because it depends on the | 
| 890 |  |  |  |  |  |  | # period of the 'sun' attribute, which may change. | 
| 891 |  |  |  |  |  |  | sub synodic_period { | 
| 892 | 35 |  |  | 35 | 1 | 97 | my ( $self ) = @_; | 
| 893 | 35 |  |  |  |  | 139 | return( 1 / abs( 1 / $self->get( 'sun' )->year() - 1 / $self->year() ) ); | 
| 894 |  |  |  |  |  |  | } | 
| 895 |  |  |  |  |  |  |  | 
| 896 |  |  |  |  |  |  | sub year { | 
| 897 | 70 |  |  | 70 | 1 | 243 | my ( $self ) = @_; | 
| 898 | 70 |  |  |  |  | 309 | return $self->__model_definition( 'tropical_period' ); | 
| 899 |  |  |  |  |  |  | } | 
| 900 |  |  |  |  |  |  |  | 
| 901 |  |  |  |  |  |  | 1; | 
| 902 |  |  |  |  |  |  |  | 
| 903 |  |  |  |  |  |  | __END__ |