#!/usr/bin/perl use v5.030; # strictness implied use warnings; use feature qw[ signatures ]; no warnings qw[ experimental::signatures ]; use Astro::Coord::ECI::Utils 'deg2rad'; use Astro::Coords; use Astro::MoonPhase; use Astro::Telescope; use DateTime; use DateTime::Format::ISO8601; use DateTime::TimeZone; use Data::Dumper; use Log::Log4perl; use Try::Tiny; ###### # Calculate the moon phase of an event in time. # Required is the date (time defaults to 00:00:01) of the event # The moon-phase calculator does not need a location # since moon phase is the same across the planet (more-or-less) # with the usual antipodean side-effects # However, by specifying a timezone or even coordinates of the # event conversion of event's time to UTC is more accurate # (else UTC time is assumed) # author: bliako, modified by aldebaran # date: 2021-10-10 ###### my $debug = 1; my $time_str = "03:22:31"; my $alt = 0; my $date_str = "2021-10-14"; my @events = ( # { # # example event specification # "name" => "a name for reference, required", # "date" => "date of the event in YYYY-MM-DD", # "time" => "optional time as hh:mm:ss, default is 00:00:01", # #specify optional location using one of these for calculating # #epoch which uses UTC time, # #otherwise, UTC timezone will be assumed for event's time: # "timezone" => "standard timezone name, e.g. America/Havana", # "location" => "cuba", # "location" => {lon=>-81.137633, lat=>22.17927}, # }, { "name" => "in the 'Ho", "date" => $date_str, "time" => $time_str, "location" => { lon => -116.2, lat => 43.61 }, }, { "name" => "on the Scylla", "date" => $date_str, "time" => $time_str, "location" => { lon => 38.245833, lat => 15.6325 }, }, { "name" => "in Antipo' 'Ho", "date" => $date_str, "time" => $time_str, "location" => { lon => 63.8, lat => -43.61 }, }, { "name" => "at Antipodlean Scylla", "date" => $date_str, "time" => $time_str, "location" => { lon => -141, lat => -15.6325 }, }, ); # Not wanting to divine the configuration file for Log::Log4perl, module used in # the query. Better for SSCCE considerations. thx parv { package Log::Log4perl; sub get_logger ($pkg) { return bless [], $pkg; } sub show (@arg) { warn @arg, "\n"; } sub debug ( $ignore, @rest ) { show( 'DEBUG: ', @rest ); } sub info ( $ignore, @rest ) { show( 'INFO: ', @rest ); } sub warn ( $ignore, @rest ) { show( 'WARNING: ', @rest ); } sub error ( $ignore, @rest ) { die 'ERROR: ', @rest, "\n"; } } my $logger = Log::Log4perl->get_logger(); $logger->info("$0"); ## event loop for my $event (@events) { my $epoch = parse_event($event); my ( $MoonPhase, $MoonIllum, $MoonAge, $MoonDist, $MoonAng, $SunDist, $SunAng ) = phase($epoch); $logger->info("Moon age: $MoonAge days"); $logger->info( "Moon phase: " . sprintf( "%.1f", 100.0 * $MoonPhase ) . " % of cycle (birth-to-death)" ); $logger->info( "Moon's illuminated fraction: ", sprintf( "%.1f", 100.0 * $MoonIllum ) . " % of full disc" ); ## determine altitude # usage: die unless exists $event->{location}; # restriction my $return = get_elevation_from_coordinates( $event->{location}->{lat}, $event->{location}->{lon}, 0 ); $logger->info("return from the google is $return meters"); my $observer = $return + 2; #(most people stand) $logger->info( event2str($event) . " at height $observer meters" ); $logger->info("Object\tAzimuth\t\tElevation"); for my $name (qw/Venus Jupiter Moon Saturn/) { my $planet = Astro::Coords->new( planet => $name ); # use DateTime my $date = DateTime->from_epoch( epoch => $epoch, time_zone => 'UTC' ); $planet->datetime($date); $planet->telescope( Astro::Telescope->new( Name => $event->{name}, Long => deg2rad( $event->{location}->{lon} ), Lat => deg2rad( $event->{location}->{lat} ), Alt => $observer, #meters above sea level ) ); my $az = $planet->az( format => 'deg' ); my $el = $planet->el( format => 'deg' ); $logger->info("$name\t$az\t\t$el"); } ## end planets loop } ## end event loop sub event2str { my $event = shift; if ( !exists $event->{_is_parsed} ) { warn "event has not been parsed, just dumping it..."; print Dump($event); } my $str = $event->{name} . " on " . $event->{datetime} . " (" . $event->{datetime}->epoch . " seconds unix-epoch)" . " timezone: " . $event->{datetime}->time_zone->name; if ( exists $event->{location} ) { if ( ref( $event->{location} ) eq 'HASH' ) { $str .= " (lat: " . $event->{location}->{lat} . ", lon: " . $event->{location}->{lon} . ")"; } else { $str .= "(" . $event->{location} . ")" } } return $str; } sub parse_event { my $event = shift; if ( !exists $event->{date} ) { die "date field is missing from event." } my $datestr = $event->{date}; die "event does not have a 'name' field, please specify one, anything really." unless exists $event->{name}; my $timestr = "00:00:01"; if ( exists $event->{time} ) { $timestr = $event->{time}; print "event2epoch(): setting time to '$timestr' ...\n" if $debug > 0; die "time '$timestr' is not valid, it must be in the form 'hh:mm:ss'." unless $timestr =~ /^\d{2}:\d{2}:\d{2}$/; } else { $event->{time} = $timestr } my $isostr = $datestr . 'T' . $timestr; my $dt = DateTime::Format::ISO8601->parse_datetime($isostr); die "failed to parse date '$isostr', check date and time fields." unless defined $dt; $event->{datetime} = $dt; my $tzstr = 'UTC'; if ( exists $event->{timezone} ) { $tzstr = $event->{timezone}; print "event2epoch(): found a timezone via 'timezone' field as '$tzstr' (that does not mean it is valid) ...\n" if $debug > 0; } elsif ( exists $event->{location} ) { my $loc = $event->{location}; if ( ( ref($loc) eq '' ) && ( $loc =~ /^[a-zA-Z]$/ ) ) { # we have a location string my @alltzs = DateTime::TimeZone->all_names; my $tzstr; for (@alltzs) { if ( $_ =~ /$loc/i ) { $tzstr = $_; last } } die "event's location can not be converted to a timezone, consider specifying the 'timezone' directly or setting 'location' coordinates with: \[lat,lon\]." unless $tzstr; print "event2epoch(): setting timezone via 'location' name to '$timestr' ...\n" if $debug > 0; } elsif ( ( ref($loc) eq 'HASH' ) && ( exists $loc->{lat} ) && ( exists $loc->{lon} ) ) { # we have a [lat,lon] array for location require Geo::Location::TimeZone; my $gltzobj = Geo::Location::TimeZone->new(); $tzstr = $gltzobj->lookup( lat => $loc->{lat}, lon => $loc->{lon} ); if ( !$tzstr ) { die "timezone lookup from location coordinates lat:" . $loc->{lat} . ", lon:" . $loc->{lon} . " has failed."; } print "event2epoch(): setting timezone via 'location' coordinates lat:" . $loc->{lat} . ", lon:" . $loc->{lon} . " ...\n" if $debug > 0; } } if ($tzstr) { print "event2epoch(): deduced timezone to '$tzstr' and setting it ...\n" if $debug > 0; try { $dt->set_time_zone($tzstr) } catch { die "$_\n failed to set the timezone '$tzstr', is it valid?" } } $event->{_is_parsed} = 1; $event->{epoch} = $dt->epoch; return $event->{epoch}; } sub get_elevation_from_coordinates { my ( $lat, $lon, $debug ) = @_; use LWP::UserAgent; use HTTP::Request; use Data::Roundtrip; $debug //= 0; my $ua = LWP::UserAgent->new( agent => 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0', ); my $response; my $payload = 'latitude=' . $lat . '&longitude=' . $lon . '&application_max_assets_mtime=1559625591'; my $payloadlen = length($payload); # this request was translated from Curl command-line # by [Corion]'s https://corion.net/curl2lwp.psgi my $req = HTTP::Request->new( 'POST' => 'https://www.mapcoordinates.net/admin/component/edit/Vpc_MapCoordinates_Advanced_GoogleMapCoords_Component/Component/json-get-elevation', [ 'Connection' => 'keep-alive', 'Accept' => '*/*', 'Accept-Encoding' => 'gzip, x-gzip, deflate, x-bzip2, bzip2', 'Accept-Language' => 'en-US,en;q=0.5', # 'Host' => 'www.mapcoordinates.net:443', 'Referer' => 'https://www.mapcoordinates.net/en', 'User-Agent' => 'Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0', 'Content-Length' => $payloadlen, 'Content-Type' => 'application/x-www-form-urlencoded; charset=UTF-8', 'DNT' => '1', 'Origin' => 'https://www.mapcoordinates.net', 'X-Requested-With' => 'XMLHttpRequest' ], $payload ); die "call to HTTP::Request has failed" unless $req; if ( $debug > 0 ) { print "$0 : $payload\n$0 : sending above payload, of $payloadlen bytes...\n"; } $response = $ua->request($req); die "Error fetching: " . $response->status_line unless $response->is_success; my $content = $response->decoded_content; my $data = Data::Roundtrip::json2perl($content); die "failed to parse received data:\n$content\n" unless exists $data->{'elevation'}; return $data->{'elevation'}; } __END__ #### #source 5.1.mp.txt #source 5.mp.txt b 262 c s v p @_ p $debug b 312 b 317 c v n n s v l 280-300 b 289 c n r s v n p $json_string l 198-215 r c c c c s v n p $json_string c c c c s n p $json_string B * c #save 5.mp.monk.txt #### $ perl -d 5.mp.pl Loading DB routines from perl5db.pl version 1.55 Editor support available. Enter h or 'h h' for help, or 'man perldebug' for more help. Subroutine get_logger redefined at 5.mp.pl line 83. at 5.mp.pl line 83. main::(5.mp.pl:32): my $debug = 1; DB<1> source 5.1.mp.txt >> #source 5.mp.txt #### >> b 262 >> c INFO: 5.mp.pl at 5.mp.pl line 94. event2epoch(): setting time to '03:22:31' ... event2epoch(): setting timezone via 'location' coordinates lat:43.61, lon:-116.2 ... event2epoch(): deduced timezone to 'Etc/GMT-7' and setting it ... INFO: Moon age: 8.12733852952393 days at 5.mp.pl line 103. INFO: Moon phase: 27.5 % of cycle (birth-to-death) at 5.mp.pl line 104. INFO: Moon's illuminated fraction: 57.9 % of full disc at 5.mp.pl line 107. main::get_elevation_from_coordinates(5.mp.pl:262): 262: my ( $lat, $lon, $debug ) = @_; >> s main::get_elevation_from_coordinates(5.mp.pl:268): 268: $debug //= 0; >> v 265: use HTTP::Request; 266: use Data::Roundtrip; 267 268==> $debug //= 0; 269 270: my $ua = LWP::UserAgent->new( agent => 271 'Moz

