<?php
/*
This is a function to compute the local moonrise, transit
and moonset times for a single date or a range of dates.
It calls the NASA/JPL Horizons API.
AUTHOR : Jay Tanner - 2025
LANGUAGE : PHP v8.2.12
*/
// ------------------------
// Define test/demo values.
$LonDeg = '-76.862737';
$LatDeg = '+42.904788';
$AltMet = '0';
$StartDate = '2025-05-20';
$StopDate = '2025-05-25';
$TimeZone = '-05:00';
$DaySumYN = 'Yes'; // Yes = Daylight Time / No = Standard Time
// ---------------------------------------------------------------
// Compute table of rising, transit and setting times of the Moon.
$RTSTimes = R_T_S_Times ('301',$StartDate,$StopDate,$TimeZone,$DaySumYN,
$LonDeg,$LatDeg,$AltMet);
print <<< _END_of_HTML_PAGE
<!DOCTYPE HTML>
<head>
<title>Moonrise, Transit, Moonset Demo</title>
<!-- Top yellow source code view link. --->
<br>
<table width="420" align="bottom" cellspacing="1" cellpadding="3">
<tr>
<td colspan="1" style="font-size:10pt; color:black; background:white;
text-align:left;">
<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>
</table>
<pre style='font-family:monospace; font-size:10.5pt; font-weight:bold;'>
############################################################
TABLE OF LOCAL RISING, TRANSIT AND SETTING TIMES OF THE MOON
VIA THE NASA/JPL HORIZONS API
GPS Lon Deg = $LonDeg
GPS Lat Deg = $LatDeg
Alt Meters = $AltMet
Start Date = $StartDate
Stop Date = $StopDate
Time Zone = $TimeZone
Day/Sum = $DaySumYN
############################################################
$RTSTimes
</pre>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
_END_of_HTML_PAGE;
/*
###########################################################################
This function returns a topocentric ephemeris of rise/transit/set times
from the NASA/JPL Horizons API. A topocentric ephemeris takes the location
and altitude of the observer into account. Standard Earth refraction model
is applied.
DaySumYN = Means Daylght Saving/Summer Time 'Yes|No'
'No' = Use Standard Clock Time (Default)
'Yes' = Use Daylight Saving/Summer Clock Time
Angles are expressed in degrees.
COLUMN CONTENT
====== =======================================================================
1 Date and Time String (UT or ZONE)
2 Sp (Solar Presence Symbol)
3 Lp (Lunar Presence Symbol)
4 Azimuth Angle (Compass Direction Reckoned Clockwise From North = 0)
5 Elevation Angle (Relative to Horizon = 0 degrees)
6 Apparent Visual Magnitude or Brightness Level
7 S-brt (Brightness of 1 Square Arcsecond of Visible Disc)
NO DEPENDENCIES
###########################################################################
*/
function R_T_S_Times ($BodyID,$StartDate,$StopDate,$TimeZone,$DaySumYN,
$LonDeg,$LatDeg,$AltMet)
{
// ===========================================================================
// Construct query URL for the NASA/JPL Horizons API.
$BodyID = trim($BodyID);
$Command = URLEncode($BodyID);
$AltKm = trim($AltMet) / 1000;
/* -----------------------------------------------------------
Adjust for Daylight/Summer Time, if indicated. This assumes
that the Time Zone string is given in the standard +-HH:mm
format or an error may occur.
*/
$DaySumYN = substr(StrToUpper(trim($DaySumYN)),0,1);
$DaySumAdj = ($DaySumYN == 'N')? 0:1;
list($TZHH, $TZmm) = PReg_Split("[\:]", $TimeZone);
$TZSign = substr($TZHH,0,1);
$TZHours = (($TZSign == '-')? -1:1)*(abs($TZHH) + $TZmm/60) + $DaySumAdj;
$i = StrPos($TZHours, '.'); if ($i == FALSE) {$TZHours .= '.00';}
$i = StrPos($TZHours, '.');
$TZHH = $TZSign.SPrintF("%02d", abs(substr($TZHours,0,$i)));
$TimeZone = "$TZHH:$TZmm";
/* ----------------------------------------------------------
Account for special case of the moon. Set distance (range)
units = 'KM' and add SOT angle (Quantity #23) for finding
the simple lunar phase angle.
*/
$AUorKM = 'AU'; $SOT = ''; // Initialize.
if ($BodyID == '301') {$AUorKM = 'KM';}
// -------------------------------------
// Construct URL for Horizons API query.
$From_Horizons_API =
"https://ssd.jpl.nasa.gov/api/horizons.api?format=text" .
"&COMMAND='$Command'" .
"&OBJ_DATA='NO'" .
"&MAKE_EPHEM='YES'" .
"&EPHEM_TYPE='OBSERVER'" .
"&CAL_FORMAT='CAL'" .
"&REF_SYSTEM='ICRF'" .
"&APPARENT='REFRACTED'" .
"&CENTER='COORD@399'" .
"&COORD_TYPE='GEODETIC'" .
"&SITE_COORD='$LonDeg,$LatDeg,$AltKm'" .
"&TIME_DIGITS='MINUTES'" .
"&TIME_ZONE='$TimeZone'" .
"&START_TIME='$StartDate%2000:00:00%20UT'" .
"&STOP_TIME='$StopDate%2023:59:59'" .
"&STEP_SIZE='1 MINUTE'" .
"&EXTRA_PREC='YES'" .
"&R_T_S_ONLY='TVH'" .
"&QUANTITIES='4,9'" .
"&CSV_FORMAT='YES'" ;
// ============================================
/* -----------------------------------------------------------------------
Send query to Horizons API to obtain the apparent topocentric ephemeris
data we need for the R-T-S times of the given body ID.
*/
$RTS = Str_Replace(",\n", " \n", File_Get_Contents($From_Horizons_API));
/* ----------------------------------------------------------------------
If no ephemeris data is found, then return an empty string as an error
state indicator.
*/
if (StrPos($RTS, '$$SOE') === FALSE) {return $RTS;}
/* -------------------------------------------------------------------------
DO NOT TRIM HERE BECAUSE INITIAL CHARACTER MAY BE A SPACE INDICATING AN
'AD' YEAR. 'BC' YEARS BEGIN WITH A SINGLE 'b' AS THE INITIAL CHARACTER.
BAD PRACTICE. LEADING/TRAILING SPACES SHOULD NOT BE USED AS TABLE DATA.
IT CAN SOMETIMES LEAD TO CONFUSION WHEN READING A LINE OF TABULAR TEXT.
IF A DATA COLUMN IS EMPTY, A SPECIAL CHARACTER SHOULD BE ASSIGNED AS AN
INDICATOR OF AN EMPTY COLUMN. EXAMPLE (~), SIMILAR TO USING (n/a).
SOMETHING OTHER THAN SIMPLY LEAVING IT EMPTY OR USING A SPACE CHARACTER.
*/
return $RTS;
} // End of R_T_S_Times(...)
?>