<?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;'>
&nbsp;View Source Code&nbsp;</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(...)


?>