<?php
/*
LUNAR PERIGEE AND APOGEE CALCULATOR
FOR THE YEARS FROM 1000 TO 9700.
Based on the NASA/JPL Horizons API v1.1
The NASA/JPL Horizons went on-line in July
of 2021.
This program is an experimental application
of the API.
AUTHOR : Jay Tanner
LANGUAGE : PHP v8.2.12
LICENSE : Public Domain
*/
// Initialize output buffer.
ob_start();
// Note currently used PHP version.
$PHPVersionStr = 'PHP v8.2.12';
// Set ephemeris year span.
$MinYear = 1000;
$MaxYear = 9998;
$EphSpan = $MaxYear - $MinYear + 1;
// Define the program cookie name and set it to expire in 30 days.
// This cookie can be shared by other programs in the same common
// home folder together and using the same input interface.
$CookieName = 'Perigees_and_Apogees_Calculator';
$SetToExpireIn30Days = time() + 30*86400;
// Define script path and filename.
$_AUTHOR_ = 'Jay Tanner';
$_PROGRAM_VERSION_ = ''; $at = "at"; $UTC = "UTC";
$_SCRIPT_PATH_ = Filter_Input(INPUT_SERVER, 'SCRIPT_FILENAME');
$_RUN_ = Filter_Input(INPUT_POST, 'SCRIPT_NAME');
// Define internal document page title, HTML
// page heading text and revision date.
$_INTERNAL_TITLE_ = "Lunar Perigees and Apogees Calculator";
$_INTERFACE_TITLE_ = "<b style='font-size:15pt; font-weight:normal;'>Lunar Perigees and Apogees Calculator</b><br><br><span style='font-size:9pt;'>For the $EphSpan-Year Span From $MinYear AD to $MaxYear AD<br>
<b style='font-weight:normal;'>Built Around the NASA/JPL Horizons API<br>PHP Program by Jay Tanner</b></span>";
$_THIS_SCRIPT_NAME_ = BaseName($_SERVER["SCRIPT_FILENAME"]);
$at = "at"; $UTC = "UTC";
$_REVISION_DATE_ = 'Revised: '.gmdate("l - F d, Y $at H:i:s $UTC", FileMTime($_THIS_SCRIPT_NAME_));
// Define Java Script message to display while computing is in progress.
$_COMPUTING_ = "TextArea1.innerHTML=' COMPUTING - THIS MAY TAKE SEVERAL SECONDS';";
// Define main TextArea text, background
// colors and HTML table row span.
$TxColor = 'black';
$BgColor = 'white';
// Initialize time scale strings.
$UT1C = $TimeScale = $TScaleStr = '';
// Do this only if [COMPUTE] button was clicked.
$w = Filter_Input(INPUT_POST, 'ComputeButton');
if (!IsSet($w))
// ----------------------------------------------------
// If [COMPUTE] button was clicked and an active cookie
// exists, then restore the previous interface settings
// from it.
{
$w = Filter_Input(INPUT_COOKIE, $CookieName);
if (IsSet($w))
{
$CookieDataString = Filter_Input(INPUT_COOKIE, $CookieName);
list
(
$Year, $TZhhmm, $kmmi, $LocLabel
) = Preg_Split("[\|]", $CookieDataString);
}
else
// Set the initial default interface startup values
// to current date UTC and Time Zone offset +00:00
// and distance units to kilometers (km).
{
$Year = GMDate('Y');
$TZhhmm = '+00:00';
$kmmi = 'km';
$LocLabel = '-';
// Store interface settings in cookie.
$CookieDataString = "$Year|$TZhhmm|$kmmi|$LocLabel";
setcookie ($CookieName, $CookieDataString, $SetToExpireIn30Days);
}
} // End of if (!isset(_POST['ComputeButton']))
// =======================================
// Read values of all interface arguments.
// Empty values are set to defaults.
$w = Filter_Input(INPUT_POST, 'ComputeButton');
if (IsSet($w))
{
// Read given year value.
$Year = trim(Filter_Input(INPUT_POST, 'Year'));
if ($Year == '') {$Year = GMDate('Y');}
$xYear = $Year;
$Year = @bcAdd($Year, 0);
// Read local time zone offset.
$TZhhmm = trim(Filter_Input(INPUT_POST, 'TZhhmm'));
if ($TZhhmm == '') {$TZhhmm = '+00:00';}
$w = Num_to_HMS($TZhhmm);
$w = HMS_to_Hours($w);
$w = Hours_to_HMS($w, 0, '+', ':');
$TZhhmm = substr($w, 0, 6);
// Determine distance units to apply.
$kmmi = trim(StrToLower(Filter_Input(INPUT_POST, 'DistUnits')));
$kmmi = (substr($kmmi,0,1) <> 'm')? 'km':'mi';
// Read optional location label string. This location should
// match the given time zone. For example, TZ = -05:00
// would not apply to London, UK (TZ = 00:00) and would
// be wrong and look abnormal. It is simply an optional
// reference description to apply to the returned table
// and can consist of any printable text.
$LocLabel = trim(Filter_Input(INPUT_POST, 'LocLabel'));
if ($LocLabel == '') {$LocLabel = '-';}
// -----------------------------------
// Store interface values in a cookie.
$CookieDataString = "$Year|$TZhhmm|$kmmi|$LocLabel";
SetCookie ($CookieName, $CookieDataString, $SetToExpireIn30Days);
}
// =========================================
// Check for errors. FALSE = Error detected.
// Change this code to detect errors.
$ErrFlag = TRUE;
// Check for valid year argument.
if (!Is_Numeric($Year) or $Year < $MinYear or $Year > $MaxYear)
{
$Year = $xYear;
$ErrFlag = FALSE;
$ErrMssg =
"Invalid Year: '$xYear'
The year must be a positive integer in the range
from $MinYear to $MaxYear.
";
}
// Check for invalid time zone argument.
if (abs(HMS_to_Hours($TZhhmm)) > 14)
{
$ErrFlag = FALSE;
$ErrMssg =
"Invalid Time Zone Offset: '$TZhhmm'
The time zone offset from UT must be in the range
from 00:00 to ± 14:00
";
}
// =====================================
// Check if any errors (FALSE) detected.
// If so, display error in RED/WHITE.
if ($ErrFlag === FALSE)
{
$TxColor = 'white';
$BgColor = '#CC0000';
$TextArea2Text = '';
$TextArea1Text =
"= ERROR =========
$ErrMssg";
}
else
// BEGIN MAIN COMPUTATIONS HERE IF NO ERRORS
// DETECTED AT THIS POINT.
// -----------------------------------------
// DEFINE LUNAR EPHEMERIS PARAMETER SETTINGS.
//
// UT is used for internal computations and
// then the output converted for the given
// time zone offset from UT.
{
// Set solar system body ID (301 = Moon = Luna)
$body_id = 301;
// Set start/stop times for a full year
// lunar perigee and apogee ephemeris.
$PrevYear = $Year - 1;
$NextYear = $Year + 1;
$TScale = 'UT';
$start_time = "$PrevYear-Dec-31"; // Start of last day of previous year.
$stop_time = "$NextYear-Jan-02"; // End of first day of following year.
$step_size = '3h';
// Set distance units symbol ('mi' or 'km').
$DistUnits = $kmmi;
// Construct calling URL for JPL Horizons API. Internally, the
// distance units are kilometers. The (DistUnits) variable above
// can override kilometers and use miles instead.
$FROM_NASA_JPL_HORIZONS_API =
"https://ssd.jpl.nasa.gov/api/horizons.api" .
"?format=text" .
"&COMMAND='$body_id'" .
"&OBJ_DATA='YES'" .
"&MAKE_EPHEM='YES'" .
"&EPHEM_TYPE='OBSERVER'" .
"&CENTER='500@399'" .
"&START_TIME='$start_time'" .
"&STOP_TIME='$stop_time'" .
"&STEP_SIZE='$step_size'" .
"&QUANTITIES='20'" .
"&CAL_FORMAT='BOTH'" .
"&SUPPRESS_RANGE_RATE='NO'" .
"&RANGE_UNITS='KM'";
// Determine time scale (UT or LT)
// UT = Universal Time
// LT = Local Time for given TZ
$TZHours = HMS_to_Hours($TZhhmm);
$TimeScale = ($TZHours <> 0)? 'LT':'UT';
// Adjust time scale label string.
// UT1 before 1962
// UTC from 1962
$UT1C = ($Year < 1962)? '1':'C';
$TScaleStr = "$TimeScale$UT1C";
$TScaleStr =($TScaleStr == 'LT1' or $TScaleStr == 'LTC')? 'LT ':"UT$UT1C";
// Get the raw lunar ephemeris table from the JPL Horizons API.
$RawMoonTable = File_Get_Contents($FROM_NASA_JPL_HORIZONS_API);
// exit("<pre>$RawMoonTable</pre>");
// Extract ONLY the table between the ephemeris
// endpoint markers ($$SOE and $$EOE).
$MoonTable = Extract_Lunar_Ephemeris($RawMoonTable, $TZhhmm);
// Construct PA work tables separated by an asterisk (*).
$PAWorkTables = Construct_Lunar_Extrema_Tables($MoonTable);
// Construct table of all PA events for the year.
$OutTable = Compute_All_PA_Events ($PAWorkTables, $kmmi);
// Run output table through final output filter.
$OutTable = Final_Filter($OutTable);
// Define separator text lines.
$TxLine48 = Str_Repeat('=', 48);
$TxLine67 = Str_Repeat('=', 67);
// Determine if Julian or Gregorian calendar applies or both.
// Julian = Dates before 1582-Oct-15-Fri
// Gregorian = Dates since 1582-Oct-15-Fri
// Both = Only the month 1582-Oct
if ($Year < 1583)
{$JGMessage = "\n Dates refer to the old Julian calendar.";}
if ($Year == 1582)
{$JGMessage = "\n Julian calendar used for dates < 1582-Oct-05-Thu\n and Gregorian calendar used for all later dates.";}
if ($Year > 1582)
{$JGMessage = "\n Dates refer to the modern Gregorian calendar.";}
// Define output ephemeris header text.
$HeaderText = " ALL LUNAR PERIGEES AND APOGEES FOR THE YEAR $Year
==================================================\n Time Zone : UT$UT1C$TZhhmm \n Location : $LocLabel\n $JGMessage";
// Construct tabulated computations for display by client web browser.
$TextArea1Text =
"$HeaderText
=================================================
EVENT Calendar Date and Time $TScaleStr Dist. $DistUnits
======= ============================ ==========
$OutTable
======= =============== =========== ==========
EVENT Calendar Date and Time $TScaleStr Dist. $DistUnits
=================================================
";
}
// -------------------------------------------------------------
// Determine the number of text rows to use in output TextArea1.
$Text1Rows = 2 + Substr_Count($TextArea1Text, "\n");
$Text1Cols = 1 + Max(Array_Map('StrLen', PReg_Split("[\n]", trim($TextArea1Text))));
if ($Text1Cols < 80) {$Text1Cols = 50;}
// Generate client webpage to display the computations.
print <<< _HTML
<!DOCTYPE HTML>
<head>
<title>Lunar Perigees and Apogees Calculator</title>
<meta name="viewport" content="width=device-width, initial-scale=0.8">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="description" content="Lunar Perigee and Apogee Calculator">
<meta name="keywords" content="apogee calculator,perigee calculator,lunar perigee,lunar apogee,apogee,perigee,PHPScienceLabs.com">
<meta name="author" content="Jay Tanner - https://www.PHPScienceLabs.com">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="expires" content="-1">
<meta name="robots" content="index,follow">
<meta name="googlebot" content="index,follow">
<style type='text/css'>
BODY
{color:silver; background-color:black; font-family:Verdana; font-size:100%;}
TABLE
{font-size:12px; border: 1px solid black;}
TD
{color:black; background-color:white; line-height:175%; font-size:14px;
padding:6px; text-align:center;}
TEXTAREA
{
color:black; background:white; font-family:monospace; font-size:11pt;
font-weight:bold; border-radius: 8px; border:1px solid black; padding:4px;
white-space:pre;
}
INPUT[type='text']::-ms-clear {width:0; height:0;}
INPUT[type='text']
{
font-size:13pt; font-family:monospace; color:black; background:white;
font-weight:bold; text-align:center; box-shadow:2px 2px 3px #666666;
border:1px solid black; border-radius:4px;
}
INPUT[type='text']:focus
{
font-size:13pt; font-family:monospace; background:white; box-shadow:2px 2px 3px #666666;
border:2px solid blue; text-align:center; font-weight:bold; border-radius:4px;
}
INPUT[type='submit']
{
background:black; color:cyan; font-family:Verdana; font-size:10pt;
font-weight:bold; border-radius:4px; border:4px solid #777777;
padding:3pt;
}
INPUT[type='submit']:hover
{
background:black; color:white; font-family:Verdana; font-size:10pt;
font-weight:bold; border-radius:4px; border:4px solid red;
padding:3pt;
}
HR {background:black; height:2px; border:0px;}
A:link
{
font-size:100%; background:transparent; color:cyan; font-family:Verdana;
font-weight:bold; text-decoration:none; line-height:175%; padding:3px;
border:1px solid transparent;
}
A:visited {font-size:100%; background:transparent; color:silver;}
A:hover
{
font-size:100%; background:yellow; color:black; border:1px solid black;
box-shadow:1px 1px 3px #222222;
}
A:active {font-size:100%; background:yellow; color:black;}
::selection {background-color:yellow; color:black;}
::-moz-selection {background-color:yellow; color:black;}
</style>
</head>
<body>
<form name='form1' method='post' action="$_RUN_">
<table align='top' border='0' cellspacing='1' cellpadding='3'>
<tr><td colspan='3' style='color:white; background:#000044; border:2px solid white; border-radius:8px 8px 0px 0px;'><b>$_INTERFACE_TITLE_</b></td></tr>
<tr>
<td style='background:#FFEAEA;'>Year<br><input name='Year' type='text' value="$Year" size='5' maxlength='4' title=' Span: $MinYear to $MaxYear '></td>
<td style='background:#EAFFEA;'>Time Zone Offset<span style='font-size:140%; font-family:monospace;'><br>UT$UT1C<sub></sub></span> <input name='TZhhmm' type='text' value="$TZhhmm" size='7' maxlength='6' title=' − West or East + ' style='border-width:1px 1px 1px 0px; text-align:left;'></td>
<td style='background:#E0FFFF;'>Distance Units (Km / Mi)<br><input name='DistUnits' type='text' value="$kmmi" size='3' maxlength='2' title=' mi = Miles or km = Kilometers '></td>
</tr>
<tr><td colspan='3' style='background:LightYellow;'>
<br>
<input name='LocLabel' type='text' value="$LocLabel" size='46' maxlength='45' title=' Optional Location Name should properly match the given Time Zone Offset '><br>Optional Location Name
</td></tr>
<tr>
<td colspan='3' style='background:black;'>
<input name='ComputeButton' type='submit' value=' C O M P U T E ' onClick="$_COMPUTING_">
</td>
</tr>
</td></tr>
<tr>
<td colspan="3" style="font-size:10pt; color:black;
background:black; text-align:center;">
<br>
<b><a href='View-Source-Code.php' target='_blank'
style='font-family:Verdana; color:black; background:yellow;
text-decoration:none; border:1px solid black; padding:4px;'>
View Source Code </a></b>
</td></tr>
<tr><td colspan='3' style='background:black; text-align:center;'>
<!-- TABLE WITHIN A TABLE --->
<table width='100' align='bottom'>
<tr>
<td colspan='3' style='color:silver; font-size:10pt; background:black;'>
<span style='color:silver; font-size:9pt; background:black;'>Double-Click Within Text Area to Select ALL Text</span><br>
<textarea name='TextArea1' style='color:$TxColor; background:$BgColor; padding:6px; border:2px solid white;' cols="$Text1Cols" rows="$Text1Rows" ReadOnly OnDblClick='this.select();' OnMouseUp='return true;'>
$TextArea1Text
</textarea></td>
</tr>
<tr><td style='color:gray; background:black;'>
<br>Program by $_AUTHOR_<br>$_REVISION_DATE_ - $PHPVersionStr
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
</td></tr>
</table>
</td></tr>
</form>
</body>
_HTML;
/*
###########################################################################
BELOW ARE UTILITY FUNCTIONS FOR USE WITH THIS PROGRAM. SOME FUNCTIONS ARE
CUSTOMIZED JUST FOR THIS APPLICATION AND MAY NOT TRANSPORT TO OTHER WORKS
OUT OF CONTEXT.
###########################################################################
============================================================================
This function converts a time elements string ('h m s') to decimal hours.
NO DEPENDENCIES
============================================================================
*/
function HMS_to_Hours ($hhmmss)
{
$hms = StrToLower(trim($hhmmss));
$hms = Str_Replace('h', ' ', $hms);
$hms = Str_Replace('m', ' ', $hms);
$hms = Str_Replace('s', ' ', $hms);
$hms = Str_Replace(':', ' ', $hms);
$hms = PReg_Replace("/\s+/", " ", trim($hms));
$whms = PReg_Split("[ ]", $hms);
$whmscount = count($whms);
$hh = ($whmscount >= 1)? bcAdd($whms[0],"0", 20) : "0";
$mm = ($whmscount >= 2)? bcAdd($whms[1],"0", 20) : "0";
$ss = ($whmscount >= 3)? bcAdd($whms[2],"0", 20) : "0";
$NumSign = (substr($hhmmss,0,1) == '-')? '-' : '';
$hh = Str_Replace('-', '', $hh);
$hh = Str_Replace('+', '', $hh);
if (substr($hhmmss,0,1) == '+') {$NumSign = '+';}
$w2 = bcAdd(bcAdd(bcMul($hh,'3600',20),bcMul($mm,'60',20),20),$ss,20);
if (bcComp($w2, '0', 20) == 0) {$NumSign = '';}
$w = bcAdd(bcDiv($w2,3600, 20), '0.00000000000000005', 16);
return $NumSign.Rtrim(Rtrim($w, '0'), '.');
} // end of HMS_to_Hours (hhmmss)
/* ######################################################################## */
/*
===========================================================================
This function returns the equivalent H:M:S time string with
several formatting options.
hours = � Decimal hours value
ssDecimals = Number of decimals in seconds part
posSign = Symbol to use for positive values ('' or '+')
'' = Empty = Return numbers only, no symbols.
'+' = Attach '+' sign to positive values.
Has no effect on negative values.
SymbolsMode = If or not to attach time symbols (h m s) or (:)
'h' = '01h 02m 03s' (Default)
':' = '01:02:03'
'' = '01 02 03'
NO DEPENDENCIES
===========================================================================
*/
function Hours_to_HMS ($hours, $ssDec=0, $posSignSymb='', $SymbMode='h')
{
$_h_ = $_m_ = $_s_ = '';
if (trim($posSignSymb) == '') {$posSignSymb = FALSE;}
$sign = ($hours < 0)? '-' : '';
$hours = abs($hours);
if (($posSignSymb === TRUE or $posSignSymb == '+') and $sign == '')
{$sign = '+';}
$hh = floor($hours);
$minutes = 60*($hours - $hh); $mm = floor($minutes);
$seconds = 60*($minutes - $mm); $ss = SPrintF("%1.3f", $seconds);
$hh = SPrintF("%02d", $hh);
$mm = SPrintF("%02d", $mm);
$ss = SPrintF("%1.$ssDec" . "f", $ss);
if ($ss == 60) {$ss = 0; $mm++;}
if ($mm == 60) {$mm = 0; $hh++;}
$hh = SPrintF("%02d", $hh);
$mm = SPrintF("%02d", $mm);
$ss = SPrintF("%1.$ssDec" . "f", $ss);
if ($ss < 10) {$ss = "0$ss";}
if ($SymbMode == 0 or $SymbMode == '')
{$_h_ = $_m_ = $_s_ = ' ';}
if ($SymbMode == 1 or $SymbMode == ':')
{$_h_ = ':'; $_m_ = ':'; $_s_ = '';}
if ($SymbMode == 2 or StrToLower($SymbMode) == 'h')
{$_h_ = 'h '; $_m_ = 'm '; $_s_ = 's';}
$w = "$sign$hh$_h_$mm$_m_$ss$_s_";
return $w;
} // end of Hours_to_HMS(...)
/* ######################################################################## */
/*
---------------------------------------------------------------------------
This function performs LaGrange interpolation within a 2-column
data table. Data intervals can be linear or non-linear forward.
Given (x) value within the given range, this function interpolates
the corresponding (y) value.
Revised, stand-alone version.
Table XY column format example:
$XYTable =
'
481 10.012
482 10.503
483 11.083
';
---------------------------------------------------------------------------
*/
function Lagrange_Interp ($XYDataTable, $xArg)
{
$XDataStr = $YDataStr = '';
$XY = PReg_Split("[ ]", PReg_Replace("/\s+/", ' ', trim($XYDataTable)));
$TotalDatacount = count($XY);
$n = $TotalDatacount / 2;
if ($TotalDatacount < 4 )
{return "ERROR: There must be at least two XY data pairs.";}
if ($n != floor($n + 0.5))
{return "ERROR: XY Data count Mismatch. Odd data element.";}
$n = $TotalDatacount / 2;
for($i=0; $i < $TotalDatacount; $i += 2)
{
$XDataStr .= $XY[$i] . ' ';
$YDataStr .= $XY[$i+1] . ' ';
}
$X = PReg_Split("[ ]", trim($XDataStr));
$Y = PReg_Split("[ ]", trim($YDataStr));
$x = trim($xArg); if ($x == '') {$x = 0.0;}
$y = 0.0;
for ($i=0; $i < $n; $i++)
{
$Li = 1.0;
for ($j=0; $j < $n; $j++)
{
if ($j <> $i) // Skip this cycle when j == i
{
$Li = ($Li*($x - $X[$j])) / ($X[$i] - $X[$j]);
}
} // next j
$y += ($Y[$i] * $Li);
} // next i
return $y;
} // end of Lagrange_Interp (XYDataTable, xArg)
/* ######################################################################## */
/*
===========================================================================
This function translates a purely numeric time string into standard form.
Applies ONLY to purely numerical time strings.
time format used = 00 to 24 hours
----------------------------------
EXAMPLES
time String Translates to
----------- -------------
1 01:00:00
12 12:00:00
123 01:23:00
1234 12:34:00
12345 01:23:45
123456 12:34:56
This function does NOT check for errors.
NO DEPENDENCIES
===========================================================================
*/
function Num_to_HMS ($NumHMSVal)
{
$w = trim($NumHMSVal);
if ($w == '') {return '00:00:00';}
if (!Is_Numeric($w)) {return $NumHMSVal;}
$FirstChar = substr($w,0,1);
$NumSign = (IntVal($w) < 0)? '-' : '+';
$w = Str_Replace($NumSign, '', $w);
if (StrLen($w) == 1) {$w = "0$w";}
if (StrLen($w) == 2) {$w = $w."0000";}
if (StrLen($w) == 3) {$w = "0$w";}
if (StrLen($w) == 4) {$w = $w."00";}
if (StrLen($w) == 5) {$w = "0$w";}
// if ($FirstChar <> '+' and $NumSign == '+') {$NumSign = '';}
$hh = SPrintF("%02d", IntVal(substr($w,0,2)));
return $NumSign.$hh.':'.substr($w,2,2).':'.substr($w,4,2);
} // end of Num_to_HMS (...)
/* ######################################################################## */
/*
============================================================================
This function returns the time of an extremum (minimum or maximum), if
any, within a given time vs event table. For example, this function
can be used to find the times of perihelion, aphelion, perigee, apogee
or any general periapsis or apoapsis times. It is based on a 5-point
data table and the extremum is computed from a polynomial derived from
the given data.
In this program, this function is used to compute the JD (Julian Date)
of a perigee or apogee event.
ARGUMENT:
DataTableStr = 5-point paired numerical data table.
ERRORS:
No error checking is done and the function assumes that an extremum
exists within the given data table.
possible Uses:
XY-Data = JDTT vs LunarDistKm
NO DEPENDENCIES
============================================================================
*/
function Extremum_5 ($DataTableStr)
{
// -----------------------------------------
// Read data table and parse numeric values.
$DataTable = PReg_Replace("/\s+/", ' ', trim($DataTableStr));
@list ($x1,$y1, $x2,$y2, $x3,$y3, $x4,$y4, $x5,$y5)
= PReg_Split("[ ]", $DataTable);
$interval = $x2 - $x1;
$a = $y2 - $y1;
$b = $y3 - $y2;
$c = $y4 - $y3;
$d = $y5 - $y4;
$e = $b - $a;
$f = $c - $b;
$g = $d - $c;
$h = $f - $e;
$i = $g - $f;
$j = $i - $h;
$k = $j/24;
$m = ($h + $i)/12;
$n = $f/2 - $k;
$p = ($b + $c)/2 - $m;
$q = $r = 0;
while ($r < 25)
{
$s = 6*($b + $c) - $h - $i + 3*$q*$q*($h + $i) + 2*$q*$q*$q*$j;
$t = $j - 12*$f;
$q = $s/$t;
$r++;
}
return $x3 + $q*$interval;
} // end of Extremum_5(...)
/* ######################################################################## */
/*
===========================================================================
This is the inverse Julian date function. Given any general Julian date
on the old Julian or modern Gregorian calendar, it returns the correspond-
ing full date and time string. The fractional part of the Julian date
indicates the time of day.
RE-ENGINEERED FOR THIS PROGRAM TO NEAREST WHOLE SECOND.
In this program, this function is used to compute the date and time of
a perigee or apogee event given its computed JD (Julian Date).
INPUT ARGUMENTS: (JDStr, AMPM24, ssDecimals)
JDStr = Julian date as numeric string, like '2432959.1843657209'
AMPM24 = time Mode
'24h' = 24h military time mode = Default
'A|P|AM|PM|AMPM|PMAM' All = 12h AM/PM civil time mode
ssDecimals = Decimals at which to round off seconds part.
Default = 0
----------------------
INPUT/OUTPUT EXAMPLES:
print Inv_JD ('2433057.13271234131', '', '');
// = G+1949-May-20-Fri 15:11:06
print Inv_JD ('2433057.13271234131', 'AP');
// = G+1949-May-20-Fri 03:11:06 PM
print Inv_JD ('1433057.13271234131', '');
// = J+1949-May-07-Fri 15:11:06
NO DEPENDENCIES
===========================================================================
*/
function Inv_JD ($JDStr, $AMPM24='24h', $ssDecimals=0)
{
$Q = 32; // Internal working decimals.
$q = floor(trim($ssDecimals));
$ssFmt = ($q == 0)? "%02d" : "%0".(3 + $q).".$q".'f';
$ssAdj = ($q == 0)? 0.5 : 0;
$JD = trim($JDStr);
// Automatically determine calendar mode to use
// according to the JD value. If not the Julian
// calendar, then default to Gregorian calendar.
// Julian calendar if JD < 2299160.5
// Gregorian calendar if JD >= 2299160.5
$JG = ($JD < 2299160.5)? 'J':'G';
$J = bcAdd($JD, '0.5', $Q);
$timeMode = substr(StrToUpper(trim($AMPM24)),0,1);
// Compute time of day in hours since beginning of date.
$timeHours = bcMul('24', bcSub($J, bcAdd($J, '0'), $Q), $Q);
// Compute Julian Day Number from Julian date value.
$JDNum = bcAdd($J, '0');
// Compute date elements (m,d,y) according to (JDMode).
$MDYstr = ($JG == 'J')? JDtoJulian($JDNum) : JDtoGregorian($JDNum);
list($m,$d,$y) = PReg_Split("[\/]", $MDYstr);
$y = SPrintF("%+d", $y);
$dd = SPrintF("%02d", $d);
// Get month name and day of week abbreviations.
$Mmm = ($JG == 'J')? JDMonthName($JDNum, 2) : JDMonthName($JDNum, 0);
$DoW = JDDayOfWeek($JDNum, 2);
// Compute time of day elements (hh,mm,ss) = 00h to 24h
$hours = $timeHours;
$hh = bcAdd($hours, '0');
$minutes = bcMul('60', bcSub($hours, $hh, $Q), $Q);
$mm = bcAdd($minutes, '0');
$seconds = bcMul('60', bcSub($minutes, $mm, $Q), $Q);
$ss = SPrintF("%06.3f", $seconds); // 0x.xxx
// Construct time of day string.
$hh = SPrintF("%02d", $hh);
$mm = SPrintF("%02d", $mm);
$ss = SPrintF($ssFmt, $seconds + $ssAdj); // 0x.xxx
// Account for that blasted 60s glitch.
if ($ss == 60) {$mm += 1; $ss = 0;}
if ($mm == 60) {$hh += 1; $mm = 0;}
// Reconstruct time of day string.
$hh = SPrintF("%02d", $hh);
$mm = SPrintF("%02d", $mm);
$ss = SPrintF("%02d", $ss); // ss
// Adjust for AM/PM/24h time return mode.
// If not AM/PM mode, then default to 24h mode.
$AMPM = ''; $hh = floor($hh);
if ($timeMode == 'A' or $timeMode == 'P')
{
if ($hh > 0 and $hh < 12) {$AMPM = ' AM';}
if ($hh == 12) {$AMPM = ' PM';}
if ($hh > 12) {$AMPM = ' PM'; $hh -= 12;}
if ($hh == 0) {$AMPM = ' AM'; $hh = 12;}
}
$hh = SPrintF("%02d", $hh);
$BCAD = ($y < 0)? 'BC':'AD';
// $y = SPrintF("% 4d", $y);
// exit("'$y'");
// Done.
return "$JG$y-$Mmm-$dd-$DoW $hh:$mm:$ss$AMPM";
} // end of Inv_JD (...)
/* ######################################################################## */
/*
This function extracts only the lunar ephemeris
columns needed to compute the PA events.
*/
function Extract_Lunar_Ephemeris ($EphemerisTableText, $TZhhmm='+00:00')
{
$wArray = explode("\n", trim($EphemerisTableText));
$wCount = count($wArray);
$TZFracDay = HMS_to_Hours(trim($TZhhmm)) / 24;
$wText = '';
$j=0;
for ($i=0; $i < $wCount; $i++)
{
$CurrLine = trim($wArray[$i]);
$CurrLine = PReg_Replace("/\s+/", " ", trim($CurrLine));
if ($CurrLine == '$$SOE') {$j=1;}
if ($CurrLine == '$$EOE') {break;}
if ($j == 1 and $CurrLine <> '$$SOE')
{
list($DateStr, $UTStr, $JDUT, $DistKm, $RangeRateKm)
= PReg_Split("[ ]", trim($CurrLine));
$JDNum = floor($JDUT + 0.5);
$DoW = JDDayOfWeek($JDNum, 2);
$JDUT = SPrintF("%1.9f", $JDUT);
// Compute date and time for the given TZ offset.
$JDLT = $JDUT + $TZFracDay;
$JDNumLT = floor($JDLT + 0.5);
$DoWLT = JDDayOfWeek($JDNumLT, 2);
$JDLT = SPrintF("%1.9f", $JDLT);
$DateStrLT = Inv_JD($JDLT, 'AP');
$DistKm = SPrintf("%10.3f", $DistKm);
$RangeRateKm = SPrintf("%+1.7f", $RangeRateKm);
$RangeRateKm = ($RangeRateKm < 0)? '-':'+';
$wText .= "$DateStrLT $JDLT $DistKm $RangeRateKm\n";
}
}
// Filter out certain unneeded elements.
$w = trim(Str_Replace('J+', '', $wText));
$w = trim(Str_Replace('G+', '', $wText));
$w = trim(Str_Replace(':00 AM', ' AM', $w));
$w = trim(Str_Replace(':00 PM', ' PM', $w));
$w = trim(Str_Replace(' ', ' ', $w));
return $w;
} // End of Extract_Lunar_Ephemeris (...)
/* ######################################################################## */
/*
This function uses the +/= transitions markers to
construct all tables used for the computation of
the lunar perigees and apogees.
Read forward looking for a change of +/- signs at
the end of the lines.
Change from - to + means perigee
Change from + to - means apogee
Collect up lines from 2 before to 2 lines after
the trasition points to make 5-line tables.
There will be at least two tables and sometimes
a third table due to a second perigee or apogee
in the same calendar month.
*/
function Construct_Lunar_Extrema_Tables ($MonthTable)
{
$mTable = trim($MonthTable);
$wArray = explode("\n", $mTable);
$wCount = count($wArray);
$OutTable = '';
for($i=1; $i < $wCount; $i++)
{
$PrevLine = trim($wArray[$i-1]);
$CurrLine = trim($wArray[$i]);
// If signs differ, the create 5-line PA work-table.
if (substr($PrevLine, -1) <> substr($CurrLine, -1))
{
$k = 0;
for($j=$i-2; $j < $i+3; $j++)
{
$k += 1;
@$ww = substr($wArray[$j], -30);
$OutTable .= "$ww\n";
if ($k % 5 == 0 and $k <> 0) {$OutTable .= "*\n";}
}
}
}
$OutTable = Str_Replace(' -', '', $OutTable);
$OutTable = Str_Replace(' +', '', $OutTable);
return RTrim(trim($OutTable), '*');
} // End of Construct_Lunar_Extrema_Tables(...)
/* ######################################################################## */
/*
This function computes all the perigee and apogee events in the PA
work-tables. The time zone has already been taken into account
within the PA work-tables prior to calling this function.
*/
function Compute_All_PA_Events ($PAWorkTables, $DistUnits='km')
{
GLOBAL $Year;
$PAWTables = trim($PAWorkTables);
$wArray = PReg_Split("[\*]", $PAWTables);
$wCount = count($wArray);
$DU = substr(StrToLower(trim($DistUnits)),0,1);
$mi = ($DU == 'm')? 1.609344 : 1.0;
// Process each PA work-table within array
// and compute each PA event date and time
// and distance.
$OutTable = '';
for($i=0; $i < $wCount; $i++)
{
$wTable = trim($wArray[$i]);
// Compute the PA event date and time string for the local TZ
$PAJDLT = Extremum_5 ($wTable);
$PAJDLT = SPrintF("%1.9f", $PAJDLT);
$PADateTimeLT = Inv_JD($PAJDLT, 'AP');
$PADateTimeLT = Str_Replace('G+', '', $PADateTimeLT);
$PADateTimeLT = Str_Replace('J+', '', $PADateTimeLT);
// Compute the distance of the PA event in miles or kilometers.
$DistMiKm = Lagrange_Interp($wTable, $PAJDLT);
$PAMarker = (substr($DistMiKm,0,1) == '3')? 'Perigee ':'Apogee ';
$DistMiKm = SPrintF("%10.3f", $DistMiKm/$mi);
// Handle special overflow case(s) at ends.
$PAJDLT = ' ';
$w = "$PAMarker $PAJDLT $PADateTimeLT $DistMiKm\n";
if (IntVal($PADateTimeLT) == $Year) {$OutTable .= "$w";}
}
return trim($OutTable);
}
/* ######################################################################## */
//Perigee 2460322.936170310
//9 to 25 = JD
/*
This function filters the table and separates
the months of the year for easier readability.
*/
function Final_Filter ($FinalTableText)
{
$T = trim($FinalTableText);
$wArray = Preg_Split("[\n]", $T);
$wCount = count($wArray);
$OutTable = '';
for ($i=1; $i < $wCount; $i++)
{
$PrevLine = trim($wArray[$i-1]);
$PrevMmm = substr($PrevLine, 32, 3);
$CurrLine = trim($wArray[$i]);
$CurrMmm = substr($CurrLine, 32, 3);
if ($PrevMmm <> $CurrMmm)
{$LineBreak = "\n";}
else
{$LineBreak = '';}
$OutTable .= "$PrevLine$LineBreak\n";
}
$OutTable = $OutTable.$wArray[$i-1];
$OutTable = Str_Replace('e ', 'e ', $OutTable);
return trim($OutTable);
}
/* ######################################################################## */
?>