<?php
/*
###########################################################################
RISE/TRANSIT/SET TIMES CALCULATOR AND NASA/JPL DATA QUERY PORTAL
WITH 30-DAY COOKIE.
The ICRF (International Celestial Reference Frame) is used used.
The B1950/FK4 system does NOT apply to topocentric coordinates,
but ONLY to epochal astrometric coordinates.
Selecting the TT time scale will force the Time Zone setting to 'No',
since it is ignored and does not apply to that scale.
The Time Zone setting is only applied to the UT Time Scale. When the
Time Scale is UT and the Time Zone is +00:00, then UT is assumed.
Otherwise, local ZONE Time is assumed.
When Daylight/Summer Time is selected, +1 hour is applied to the Time
Zone internally.
AUTHOR : Jay Tanner - 2024
LANGUAGE : PHP v7.4.9
LICENSE : Public Domain
NOTE:
All ephemerides are generated as plain-text CSV data tables.
###########################################################################
*/
/* -----------------------------
Is OB a clinic down the hall?
*/
ob_start();
// -----------------------------------
// Get current year from system clock.
$cYear = date('Y');
// ---------------------------------------------------------------
// Define the program cookie name and set it to expire in 30 days.
$CookieName = 'Rise-Transit-Set-Times-Calculator';
$ExpiresIn30Days = time() + 30*86400;
// ---------------------------------
// Define PHP program and HTML info.
$_AUTHOR_ = "by Jay Tanner - $cYear";
$_VERSION_ = 'v1.00 - ';
$at = "at Local Time ";
$LTC = "UTC";
$_SCRIPT_FILE_PATH_ = Filter_Input(INPUT_SERVER, 'SCRIPT_FILENAME');
$_REVISION_DATE_ = $_VERSION_ . 'Revised: '
. date("Y-F-d-l $at h:i:s A ($LTC", FileMTime($_SCRIPT_FILE_PATH_))
. "−05:00)";
$_BROWSER_TAB_TEXT_ = "Rise | Transit | Set Times Calculator";
$_INTERFACE_TITLE_ = "<span style='font-size:15pt;'>Rise | Transit | Set Times Calculator</span>
<span style='font-size:13pt;'><br>+<br>General Data Query Portal</span>
<br><br>
<span style='font-size:11pt;'>Built Around the NASA/JPL Horizons API </span><br>
<span style='font-size:9pt;'>$_VERSION_ $_AUTHOR_</span>";
// ---------------------------------------------------
// Define JavaScript message to display while working.
$_COMPUTING_ = "TextArea1.innerHTML=' W.O.R.K.I.N.G --- This may take several seconds.';";
// --------------------------------------
// Special GLOBALs just for this program.
GLOBAL $TargetBodyNameText, $xDSSTText, $MoonPhaseTableText;
// ----------------------------------
// Define default home location data.
$HomeLoc = 'Central New York State, USA';
$HomeLon = '-76.862737';
$HomeLat = '+42.904788';
$HomeAlt = '+150'; // Meters
// -----------------------------------------
// Define 3-letter month name abbreviations.
define('MONTHS', 'JanFebMarAprMayJunJulAugSepOctNovDec');
// -------------------------------------------------------
// Define a text separator bar of 80 pound (#) characters.
$_BAR_ = Str_Repeat('#', 80);
/* -----------------------------------------------
Define text for HTML title-text INFO blocks.
Just hover over the 'INFO' text and a yellow
menu will be displayed.
*/
$BodyIDTitleText =
" This can be any NASA/JPL Body ID or Special Query.
QUERY RETURNS
news Horizons On-Line System News + Backlog
? Basic information on using the API
?! Technical information on using the API
* List of Major Bodies and IDs or Record #s
MB Same as * (Also used as a search wildcard)
-* List of Spacecraft, Vehicles, Rovers, etc.
com; List of Comets and Record #s (very long list)
1; The Asteroid Ceres (Asteroid #1)
Vesta The Asteroid Vesta (Asteroid #4)
10 The Sun (Sol)
301 The Moon (Luna)
199 The Planet Mercury
499 The Planet Mars
etc.";
// ----------------------------------------------------
// Read [SUBMIT] button state.
$w = Filter_Input(INPUT_POST, 'SubmitButton');
// Do this ONLY if the [SUBMIT] button was NOT clicked.
if (!IsSet($w))
{
// ----------------------------------------------------------------------
// If this program is being called externally, rather than being executed
// by clicking the [SUBMIT] button, and an active cookie also exists,
// then restore the previously saved interface settings from it. If
// the user leaves and comes back later, all the interface settings
// will be remembered and restored if the cookie was not deleted.
// Try to read cookie content, if any.
$Cookie = Filter_Input(INPUT_COOKIE, $CookieName);
// Check if cookie content exists.
// IsSet === Has content === Not blank space(s) or empty
if (IsSet($Cookie))
{
$CookieDataString = Filter_Input(INPUT_COOKIE, $CookieName);
list
(
$BodyID,
$StartYear,
$StartMonth,
$StartDay,
$TZSign,
$TZHH,
$TZmm,
$StopYear,
$StopMonth,
$StopDay,
$DSSTYN,
$LocName,
$LonDeg,
$LatDeg,
$AltMet
) = Preg_Split("[\|]", $CookieDataString);
}
else
// -----------------------------------------------------------
// If there is no previous cookie with the interface settings,
// then set the initial default interface startup values and
// store them in a new cookie.
{
$BodyID = '301';
$StartYear = date('Y');
$StartMonth = date('M');
$StartDay = date('d');
$TZSign = '-';
$TZHH = '05';
$TZmm = '00';
$StopYear = date('Y');
$StopMonth = date('M');
$StopDay = date('d');
$DSSTYN = 'No'; // 'Yes|No'
$LocName = $HomeLoc;
$LonDeg = $HomeLon;
$LatDeg = $HomeLat;
$AltMet = $HomeAlt;
// -------------------------------------------
// Store current interface settings in cookie.
$CookieDataString = "$BodyID|$StartYear|$StartMonth|$StartDay|$TZSign|$TZHH|$TZmm|$StopYear|$StopMonth|$StopDay|$DSSTYN|$LocName|$LonDeg|$LatDeg|$AltMet";
SetCookie ($CookieName, $CookieDataString, $ExpiresIn30Days);
} // End of else {...}
} // End of if (!isset(_POST['SubmitButton']))
// ------------------------------------------
// Read values of interface argument(s) and
// set any empty arguments to default values.
// Read [SUBMIT] button state.
$SubmitButton = Filter_Input(INPUT_POST, 'SubmitButton');
// Check [SUBMIT] button state.
// IsSet TRUE Means Clicked.
if (IsSet($SubmitButton))
{
// -----------------------------------------------
// Read arguments from interface input text boxes.
$BodyID = trim(Filter_Input(INPUT_POST, 'BodyID'));
if ($BodyID == '') {$BodyID = '*';}
$StartYear = trim(Filter_Input(INPUT_POST, 'StartYear'));
if ($StartYear == '') {$StartYear = date('Y');}
$StartYear = SPrintF("%04d", $StartYear);
$StartMonth = trim(Filter_Input(INPUT_POST, 'StartMonth'));
if ($StartMonth == '') {$StartMonth = date('M');}
$StartMonth = trim(UCFirst(StrToLower(substr($StartMonth,0,3))));
if (Is_Numeric($StartMonth))
{
$m = IntVal(trim($StartMonth));
if ($m < 0 or $m > 12) {$m = date('m');}
$StartMonth = substr(MONTHS, 3*($m-1), 3);
}
$StartDay = trim(Filter_Input(INPUT_POST, 'StartDay'));
if ($StartDay == '') {$StartDay = date('d');}
$StartDay = SPrintF("%02d", $StartDay);
$TZSign = trim(Filter_Input(INPUT_POST, 'TZSign'));
$TZHH = trim(Filter_Input(INPUT_POST, 'TZHH'));
$TZmm = trim(Filter_Input(INPUT_POST, 'TZmm'));
if ($TZSign == '' and $TZHH == '' and $TZmm == '')
{$TZSign = '-'; $TZHH = '05'; $TZmm = '00';}
if ($TZHH == '') {$TZHH = '00';}
if ($TZmm == '') {$TZmm = '00';}
if ($TZSign == '') {$TZSign = '+';}
$TZHH = SPrintF("%02d", $TZHH);
$TZmm = SPrintF("%02d", $TZmm);
if ($TZHH == '00' and $TZmm == '00') {$TZSign = '+';}
$StopYear = trim(Filter_Input(INPUT_POST, 'StopYear'));
if ($StopYear == '') {$StopYear = date('Y');}
$StopYear = SPrintF("%04d", $StopYear);
$StopMonth = trim(Filter_Input(INPUT_POST, 'StopMonth'));
if ($StopMonth == '') {$StopMonth = date('m');}
$StopMonth = trim(UCFirst(StrToLower(substr($StopMonth,0,3))));
if (Is_Numeric($StopMonth))
{
$m = IntVal(trim($StopMonth));
if ($m < 0 or $m > 12) {$m = date('m');}
$StopMonth = substr(MONTHS, 3*($m-1), 3);
}
$StopDay = trim(Filter_Input(INPUT_POST, 'StopDay'));
if ($StopDay == '') {$StopDay = date('d');}
$StopDay = SPrintF("%02d", $StopDay);
$DSSTYN = trim(Filter_Input(INPUT_POST, 'DSSTYN'));
$DSSTYN = (substr(StrToUpper($DSSTYN),0,1) == 'Y')? 'Yes':'No';
$LocName = trim(Filter_Input(INPUT_POST, 'LocName'));
if ($LocName == '') {$LocName = $HomeLoc;}
// ----------------------------------------------------
// Get longitude input as decimal degrees or DMS string
// and convert to decimal degrees.
$LonDeg = trim(Filter_Input(INPUT_POST, 'Longitude'));
if ($LonDeg == '') {$LonDeg = $HomeLon;}
$LonDeg = DMS_to_Degrees($LonDeg, 9);
// ---------------------------------------------------
// Get latitude input as decimal degrees or DMS string
// and convert to decimal degrees.
$LocName = trim(Filter_Input(INPUT_POST, 'LocName'));
if ($LocName == '') {$LocName = $HomeLoc;}
$LatDeg = trim(Filter_Input(INPUT_POST, 'Latitude'));
if ($LatDeg == '') {$LatDeg = $HomeLat;}
$LatDeg = DMS_to_Degrees($LatDeg, 9);
$AltMet = trim(Filter_Input(INPUT_POST, 'AltMet'));
if ($AltMet == '') {$AltMet = '+0';}
$AltMet = SPrintF("%+d", $AltMet);
// --------------------------------------
// Store interface arguments in a cookie.
$CookieDataString = "$BodyID|$StartYear|$StartMonth|$StartDay|$TZSign|$TZHH|$TZmm|$StopYear|$StopMonth|$StopDay|$DSSTYN|$LocName|$LonDeg|$LatDeg|$AltMet";
SetCookie ($CookieName, $CookieDataString, $ExpiresIn30Days);
}
// -------------------------------------------------------------
// Construct Start/Stop Dates and Time Zone strings for the API.
// Must be perfectly formatted or an error message will result.
$StartDate = "$StartYear-$StartMonth-$StartDay";
$StopDate = "$StopYear-$StopMonth-$StopDay";
$TimeZone = "$TZSign$TZHH:$TZmm";
// ----------------------------------------
// Define Daylight Saving/Summer Time text.
$xDSSTText = ($DSSTYN == 'Yes')? 'Daylight/Summer Time' : 'Standard Time';
// -----------------------------
// Set initial uniform width for
// tables alignment in pixels.
$TableWidth = '800';
// ------------------------------
// Initialize output Text Areas.
$OutputText1 = $OutputText2 = '';
// *********************************************************
// BEGIN MAIN COMPUTATIONS HERE IF NO ERRORS DETECTED ABOVE.
// *********************************************************
// ------------------------------------
// Get the raw RTS ephemeris CSV table.
$RawEphemText = R_T_S_Times ($BodyID,$StartDate,$StopDate,$TimeZone,
$DSSTYN,$LonDeg,$LatDeg,$AltMet);
$RawEphemText = HTMLEntities($RawEphemText);
/* -----------------------------------------------------------
Check for non-ephemeris data. If there is no $$SOE marker,
then no ephemeris table body was returned and it was some
other text.
*/
$OutputText1 = '';
// --------------------------
// Check for ephemeris body.
$ii = StrPos($RawEphemText, '$$SOE');
/* ----------------------------------------------------
If an ephemeris table was returned, then extract the
required text blocks from the raw ephemeris data.
NOTE: The @ symbol supresses notices when the moon is
not the selected object.
*/
if ($ii !== FALSE)
{
$TargetBodyNameText = Get_Target_Body_Name ($RawEphemText);
// ------------------------------
// Make the RTS table text block.
$RTSTableText = @Make_RTS_Table($RawEphemText);
// ----------------------------------------
// Check for spacecraft or special symbols.
if ($BodyID == '?!') {$TargetBodyNameText = $RTSTableText = '';}
if (StrPos($RawEphemText, 'spacecraft') !== FALSE) {$TargetBodyNameText = $BodyID;}
if (StrPos($RTSTableText, 'No ephemeris meets criteria.') !== FALSE) {$RTSTableText = 'No ephemeris data.';}
if (StrPos($RTSTableText, 'Horizons On-Line System News') !== FALSE) {$TargetBodyNameText = $RTSTableText = '';}
/* --------------------------------------------
Define table of lunar phases to be displayed
if the target body is the Moon (310 Luna).
Othersise, it will be ignored.
*/
$MoonPhaseTableText = '';
if ($BodyID == '301')
{
$MoonPhaseTableText =
"
========================================
Phase_Deg General_Phase_Description
=========== ===========================
0° New Moon
45 Waxing Crescent Moon
90 First Quarter Moon
135 Waxing Gibbous Moon
180 Full Moon
225 Waning Gibbous Moon
270 Last Quarter Moon
315 Waning Crescent Moon
360/0 New Moon";
}
/* ------------------------------------------------------
Combine all required segment text blocks into a single
text block.
*/
$OutputText2 = $RawEphemText;
$OutputText1 =
"$RTSTableText
$MoonPhaseTableText
$_BAR_
";
}
else
// -----------------------------------------------------------------
// Do this if something other than an ephemeris table is returned.
{
$TargetBodyNameText = "$BodyID\n";
$OutputText2 .= "NASA/JPL BODY ID:\n" . $TargetBodyNameText . "\n";
$OutputText2 .= $RawEphemText;
}
// *******************************************
// DROP THROUGH HERE AFTER COMPUTATIONS ABOVE
// TO PRINT OUT THE RESULTS OF THE OPERATIONS.
// *******************************************
$TextArea1Text =
"$_BAR_
RISE | TRANSIT | SET TIMES CALCULATOR
and
General NASA/JPL Data Query Portal
NASA/JPL BODY ID: $BodyID
$TargetBodyNameText
---------------
FOR OBSERVER AT
Location Name or Label = $LocName
GPS Longitude = $LonDeg° +Positive = East
GPS Latitude = $LatDeg°
Sea Level Altitude = $AltMet m
Time Zone = UT$TimeZone +Positive = East
Start Date = $StartDate
Stop Date = $StopDate
Daylight/Summer Time = $DSSTYN
$_BAR_
$OutputText1";
// ****************************
// Define TextArea2 text block.
$TextArea2Text = "$OutputText2";
// **************************************************************************
// Determine number of text columns and rows to use in the output text areas.
// These values vary randomly according to the text block width and length.
// The idea is to eliminate the need for scroll-bars within the text areas
// or worry as much about the variable dimensions of a text display area.
// --------------------------------------------
// Text Area 1 - Default = At least 80 columns.
$Text1Cols = 1 + Max(Array_Map('StrLen', PReg_Split("[\n]", trim($TextArea1Text))));
if ($Text1Cols < 80) {$Text1Cols = 80;}
$Text1Rows = 3 + Substr_Count($TextArea1Text, "\n");
// --------------------------------------------
// Text Area 2 - Default = At least 80 columns.
$Text2Cols = 1 + Max(Array_Map('StrLen', PReg_Split("[\n]", trim($TextArea2Text))));
if ($Text2Cols < 80) {$Text2Cols = 80;}
$Text2Rows = 3 + Substr_Count($TextArea2Text, "\n");
// ******************************************
// ******************************************
// GENERATE CLIENT WEB PAGE TO DISPLAY OUTPUT
print <<< _HTML
<!DOCTYPE HTML>
<HTML>
<head>
<title>$_BROWSER_TAB_TEXT_</title>
<meta name='viewport' content='width=device-width, initial-scale=0.8'>
<meta http-equiv='content-type' content='text/html; charset=UTF-8'>
<meta http-equiv='pragma' content='no-cache'>
<meta http-equiv='expires' content='-1'>
<meta name='description' content='xxxxxxxxxxxxxxx'>
<meta name='keywords' content='PHPScienceLabs.com'>
<meta name='author' content='Jay Tanner - https://www.PHPScienceLabs.com'>
<meta name='robots' content='index,follow'>
<meta name='googlebot' content='index,follow'>
<style>
BODY
{
background:black; color:white; font-family:Verdana; font-size:12pt;
line-height:125%;
}
TABLE
{font-family:Verdana;}
TD
{
color:black; background:white; line-height:150%; font-size:10pt;
padding:6px; text-align:center;
}
OL
{font-family:Verdana; font-size:12pt; line-height:150%; text-align:justify;}
UL
{font-family:Verdana; font-size:12pt; line-height:150%; text-align:justify;}
LI
{font-family:Verdana; line-height:150%;}
PRE
{
background:white; color:black; font-family:monospace; font-size:12.5pt;
font-weight:bold; text-align:left; line-height:125%; padding:6px;
border:2px solid black; border-radius:8px;
page-break-before:page;
}
DIV
{
background:white; color:black; font-family:Verdana; font-size:11pt;
font-weight:normal; line-height:125%; padding:6px;
}
TEXTAREA
{
background:white; color:black; font-family:monospace; font-size:11pt;
font-weight:bold; padding:4pt; white-space:pre; border-radius:8px;
line-height:125%;
}
INPUT[type='text']::-ms-clear {width:0; height:0;}
INPUT[type='text']
{
background:white; color:black; font-family:monospace; font-size:13pt;
font-weight:bold; text-align:center; box-shadow:1px 2px 2px #808080;
border:2px solid black; border-radius:4px; padding:4px;
}
INPUT[type='text']:focus
{
font-family:monospace; background:white; box-shadow:1px 2px 2px #808080;
font-size:13pt; border:2px solid blue; text-align:center; font-weight:bold;
border-radius:4px; padding:4px;
}
INPUT[type='submit']
{
background:black; color:gray; font-family:Verdana; font-size:10pt;
font-weight:bold; border-radius:4px; border:4px solid gray;
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 GreenYellow;
padding:3pt;
}
/* -------------------------------------------
Link states below MUST be defined in CSS in
the following sequence to work correctly:
:link, :visited, :hover, :active
*/
A:link
{
font-size:10pt; background:transparent; color:#8080FF; border-radius:4px;
font-family:Verdana; font-weight:bold; text-decoration:none;
line-height:175%; padding:3px; border:1px solid transparent;
}
A:visited
{
font-size:10pt; background:transparent; color:DodgerBlue; border-radius:4px;
}
A:hover
{
font-size:10pt; background:yellow; color:black; border:1px solid black;
box-shadow:1px 1px 3px #222222; border-radius:4px;
}
A:active
{
font-size:10pt; background:yellow; color:black; border-radius:4px;
}
HR {background:red; height:4px; border:0px;}
[title-text]:hover:after
{
opacity:1.0;
transition:all 0.1s ease 0.1s;
text-align:left;
visibility:visible;
}
[title-text]:after
{
content:attr(title-text);
opacity:1.0;
z-index:1;
position:absolute;
visibility:hidden;
right:-420%;
text-align:left;
background:yellow;
color:black;
font-family:monospace;
font-size:9pt;
font-weight:bold;
padding:1px 5px 2px 5px;
white-space:pre;
border:3px solid red;
box-shadow:2px 2px 8px #222222;
line-height:125%;
}
[title-text] {position: relative;}
::selection{background-color:yellow !important; color:black !important;}
::-moz-selection{background-color:yellow !important; color:black !important;}
</style>
</head>
<body>
<!-- Define container form --->
<form name="form1" method="post" action="">
<!-- Define main page title/header. --->
<table width="$TableWidth" align="top" border="0" cellspacing="1" cellpadding="3">
<tr><td colspan="99" style='color:white; background-color:#000066; border:2px solid white; border-radius:8px 8px 0px 0px;'>$_INTERFACE_TITLE_</td></tr>
</table>
<!-- Body ID input --->
<table width="$TableWidth" align="bottom" border="0" cellspacing="1" cellpadding="3">
<tr><td style="background:LightYellow; line-height:175%;" colspan='99'>
<b>NASA/JPL Body ID#, Record #, or Special Query or Command</b><br>
<input name="BodyID" type="text" value="$BodyID" size="65" maxlength="81"><br>
<span title-text="$BodyIDTitleText"> <b>INFO</b> </span>
</td></tr>
</table>
<!-- Optional Location Name or Label --->
<table width="$TableWidth" align="bottom" border="0" cellspacing="1" cellpadding="3">
<tr>
<td style='background:#E0FFE0;' title=' This can be any location name or label. \n'>Optional Location Name or Label<br><input name="LocName" type="text" value="$LocName" size="65" maxlength="64"></td>
</tr>
</table>
<!-- Longitude/Latitude/Altitude --->
<table width="$TableWidth" align="bottom" border="0" cellspacing="1" cellpadding="3">
<tr title=" IMPORTANT:\n Make sure that the given geographic coordinates are \n within the given time zone or computed times may \n not be accurate.">
<td width='33%' style='background:LightCyan; line-height:180%;'>GPS Longitude +Pos = E<br>
<input name="LonDeg" type="text" value="$LonDeg" size="15" maxlength="14" title=' GPS Longitude in Decimal Degrees\nor as\n Deg Min Sec Separated by Spaces \n'>
</td>
<td width='33%' style='background:LightCyan; line-height:180%;'>GPS Latitude +Pos = N<br>
<input name="LatDeg" type="text" value="$LatDeg" size="15" maxlength="14" title=' GPS Latitude in Decimal Degrees\nor as\n Deg Min Sec Separated by Spaces \n'>
</td>
<td style='background:LightCyan; line-height:180%;'>± Sea Level Altitude<br>
<input name="AltMet" type="text" value="$AltMet" size="9" maxlength="8" title=' Altitude in meters relative to sea level. '> meters
</td>
</tr>
</table>
<!-- Start Date/Time Zone --->
<table width="$TableWidth" align="bottom" border="0" cellspacing="1" cellpadding="3">
<tr>
<td width='50%' style="text-align:left; line-height:175%;" title=" This is the ephemeris Start Date, Time Zone \n and Daylight Saving | Summer Time Choice. \n">
<b>START:</b> Calendar Date<br>
<input name="StartYear" type="text" value="$StartYear" size="5" maxlength="4">
<input name="StartMonth" type="text" value="$StartMonth" size="4" maxlength="3">
<input name="StartDay" type="text" value="$StartDay" size="3" maxlength="2"> Time Zone
<input name="TZSign" type="text" value="$TZSign" size="1" maxlength="1">
<input name="TZHH" type="text" value="$TZHH" size="2" maxlength="2" title=''><b> :</b>
<input name="TZmm" type="text" value="$TZmm" size="2" maxlength="2" title=''>
Daylight/Summer Time ? <input name='DSSTYN' type='text' value="$DSSTYN" size='4' maxlength='3' title=' No = Use Standard Time (Default) \n\n Yes = Use Daylight/Summer Time '>
</td>
</tr>
</table>
<!-- Stop Date/Time --->
<table width="$TableWidth" align="bottom" border="0" cellspacing="1" cellpadding="3">
<tr title=" This is the ephemeris table Stop Date \n\n For an ephemeris table, the Stop Date\n must be LATER than the Start Date. \n">
<td width='50%' style="background:white; text-align:left; line-height:180%;">
<b>STOP:</b> Calendar Date<br>
<input name="StopYear" type="text" value="$StopYear" size="5" maxlength="4">
<input name="StopMonth" type="text" value="$StopMonth" size="4" maxlength="3">
<input name="StopDay" type="text" value="$StopDay" size="3" maxlength="2">
</td>
</tr></table>
<!-- Define [SUBMIT] button --->
<table width="$TableWidth" align="bottom" border="0" cellspacing="1" cellpadding="3">
<tr><td colspan="99" style="text-align:center; background-color:black;"><input type="submit" name="SubmitButton" value=" S U B M I T " OnClick="$_COMPUTING_"></td></tr>
</table>
<!-- Source Code View and/or Download Link. --->
<table width="$TableWidth">
<tr>
<td colspan="99" style="font-size:10pt; color:GreenYellow; background:black; text-align:center;">
<b><a href='View-Source-Code.php' target='_blank'>View/Copy PHP Source Code</a></b>
</td>
</tr>
</table>
<!-- Define TextArea1 --->
<table width="$TableWidth" align="bottom" border="0" cellspacing="1" cellpadding="3">
<tr>
<td colspan="99" style="text-align:center; color:yellow; background-color:black;"><br><b> Double-Click Within Text Area to Select ALL Text </b><br>
<textarea ID="TextArea1" name="TextArea1" style="color:black; background:white; padding:6px; border:2px solid white;" cols="$Text1Cols" rows="$Text1Rows" ReadOnly OnDblClick="this.select();" OnMouseUp="return true;">
$TextArea1Text
</textarea>
</td>
</tr>
</table>
<!-- Define TextArea2 --->
<table width="$TableWidth" align="bottom" border="0" cellspacing="1" cellpadding="3">
<tr>
<td colspan="99" style="text-align:center; color:yellow; background:black;"><br>
<b> Double-Click Within Text Area to Select ALL Text </b><br>
<textarea ID="TextArea2" name="TextArea2" style="color:black; background:white; padding:6px;" cols="$Text2Cols" rows="$Text2Rows" ReadOnly OnDblClick="this.select();" OnMouseUp="return true;">
$TextArea2Text
</textarea>
</td>
</tr>
</table>
<!-- Page footer --->
<table width="666" align="bottom" border="0" cellspacing="1" cellpadding="3">
<tr>
<td colspan="99" style="color:GreenYellow; background:black; text-align:center;">PHP Program $_AUTHOR_<br>
<span style="color:silver; background:black;">$_REVISION_DATE_</span>
</td>
</tr>
</table>
</form>
<!-- End of container form --->
<!-- Extra bottom scroll space --->
<br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br>
</body>
</HTML>
_HTML;
/*
###########################################################################
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.
DSSTYN = 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.
The test/demo ephemeris quantities returned are listed below, but more can
easily be added. The only extra feature added here applies to the Moon,
appending two more columns (8,9) to the table.
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)
8 S-O-T (Sun-Observer-Target angle) used to find the lunar phase.
9 /r (/L = Moon Leading the Sun or /T = Moon Trailing the Sun)
NO DEPENDENCIES
###########################################################################
*/
function R_T_S_Times ($BodyID,$StartDate,$StopDate,$TimeZone,$DSSTYN,
$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.
*/
$DSSTYN = substr(StrToUpper(trim($DSSTYN)),0,1);
$DSSTAdj = ($DSSTYN == 'N')? 0:1;
list($TZHH, $TZmm) = PReg_Split("[\:]", $TimeZone);
$TZSign = substr($TZHH,0,1);
$TZHours = (($TZSign == '-')? -1:1)*(abs($TZHH) + $TZmm/60) + $DSSTAdj;
$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'; $SOT = ',23';}
// -------------------------------------
// Construct URL for Horizons API query.
$From_Horizons_API =
"https://ssd.jpl.nasa.gov/api/horizons.api?format=text" .
"&COMMAND='$Command'" .
"&OBJ_DATA='YES'" .
"&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$SOT'" .
"&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.
*/
return $RTS;
} // End of R_T_S_Times(...)
/* #########
UTILITIES
#########
*/
/*
###########################################################################
This function returns decimal degrees equivalent to a DMS string argument
to the specified number of decimals.
The Degrees, Minutes and Seconds string values are separated by spaces.
Generic. No special error checking is done.
###########################################################################
*/
function DMS_to_Degrees ($DMSString, $Decimals=14)
{
$DDmmss = trim($DMSString);
$decimals = trim($Decimals);
// -----------------------------------
// Account for any numerical +/- sign.
$NumSign = substr($DDmmss,0,1);
if ($NumSign == '-')
{$DDmmss = substr($DDmmss,1,StrLen($DDmmss));}
else
{
if ($NumSign == '+')
{$DDmmss = substr($DDmmss,1,StrLen($DDmmss));}
$NumSign = '+';
}
// -----------------------
// Remove all white space.
$DDmmss = PReg_Replace("/\s+/", " ", $DDmmss);
// ----------------------------------------
// Count the DMS time elements from 1 to 3.
$n = 1 + Substr_Count($DDmmss, ' ');
$dd = $mm = $ss = 0;
// --------------------------------------
// Collect all given time element values.
// They can be integer or decimal values.
for ($i=0; $i < 1; $i++)
{
if ($n == 1){list($dd) = PReg_Split("[ ]", $DDmmss);}
if ($n == 2){list($dd,$mm) = PReg_Split("[ ]", $DDmmss);}
if ($n == 3){list($dd,$mm,$ss) = PReg_Split("[ ]", $DDmmss);}
}
// ----------------------------------
// Compute DMS equivalent in degrees.
return $NumSign.(round((3600*$dd + 60*$mm + $ss)/3600,$decimals));
} // End of DMS_to_Degrees(...)
/*
###########################################################################
This function extracts and returns ONLY target body text line from the
raw ephemeris.
The target body text always begins with the characters:
'Target body name:'
Generic. No special error checking is done.
###########################################################################
*/
function Get_Target_Body_Name ($RawEphemText)
{
$T = trim($RawEphemText);
$i = StrPos($T, '$$SOE'); if ($i === FALSE) {return $i;}
$i = StrPos($T, 'Target body name:');
$j = StrPos($T, '}');
$w = PReg_Replace("/\s+/", " ", trim(substr($T, $i, $j-$i+1)));
$w = trim(Str_Replace('Target body name:','', $w));
$i = StrPos($w, '}') + 1;
return substr($w, 0, $i);
}
/*
###########################################################################
This function constructs a complete Rise/Transit/Set Table from a raw RTS
ephemeris as returned by the API.
###########################################################################
*/
function Make_RTS_Table ($RawRTSCSVText)
{
GLOBAL $TargetBodyNameText, $xDSSTText, $MoonPhaseTableText;
$T = trim($RawRTSCSVText);
// -----------------------------------------
// Extract only the CSV ephemeris text block
// from the given raw returned RTS text.
$i = StrPos($T, '$$SOE');
if ($i === FALSE) {return '';}
$j = StrPos($T, '$$EOE');
$T = trim(substr($T, $i+5, $j-$i-5));
$wArray = PReg_Split("[\n]", $T);
$wCount = count($wArray);
$wTable = '';
for ($i=0; $i < $wCount; $i++)
{
$CurrLine = trim($wArray[$i]);
list
(
$DateTimeStr,
$P,
$RorTorS,
$AzimDeg,
$ElevDeg,
$VisMag,
$Sbrt,
$SOT,
$LorT
) = PReg_Split("[,]", $CurrLine);
$DateTimeStr = Str_Replace(' ', ' ', $DateTimeStr);
$ElevDeg = trim($ElevDeg);
$LorT = trim($LorT);
$VisMag = trim($VisMag);
$SOT = trim($SOT);
$AzimDeg = SPrintF("% 7.3f", trim($AzimDeg));
$ElevDeg = SPrintF("% +7.3f", trim($ElevDeg));
if ($RorTorS == 'r') {$RorTorS = 'Rise '; $ElevDeg = ' - ';}
if ($RorTorS == 't') {$RorTorS = 'Transit';}
if ($RorTorS == 's') {$RorTorS = 'Set '; $ElevDeg = ' - ';}
if ($LorT == '/L' or $LorT == '/T')
{
$PhaseAng = ($LorT == '/L')? 360-trim($SOT) : trim($SOT);
$xPh1 = 'Phase_Deg'; $xPh2 = '=========';
} else {$PhaseAng = ''; $xPh1 = $xPh2 = '';}
// -----------------------------------------
// Determine compass directions of rise/set.
$a = FloatVal($AzimDeg);
$a -= 360*floor($a/360);
$k = ($a < 11.25 or $a > 348.75)? 0 : floor(0.5 + $a/22.5);
$u = trim(substr('N NNENE ENEE ESESE SSES SSWSW WSWW WNWNW NNWN', 3*$k, 3));
$u = substr("$u ",0,3);
if (substr($u, -2) == ' ') {$u = ' '.trim($u).' ';}
$dir = $u;
// -----------------
// Visual magnitude.
$VisMag = ($VisMag <> 'n.a.')? SPrintF("% +7.3f", $VisMag) : " $VisMag";
// --------------------------------
// Construct ephemeris output line.
$wTable .= "$DateTimeStr $RorTorS $AzimDeg $dir $ElevDeg $VisMag $PhaseAng\n";
}
// @@@@@@@@@@@@@@@@@@@@@@@
// -------------------------------
// Store CSV text block in working
// array and count the elements.
unset($wArray);
$wArray = PReg_Split("[\n]", $wTable);
$wCount = count($wArray);
$wTable = '';
// --------------------------------------------------------------
// Construct table with different dates separated by blank lines.
$FirstLine = trim($wArray[0]);
if (Is_Numeric(substr($FirstLine,0,1))) {$FirstLine = " $FirstLine";}
for($i=1; $i < $wCount; $i++)
{
$PrevLine = trim($wArray[$i-1]);
$CurrLine = trim($wArray[$i-0]);
if (Is_Numeric(substr($PrevLine,0,1))) {$PrevLine = " $PrevLine";}
if (Is_Numeric(substr($CurrLine,0,1))) {$CurrLine = " $CurrLine";}
$PrevDay = substr($PrevLine,10,2);
$CurrDay = substr($CurrLine,10,2);
/* --------------------------------------------------
Compare previous and current day numbers to see if
they are equal. If not, then insert a blank line
before printing the subsequent ephemeris line.
*/
if ($PrevDay == $CurrDay)
{$wTable .= "$CurrLine\n";}
else
{$wTable .= "\n$CurrLine\n";}
}
$wTable = RTrim("$FirstLine\n$wTable");
return
" RISE / TRANSIT / SET TIMES TABLE
NASA/JPL BODY ID:
$TargetBodyNameText
$xDSSTText
=============================================================================
Calen_Date Time Event Azim_Deg Dir Elev_Deg Vis_Mag $xPh1
============ ===== ======= ======== === ======== ======= $xPh2
$wTable";
} // End of Make_RTS_Table (...)
// END OF PROGRAM
?>