The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package main;

use strict;
use warnings;

use lib qw{ inc };

use Astro::Coord::ECI;
use Astro::Coord::ECI::Moon;
use Astro::Coord::ECI::Utils qw{ :time deg2rad PI TWOPI };
use My::Module::Test qw{ :tolerance format_time };
use Test::More 0.88;

# Moon position in ecliptic latitude/longitude and distance.
# Tests: ::Moon->time_set() (and ecliptic())

# This test is based on Meeus' example 47.a.

# Meeus states that his accuracy is 10 seconds of arc in longitude, and
# 4 seconds in latitude. He does not give an accuracy on the distance.

# Note that we're not too picky about the position of the sun, since
# it's an extended object. One part in a thousand is less than half its
# disk.

{
    my $time = timegm( 0, 0, 0, 12, 3, 92 );

    my ( $lat, $long, $delta ) = Astro::Coord::ECI::Moon->
	dynamical( $time )->ecliptic();

    tolerance_frac $lat, deg2rad( -3.229126 ), 1e-6,
	'Ecliptic latitude of Moon April 12 1992 00:00:00 dynamical';

    tolerance_frac $long, deg2rad( 133.167265 ), 1e-6,
	'Ecliptic longitude of Moon April 12 1992 00:00:00 dynamical';

    tolerance_frac $delta, 368409.7, 1e-6,
	'Ecliptic distance to Moon April 12 1992 00:00:00 dynamical';
}

# phase of the moon.
# Tests: phase ()

# This test is based on Meeus' example 49.a, but worked backward.

{
    my $time = timegm( 42, 37, 3, 18, 1, 77 );

    my $got = Astro::Coord::ECI::Moon->dynamical( $time )->phase();
    $got >= PI
	and $got -= TWOPI;

    tolerance $got, 0, 1e-4,
	'Phase of Moon February 18 1977 3:37:42 dynamical';
}

# Phase angle and illuminated fraction.

# This test is based on Meeus' example 48.a.

{
    my $time = timegm( 0, 0, 0, 12, 3, 92 );
    my ( $phase, $illum ) =
	Astro::Coord::ECI::Moon->dynamical( $time )->phase();

    tolerance $phase, deg2rad( 180 - 69.0756 ), 3e-3,
	'Phase of Moon April 12 1992 00:00:00 dynamical';

    tolerance $illum, .6786, .01,
	'Fraction of Moon illuminated April 12 1992 00:00:00 dynamical';
}


# next_quarter and next_quarter_hash

# This test is based on Meeus' example 49.1, right way around.

{
    my $time = timegm( 0, 0, 0, 1, 1, 77 );
    my $want = timegm( 42, 37, 3, 18, 1, 77 );
    my $moon = Astro::Coord::ECI::Moon->new();
    my $tolerance = 2;

    my $got = $moon->dynamical( $time )->next_quarter( 0 );

    tolerance $got, $want, $tolerance,
	'Next new Moon after February 1 1977 00:00:00 dynamical',
	\&format_time;

    $got = $moon->dynamical( $time )->next_quarter_hash( 0 );

    tolerance $got->{time}, $want, $tolerance,
	'Hash of next new Moon after February 1 1977 00:00:00 dynamical',
	\&format_time;
}


# Singleton object

{
    local $Astro::Coord::ECI::Moon::Singleton = 1;

    my @moon = map { Astro::Coord::ECI::Moon->new() } ( 0, 1 );

    cmp_ok $moon[0], '==', $moon[1],
    'Get same object from different calls to new() with $Singleton true';
}

{
    local $Astro::Coord::ECI::Moon::Singleton = 0;

    my @moon = map { Astro::Coord::ECI::Moon->new() } ( 0, 1 );

    cmp_ok $moon[0], '!=', $moon[1],
    'Get different objects from different calls to new() with $Singleton false';
}

SKIP: {

    note 'Almanac computed for an explicit location';

    my $sta = Astro::Coord::ECI->new(
	name => 'Washington, DC'
    )->geodetic(
	deg2rad(38.9),	# Position according to
	deg2rad(-77.0),	# U. S. Naval Observatory's
	0,		# http://aa.usno.navy.mil/data/docs/RS_OneDay.php
    );
    my $moon = Astro::Coord::ECI::Moon->new();
    my $time = timegm( 0, 0, 5, 1, 0, 108 );	# Jan 1, 2008 in TZ -5

    my @events = $moon->universal( $time )->almanac( $sta );

    cmp_ok scalar @events, '==', 3,
	'Almanac method returned three events';

    @events
	or skip 'No events found', 12;

    is $events[0][1], 'horizon', 'First event is horizon crossing';

    cmp_ok $events[0][2], '==', 1, 'First event is Moon rise';

    is $events[0][3], 'Moon rise', q{First event description is 'Moon rise'};

    tolerance $events[0][0], timegm( 0, 15,  6, 1, 0, 108 ), 60,
	'Moon rise occurred at January 1 2008 6:15:00 GMT',
	\&format_time;

    @events > 1
	or skip 'Only one event found', 8;

    is $events[1][1], 'transit', 'Second event is meridian crossing';

    cmp_ok $events[1][2], '==', 1, 'Second event is Moon culmination';

    is $events[1][3], 'Moon transits meridian',
	q{Second event description is 'Moon transits meridian'};

    tolerance $events[1][0], timegm( 0, 46, 11, 1, 0, 108 ), 60,
	'Moon culmination occurred at January 1 2008 11:46:00 GMT',
	\&format_time;

    @events > 2
	or skip 'Only two events found', 4;

    is $events[2][1], 'horizon', 'Third event is horizon crossing';

    cmp_ok $events[2][2], '==', 0, 'Third event is Moon set';

    is $events[2][3], 'Moon set', q{Third event description is 'Moon set'};

    tolerance $events[2][0], timegm( 0, 8,  17, 1, 0, 108 ), 60,
	'Moon set occurred at January 1 2008 17:08:00 GMT',
	\&format_time;
}

