File Coverage

lib/Astro/Montenbruck/RiseSet/Plarise.pm
Criterion Covered Total %
statement 72 74 97.3
branch 6 14 42.8
condition n/a
subroutine 14 14 100.0
pod 0 1 0.0
total 92 103 89.3


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