These values are not the zero values I was concerned about

illa/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0', ); 272 273: my $response; 274: my $payload = >> p @_ 43.61-116.20 >> p $debug 0 >> b 312 >> b 317 ##
## >> c main::get_elevation_from_coordinates(5.mp.pl:312): 312: $response = $ua->request($req); >> v 309 "$0 : $payload\n$0 : sending above payload, of $payloadlen bytes...\n"; 310 } 311 312==>b $response = $ua->request($req); 313: die "Error fetching: " . $response->status_line 314 unless $response->is_success; 315 316: my $content = $response->decoded_content; 317:b my $data = Data::Roundtrip::json2perl($content); 318 die "failed to parse received data:\n$content\n" ... >> s HTTP::Message::decoded_content(/usr/share/perl5/HTTP/Message.pm:283): 283: my($self, %opt) = @_; >> v 280 281 sub decoded_content 282 { 283==> my($self, %opt) = @_; 284: my $content_ref; 285: my $content_ref_iscopy; 286 287: eval { 288: $content_ref = $self->content_ref; 289: die "Can't decode ref content" if ref($content_ref) ne "SCALAR"; >> l 280-300 280 281 sub decoded_content 282 { 283==> my($self, %opt) = @_; 284: my $content_ref; 285: my $content_ref_iscopy; 286 287: eval { 288: $content_ref = $self->content_ref; 289: die "Can't decode ref content" if ref($content_ref) ne "SCALAR"; 290 291: if (my $h = $self->header("Content-Encoding")) { 292: $h =~ s/^\s+//; 293: $h =~ s/\s+$//; 294: for my $ce (reverse split(/\s*,\s*/, lc($h))) { 295: next unless $ce; 296: next if $ce eq "identity" || $ce eq "none"; 297: if ($ce eq "gzip" || $ce eq "x-gzip") { 298: require IO::Uncompress::Gunzip; 299: my $output; 300: IO::Uncompress::Gunzip::gunzip($content_ref, \$output, Transparent => 0) >> b 289 >> c HTTP::Message::decoded_content(/usr/share/perl5/HTTP/Message.pm:289): 289: die "Can't decode ref content" if ref($content_ref) ne "SCALAR"; >> n HTTP::Message::decoded_content(/usr/share/perl5/HTTP/Message.pm:291): 291: if (my $h = $self->header("Content-Encoding")) { >> r scalar context return from HTTP::Message::decoded_content: '{"elevation":821,"success":true,"requestNum":"163546920137"}' #### main::get_elevation_from_coordinates(5.mp.pl:317): 317: my $data = Data::Roundtrip::json2perl($content); 318: die "failed to parse received data:\n$content\n" >> s Data::Roundtrip::json2perl(/usr/local/share/perl/5.30.0/Data/Roundtrip.pm:192): 192: my $json_string = $_[0]; >> v 189: return $pv 190 } 191 sub json2perl { 192==> my $json_string = $_[0]; 193 #my $params = defined($_[1]) ? $_[1] : {}; 194: my $pv = JSON::decode_json(Encode::encode_utf8($json_string)); 195: if( ! defined $pv ){ warn "json2perl() : error, call to json2perl() has failed"; return undef } 196: return $pv; 197 } 198 sub json2json { >> n Data::Roundtrip::json2perl(/usr/local/share/perl/5.30.0/Data/Roundtrip.pm:194): 194: my $pv = JSON::decode_json(Encode::encode_utf8($json_string)); >> p $json_string {"elevation":821,"success":true,"requestNum":"163546920137"} >> l 198-215 198 sub json2json { 199: my $json_string = $_[0]; 200: my $params = defined($_[1]) ? $_[1] : {}; 201 202: my $pv = json2perl($json_string, $params); 203: if( ! defined $pv ){ warn "json2perl() : error, call to json2perl() has failed"; return undef } 204: $json_string = perl2json($pv, $params); 205: if( ! defined $json_string ){ warn "json2perl() : error, call to perl2json() has failed"; return undef } 206 207: return $json_string; 208 } 209 sub yaml2yaml { 210: my $yaml_string = $_[0]; 211: my $params = defined($_[1]) ? $_[1] : {}; 212 213: my $pv = yaml2perl($yaml_string, $params); 214: if( ! defined $pv ){ warn "yaml2perl() : error, call to yaml2perl() has failed"; return undef } 215: $yaml_string = perl2yaml($pv, $params); >> r scalar context return from Data::Roundtrip::json2perl: 'elevation' => 821 'requestNum' => 163546920137 'success' => JSON::PP::Boolean=SCALAR(0x55e918df2b78) -> 1 #### ... event2epoch(): setting time to '03:22:31' ... event2epoch(): setting timezone via 'location' coordinates lat:15.6325, lon:38.245833 ... event2epoch(): deduced timezone to 'Africa/Asmara' and setting it ... ... {"elevation":841,"success":true,"requestNum":"163546921268"} DB<13> c INFO: return from the google is 841 meters at 5.mp.pl line 115. INFO: on the Scylla on 2021-10-14T03:22:31 (1634170951 seconds unix-epoch) timezone: Africa/Nairobi (lat: 15.6325, lon: 38.245833) at height 843 meters #### ... event2epoch(): setting time to '03:22:31' ... event2epoch(): setting timezone via 'location' coordinates lat:-43.61, lon:63.8 ... event2epoch(): deduced timezone to 'Etc/GMT+4' and setting it ... ... DB<13> c main::get_elevation_from_coordinates(5.mp.pl:317): 317: my $data = Data::Roundtrip::json2perl($content); 318: die "failed to parse received data:\n$content\n" DB<13> s Data::Roundtrip::json2perl(/usr/local/share/perl/5.30.0/Data/Roundtrip.pm:192): 192: my $json_string = $_[0]; DB<13> n Data::Roundtrip::json2perl(/usr/local/share/perl/5.30.0/Data/Roundtrip.pm:194): 194: my $pv = JSON::decode_json(Encode::encode_utf8($json_string)); DB<13> p $json_string {"elevation":0,"success":true,"requestNum":"163546928850"} #### DB<14> B * Deleting all breakpoints... DB<15> c INFO: return from the google is 0 meters at 5.mp.pl line 115. INFO: in Antipo' 'Ho on 2021-10-14T03:22:31 (1634196151 seconds unix-epoch) timezone: -0400 (lat: -43.61, lon: 63.8) at height 2 meters at 5.mp.pl line 117. ... Debugged program terminated. Use q to quit or R to restart, use o inhibit_exit to avoid stopping after program termination, h q, h R or h o to get additional info. DB<15> save 5.mp.monk.txt commands saved in 5.mp.monk.txt DB<16> q $ #### INFO: on the Scylla on 2021-10-14T03:22:31 (1634170951 seconds unix-epoch) timezone: Africa/Nairobi (lat: 15.6325, lon: 38.245833) at height 843 meters INFO: Object Azimuth Elevation INFO: Venus 168.793996164728 -81.0110616258686 INFO: Jupiter 258.080321870357 -14.8575841846033 INFO: Moon 251.270738258432 -35.2685064885712 INFO: Saturn 256.410482564197 -30.2637811706649 #### $ perl -Mre=debug -e "/just|another|perl|hacker/" Compiling REx "just|another|perl|hacker" ~ tying lastbr BRANCH (11) to ender END (15) offset 4 Final program: 1: TRIEC-EXACT[ahjp] (15) 15: END (0) stclass AHOCORASICKC-EXACT[ahjp] minlen 4 Freeing REx: "just|another|perl|hacker" $