I hope this is not too off topic for the list.

While profiling DateTime::Indic with Devel::NYTProf, I discovered that my calls to DateTime::Event::Lunar ::new_moon_before and ::new_moon_after were major bottlenecks. So I decided to replace them with code which is ultimately derived from Meeus's Astronomical Algorithms chapter 32. It makes a huge difference in speed but there is one problem.

Take a look at the output of the short example program below. In most cases, the DateTime::Event::Lunar functions and my newmoon function give almost the same results. But every now again newmoon() is off by an entire lunar cycle. Why is this? How can I fix it?

#!/usr/bin/perl
use strict;
use warnings;

use DateTime;
use DateTime::Event::Lunar;
use DateTime::Event::Sunrise;
use Math::Trig qw/ deg2rad /;
use POSIX qw/ floor /;

my $date = DateTime->new(year => 2008, month => 1, day => 1); my $sun = DateTime::Event::Sunrise->new(
    latitude  => '18.96',
    longitude => '72.82',
    altitude  => 0,
);

# year 2008
for (1 .. 366) {
    # time of sunrise on $date adjusted for Indian Standard Time
    my $sunrise = $sun->sunrise_datetime($date)->set_time_zone('Asia/Kolkata');

    # old method
    my $pnm1 = DateTime::Event::Lunar->new_moon_before(
        datetime     => $sunrise,
    )->jd;
    my $nnm1 = DateTime::Event::Lunar->new_moon_after(
        datetime    => $sunrise,
    )->jd;

    # new method
    # 0 = previous new moon before $sunrise
    my $pnm2 = newmoon($sunrise->jd, 0);
    # 1 = next new moon before $sunrise
    my $nnm2 = newmoon($sunrise->jd, 1);

    my $pnmdelta = $pnm2 - $pnm1;
    my $nnmdelta = $nnm2 - $nnm1;
    print "$sunrise $pnmdelta $nnmdelta\n";
    $date->add(days => 1);
}

use constant J1900         => 2_415_020.0;
use constant synodic_month => 29.530_588_68;

sub newmoon {
    my ($jdate, $arg) = @_;

    my $k = floor((($jdate - J1900) / 365.25) * 12.3685) + $arg;
    # time in Julian centuries since J1900
    my $t = ($jdate - J1900) / 36525.0;
    # square for frequent use
    my $t2 = $t*$t;
    # cube for frequent use
    my $t3 = $t2*$t;

    # mean time of phase
    my $jdnv = (2_415_020.759_33) + synodic_month * $k + 0.000_117_8 * $t2
        - 0.000_000_155 * $t3
        + 0.000_33 * sin(deg2rad(166.56 + 132.87 * $t - 0.009_173 * $t2));

    # Sun's mean anomaly
    my $m = deg2rad(359.224_2 + 29.105_356_08 * $k - 0.000_033_3 * $t2
        - 0.000_003_47 * $t3);
    # Moon's mean anomaly
    my $mprime = deg2rad(306.025_3 + 385.816_918_06 * $k + 0.010_730_6 * $t2
        + 0.000_012_36 * $t3);
    # Moon's argument of latitude
    my $f = deg2rad(21.296_4 + 390.670_506_46 * $k - 0.001_652_8 * $t2
        - 0.000_002_39 * $t3);

    # Correction for new moon
    my $djd = (0.1734 - 0.000_393 * $t) * sin($m) + 0.002_1 * sin(2 * $m);
    $djd = $djd - 0.406_8 * sin($mprime) + 0.016_1 * sin(2 * $mprime);
    $djd = $djd - 0.000_4 * sin(3 * $mprime) + 0.010_4 * sin(2 * $f) ;
    $djd = $djd - 0.005_1 * sin($m + $mprime) - 0.007_4 * sin($m - $mprime);
    $djd = $djd + 0.000_4 * sin(2 * $f + $m) - 0.000_4 * sin(2 * $f - $m);
    $djd = $djd - 0.000_6 * sin(2 * $f + $mprime) + 0.001 *
        sin(2 * $f - $mprime);
    $djd = $djd + 0.000_5 * sin($m + 2 * $mprime);
    $jdnv += $djd;

    return $jdnv;
}


--
Jaldhar H. Vyas <jald...@braincells.com>

Reply via email to