| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Astro::Montenbruck::RiseSet::Plarise; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 3 |  |  | 3 |  | 17 | use strict; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 79 |  | 
| 4 | 3 |  |  | 3 |  | 12 | use warnings; | 
|  | 3 |  |  |  |  | 5 |  | 
|  | 3 |  |  |  |  | 74 |  | 
| 5 | 3 |  |  | 3 |  | 13 | no warnings qw/experimental/; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 93 |  | 
| 6 | 3 |  |  | 3 |  | 12 | use feature qw/switch/; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 229 |  | 
| 7 |  |  |  |  |  |  |  | 
| 8 | 3 |  |  | 3 |  | 18 | use Exporter qw/import/; | 
|  | 3 |  |  |  |  | 5 |  | 
|  | 3 |  |  |  |  | 77 |  | 
| 9 | 3 |  |  | 3 |  | 13 | use Readonly; | 
|  | 3 |  |  |  |  | 5 |  | 
|  | 3 |  |  |  |  | 221 |  | 
| 10 |  |  |  |  |  |  |  | 
| 11 | 3 |  |  | 3 |  | 15 | use Math::Trig qw/:pi deg2rad rad2deg acos/; | 
|  | 3 |  |  |  |  | 5 |  | 
|  | 3 |  |  |  |  | 359 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 | 3 |  |  | 3 |  | 20 | use Astro::Montenbruck::MathUtils qw/to_range/; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 145 |  | 
| 14 | 3 |  |  | 3 |  | 17 | use Astro::Montenbruck::Time qw/cal2jd jd_cent/; | 
|  | 3 |  |  |  |  | 5 |  | 
|  | 3 |  |  |  |  | 138 |  | 
| 15 | 3 |  |  | 3 |  | 1143 | use Astro::Montenbruck::Time::Sidereal qw/ramc/; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 139 |  | 
| 16 | 3 |  |  | 3 |  | 18 | use Astro::Montenbruck::RiseSet::Constants qw/:events :states/; | 
|  | 3 |  |  |  |  | 6 |  | 
|  | 3 |  |  |  |  | 2205 |  | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | our @EXPORT_OK = qw/rst_func/; | 
| 19 |  |  |  |  |  |  | our $VERSION   = 0.01; | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  | Readonly our $SID => 0.9972696; # Conversion sidereal/solar time | 
| 22 |  |  |  |  |  |  | Readonly our $ZT_MIN => 0.008; | 
| 23 |  |  |  |  |  |  | Readonly our $MAX_COUNT => 10; | 
| 24 |  |  |  |  |  |  |  | 
| 25 |  |  |  |  |  |  | sub _cs_phi { | 
| 26 | 2 |  |  | 2 |  | 3 | my $phi  = shift; | 
| 27 | 2 |  |  |  |  | 8 | my $rphi = deg2rad($phi); | 
| 28 | 2 |  |  |  |  | 67 | cos($rphi), sin($rphi); | 
| 29 |  |  |  |  |  |  | } | 
| 30 |  |  |  |  |  |  |  | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | sub rst_func { | 
| 34 | 2 |  |  | 2 | 0 | 43 | my %arg = ( date => undef, phi => undef, lambda => undef, @_ ); | 
| 35 | 2 |  |  |  |  | 4 | my $jd0 = cal2jd( @{ $arg{date} } ); | 
|  | 2 |  |  |  |  | 8 |  | 
| 36 | 2 |  |  |  |  | 6 | my $phi = $arg{phi}; | 
| 37 | 2 |  |  |  |  | 6 | my ( $cphi, $sphi ) = _cs_phi( $phi ); | 
| 38 |  |  |  |  |  |  |  | 
| 39 | 2 |  |  |  |  | 14 | my $lst_0h = ramc( $jd0, $arg{lambda} ) / 15; | 
| 40 |  |  |  |  |  |  |  | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | sub { | 
| 43 | 10 |  |  | 10 |  | 101 | my %arg = ( | 
| 44 |  |  |  |  |  |  | sin_h0 =>       undef, # sine of altitude correction | 
| 45 |  |  |  |  |  |  | get_position => undef, # function for calculation equatorial coordinates of the body | 
| 46 |  |  |  |  |  |  | @_ | 
| 47 |  |  |  |  |  |  | ); | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | #  Compute geocentric planetary position at 0h and 24h local time | 
| 50 | 10 |  |  |  |  | 19 | my @ra; | 
| 51 |  |  |  |  |  |  | my @de; | 
| 52 | 10 |  |  |  |  | 36 | ($ra[$_], $de[$_]) = $arg{get_position}->($jd0 + $_) for (0..1); | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | # Generate continuous right ascension values in case of jumps | 
| 55 |  |  |  |  |  |  | # between 0h and 24h | 
| 56 | 10 | 50 |  |  |  | 196 | $ra[1] += pi2 if $ra[0] - $ra[1] >  pi; | 
| 57 | 10 | 50 |  |  |  | 25 | $ra[0] += pi2 if $ra[0] - $ra[1] < -pi; | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | sub { | 
| 60 | 30 |  |  |  |  | 1138 | my $event = shift; | 
| 61 |  |  |  |  |  |  |  | 
| 62 | 30 |  |  |  |  | 146 | my $zt; | 
| 63 | 30 |  |  |  |  | 41 | my $zt0 = 12.0; # Starting value 12h local time | 
| 64 | 30 |  |  |  |  | 46 | my $state = $event; | 
| 65 | 30 |  |  |  |  | 74 | for (my $i = 0; $i <= $MAX_COUNT; $i++) { | 
| 66 |  |  |  |  |  |  | # Linear interpolation of planetary position | 
| 67 | 69 |  |  |  |  | 473 | my $ra = $ra[0] + ($zt0 / 24) * ($ra[1] - $ra[0]); | 
| 68 | 69 |  |  |  |  | 110 | my $de = $de[0] + ($zt0 / 24) * ($de[1] - $de[0]); | 
| 69 | 69 |  |  |  |  | 128 | my $above = rad2deg($de) > 90 - $phi; | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | # Compute semi-diurnal arc (in radans) | 
| 72 | 69 |  |  |  |  | 501 | my $sda = ($arg{sin_h0} - sin($de) * $sphi) / (cos($de) * $cphi); | 
| 73 | 69 | 50 |  |  |  | 126 | if (abs($sda) < 1) { | 
|  |  | 0 |  |  |  |  |  | 
| 74 | 69 |  |  |  |  | 145 | $sda = acos($sda); | 
| 75 |  |  |  |  |  |  | } elsif ($phi > 0) { | 
| 76 |  |  |  |  |  |  | # Test for circumpolar motion or invisibility | 
| 77 | 0 | 0 |  |  |  | 0 | $state = $above ? $STATE_CIRCUMPOLAR : $STATE_NEVER_RISES; | 
| 78 | 0 |  |  |  |  | 0 | last; | 
| 79 |  |  |  |  |  |  | } | 
| 80 | 69 |  |  |  |  | 436 | my $lst = $lst_0h + $zt0 / $SID; # Sidereal time at univ. time ZT0 | 
| 81 | 69 |  |  |  |  | 289 | my $h = $lst - rad2deg($ra) / 15; | 
| 82 | 69 |  |  |  |  | 400 | my $dtau = do { | 
| 83 | 69 |  |  |  |  | 87 | given ($event) { | 
| 84 | 69 |  |  |  |  | 132 | $h + rad2deg($sda) / 15 when $EVT_RISE; | 
| 85 | 46 |  |  |  |  | 214 | $h                      when $EVT_TRANSIT; | 
| 86 | 24 |  |  |  |  | 105 | $h - rad2deg($sda) / 15 when $EVT_SET; | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  | }; | 
| 89 | 69 |  |  |  |  | 582 | my $dzt = $SID * (to_range($dtau + 12, 24) - 12); | 
| 90 | 69 |  |  |  |  | 262 | $zt0 -= $dzt; | 
| 91 | 69 |  |  |  |  | 83 | $zt = $zt0; | 
| 92 | 69 | 100 |  |  |  | 128 | last if abs($dzt) <= $ZT_MIN; | 
| 93 |  |  |  |  |  |  | } | 
| 94 |  |  |  |  |  |  |  | 
| 95 | 30 | 50 |  |  |  | 200 | return $state eq $event ? ($state, $jd0 + $zt / 24) | 
| 96 |  |  |  |  |  |  | : ($state, undef) | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | } | 
| 99 |  |  |  |  |  |  |  | 
| 100 | 10 |  |  |  |  | 69 | } | 
| 101 | 2 |  |  |  |  | 16 | } | 
| 102 |  |  |  |  |  |  |  | 
| 103 |  |  |  |  |  |  | 1; | 
| 104 |  |  |  |  |  |  | __END__ | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | =pod | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | =encoding UTF-8 | 
| 109 |  |  |  |  |  |  |  | 
| 110 |  |  |  |  |  |  | =head1 NAME | 
| 111 |  |  |  |  |  |  |  | 
| 112 |  |  |  |  |  |  | Astro::Montenbruck::RiseSet::Plarise — rise and set. | 
| 113 |  |  |  |  |  |  |  | 
| 114 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 115 |  |  |  |  |  |  |  | 
| 116 |  |  |  |  |  |  | use Astro::Montenbruck::RiseSet::Constants qw/:events :altitudes/; | 
| 117 |  |  |  |  |  |  | use Astro::Montenbruck::RiseSet::Plarise qw/:rst_func/; | 
| 118 |  |  |  |  |  |  |  | 
| 119 |  |  |  |  |  |  | # build top-level function for any event and any celestial object | 
| 120 |  |  |  |  |  |  | # for given time and place | 
| 121 |  |  |  |  |  |  | my $rst_func = rst_func( | 
| 122 |  |  |  |  |  |  | date     => [1989, 3, 23], | 
| 123 |  |  |  |  |  |  | phi      => 48.1, # geographic latitude | 
| 124 |  |  |  |  |  |  | lambda   => -11.6 # geographic longitude | 
| 125 |  |  |  |  |  |  | ); | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | # build second level functon for calculating any event for given object | 
| 128 |  |  |  |  |  |  | my $evt_func = $rst_func->( | 
| 129 |  |  |  |  |  |  | get_position => sub { | 
| 130 |  |  |  |  |  |  | my $jd = shift; | 
| 131 |  |  |  |  |  |  | # return equatorial coordinates of the celestial body for the Julian Day. | 
| 132 |  |  |  |  |  |  | }, | 
| 133 |  |  |  |  |  |  | sin_h0       => sin( deg2rad($H0_PLANET) ), | 
| 134 |  |  |  |  |  |  | ); | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | # finally, calculate time of rise event. Alternatively, use $EVT_SET or $EVT_TRANSIT | 
| 137 |  |  |  |  |  |  | my ($state, $jd) = $evt_func->($EVT_RISE); | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | =head1 VERSION | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | Version 0.01 | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 145 |  |  |  |  |  |  |  | 
| 146 |  |  |  |  |  |  | Low level routines for calculating rise and set times of celestial bodies. | 
| 147 |  |  |  |  |  |  | They are especially usefull for calculating different types of twilight. | 
| 148 |  |  |  |  |  |  |  | 
| 149 |  |  |  |  |  |  | =head1 FUNCTIONS | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | =head2 riseset ( %args ) | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | time of rise and set events. | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | =head3 Named Arguments | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | =over | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | =item * B<get_position> — function, which given I<Standard Julian Day>, | 
| 160 |  |  |  |  |  |  | returns equatorial coordinates of the celestial body, in radians. | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | =item * B<date> — array of B<year> (astronomical, zero-based), B<month> [1..12] | 
| 163 |  |  |  |  |  |  | and B<day>, [1..31]. | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  | =item * B<phi> — geographic latitude, degrees, positive northward | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | =item * B<lambda> —geographic longitude, degrees, positive westward | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | =item * B<get_position> — function, which given I<Standard Julian Day>, | 
| 171 |  |  |  |  |  |  | returns equatorial coordinates of the celestial body, in radians. | 
| 172 |  |  |  |  |  |  |  | 
| 173 |  |  |  |  |  |  | =item * B<sin_h0> — sine of the I<standard altitude>, i.e. the geometric altitude | 
| 174 |  |  |  |  |  |  | of the center of the body at the time of apparent rising or setting. | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | =item * C<on_event> callback is called when the event time is determined. | 
| 178 |  |  |  |  |  |  | The arguments are: | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | =over | 
| 181 |  |  |  |  |  |  |  | 
| 182 |  |  |  |  |  |  | =item * event type, one of C<$EVT_RISE> or C<$EVT_SET> | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | =item * Univerrsal time of the event | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | =back | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | on_event => sub { my ($evt, $ut) = @_; ... } | 
| 189 |  |  |  |  |  |  |  | 
| 190 |  |  |  |  |  |  | =item * C<on_noevent> is called when the event does not happen at the given date, | 
| 191 |  |  |  |  |  |  | either because the body never rises, or is circumpolar. The argument is respectively | 
| 192 |  |  |  |  |  |  | C<$STATE_NEVER_RISES> or C<$STATE_CIRCUMPOLAR>. | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  | on_noevent => sub { my $state = shift; ... } | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | =back | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | =head1 AUTHOR | 
| 199 |  |  |  |  |  |  |  | 
| 200 |  |  |  |  |  |  | Sergey Krushinsky, C<< <krushi at cpan.org> >> | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 203 |  |  |  |  |  |  |  | 
| 204 |  |  |  |  |  |  | Copyright (C) 2010-2022 by Sergey Krushinsky | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | This library is free software; you can redistribute it and/or modify | 
| 207 |  |  |  |  |  |  | it under the same terms as Perl itself. | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | =cut |