#!/usr/bin/perl use v5.030; use warnings; #use Time::Piece; using DateTime instead 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 = 0; my $time_str = "03:22:31"; my $alt = 817; 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" => "$alt meters in the 'Ho", "date" => $date_str, "time" => $time_str, "location" => { lon => -116.2, lat => 43.61 }, }, { "name" => "$alt meters on the Scylla", "date" => $date_str, "time" => $time_str, "location" => { lon => 38.245833, lat => 15.6325 }, }, { "name" => "$alt meters in Antipo' 'Ho", "date" => $date_str, "time" => $time_str, "location" => { lon => 63.8, lat => -43.61 }, }, { "name" => "$alt meters at Antipodlean Scylla", "date" => $date_str, "time" => $time_str, "location" => { lon => -141, lat => -15.6325 }, }, ); my $file = '/home/hogan/Documents/hogan/logs/3.log4perl.txt'; # unlink $file or warn "Could not unlink $file: $!"; my $log_conf4 = "/home/hogan/Documents/hogan/logs/conf_files/3.conf"; Log::Log4perl::init($log_conf4); #info my $logger = Log::Log4perl->get_logger(); $logger->info("$0"); ## event loop for my $event (@events) { my $epoch = parse_event($event); $logger->info( event2str($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" ); $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 => $alt, #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}; } __END__