SKIP: {

    note 'Almanac computed for the location in the station attribute';

    my $sta = Astro::Coord::ECI->new(
	name => 'Washington, DC'
    )->geodetic(
	deg2rad(38.9),	# Position according to
	deg2rad(-77.0),	# U. S. Naval Observatory's
	0,		# http://aa.usno.navy.mil/data/docs/RS_OneDay.php
    );
    my $moon = Astro::Coord::ECI::Moon->new( station => $sta );
    my $time = timegm( 0, 0, 5, 1, 0, 108 );	# Jan 1, 2008 in TZ -5

    my @events = $moon->universal( $time )->almanac();

    cmp_ok scalar @events, '==', 3,
	'Almanac method returned three events';

    @events
	or skip 'No events found', 12;

    is $events[0][1], 'horizon', 'First event is horizon crossing';

    cmp_ok $events[0][2], '==', 1, 'First event is Moon rise';

    is $events[0][3], 'Moon rise', q{First event description is 'Moon rise'};

    tolerance $events[0][0], timegm( 0, 15,  6, 1, 0, 108 ), 60,
	'Moon rise occurred at January 1 2008 6:15:00 GMT',
	\&format_time;

    @events > 1
	or skip 'Only one event found', 8;

    is $events[1][1], 'transit', 'Second event is meridian crossing';

    cmp_ok $events[1][2], '==', 1, 'Second event is Moon culmination';

    is $events[1][3], 'Moon transits meridian',
	q{Second event description is 'Moon transits meridian'};

    tolerance $events[1][0], timegm( 0, 46, 11, 1, 0, 108 ), 60,
	'Moon culmination occurred at January 1 2008 11:46:00 GMT',
	\&format_time;

    @events > 2
	or skip 'Only two events found', 4;

    is $events[2][1], 'horizon', 'Third event is horizon crossing';

    cmp_ok $events[2][2], '==', 0, 'Third event is Moon set';

    is $events[2][3], 'Moon set', q{Third event description is 'Moon set'};

    tolerance $events[2][0], timegm( 0, 8,  17, 1, 0, 108 ), 60,
	'Moon set occurred at January 1 2008 17:08:00 GMT',
	\&format_time;
}

SKIP: {
    my $sta = Astro::Coord::ECI->new(
	name => 'Washington, DC'
    )->geodetic(
	deg2rad(38.9),	# Position according to
	deg2rad(-77.0),	# U. S. Naval Observatory's
	0,		# http://aa.usno.navy.mil/data/docs/RS_OneDay.php
    );
    my $moon = Astro::Coord::ECI::Moon->new();
    my $time = timegm( 0, 0, 5, 1, 0, 108 );	# Jan 1, 2008 in TZ -5

    my @events = $moon->universal( $time )->almanac_hash( $sta );

    cmp_ok scalar @events, '==', 3,
	'Almanac_hash method returned three events';

    @events
	or skip 'No events found', 12;

    is $events[0]{almanac}{event}, 'horizon',
	'First event is horizon crossing';

    cmp_ok $events[0]{almanac}{detail}, '==', 1,
	'First event is Moon rise';

    is $events[0]{almanac}{description}, 'Moon rise',
	q{First event description is 'Moon rise'};

    tolerance $events[0]{time}, timegm( 0, 15,  6, 1, 0, 108 ), 60,
	'Moon rise occurred at January 1 2008 6:15:00 GMT',
	\&format_time;

    @events > 1
	or skip 'Only one event found', 8;

    is $events[1]{almanac}{event}, 'transit',
	'Second event is meridian crossing';

    cmp_ok $events[1]{almanac}{detail}, '==', 1,
	'Second event is Moon culmination';

    is $events[1]{almanac}{description}, 'Moon transits meridian',
	q{Second event description is 'Moon transits meridian'};

    tolerance $events[1]{time}, timegm( 0, 46, 11, 1, 0, 108 ), 60,
	'Moon culmination occurred at January 1 2008 11:46:00 GMT',
	\&format_time;

    @events > 2
	or skip 'Only two events found', 4;

    is $events[2]{almanac}{event}, 'horizon',
	'Third event is horizon crossing';

    cmp_ok $events[2]{almanac}{detail}, '==', 0,
	'Third event is Moon set';

    is $events[2]{almanac}{description}, 'Moon set',
	q{Third event description is 'Moon set'};

    tolerance $events[2]{time}, timegm( 0, 8,  17, 1, 0, 108 ), 60,
	'Moon set occurred at January 1 2008 17:08:00 GMT',
	\&format_time;
}

done_testing;

1;

# ex: set textwidth=72 :