<?php

/*
   ###########################################################################
   NASA/JPL HORIZONS LUNAR EPHEMERIS WITH 30-DAY COOKIE.  FOR TOPOCENTRIC
   PERSPECTIVE TAKING INTO ACCOUNT GEOGRAPHIC LOCATION AND TIME ZONE.

   The computations are performed by the NASA/JPL Horizons API server
   located at the Jet Propulsion Laboratory (JPL) campus in Pasadena,
   California, USA.  (4800 Oak Grove Drive, Pasadena, CA 91109, USA).

   AUTHOR   : Jay Tanner - 2026
   LANGUAGE : PHP v8.2.12
   LICENSE  : Public Domain

   NOTES:
   [1] Quantity #2 is used for apparent topocentric RA and Declination and
       takes your GPS location coordinates and time zone into account.

   [2] The default location coordinates are for my town - Geneva, NY, USA.
       These default values can be easily changed in the source code and
       customized for personal home-based usage.

   [3] The program can run on a local PC or a hosted PHP-enabled web site
       with an Internet connection via any standard web browser.

       This is just a basic demo ephemeris to demonstrate how it can be done.
   ###########################################################################
*/

   
OB_Start(); // Initialize output buffer.

// ---------------------------------------------------------------
// Define the program cookie name and set it to expire in 30 days.

   
$CookieName 'Basic-Horizons-Lunar-Ephemeris';
   
$SetToExpireIn30Days time() + 30*86400;

// ------------------------------------------------------------------
// Define JavaScript message to display in (TextArea1) while working.

   
$_COMPUTING_ "TextArea1.innerHTML='W.O.R.K.I.N.G --- This may take a few seconds.';";

// ---------------------------------
// Define PHP program and HTML info.

   
$_AUTHOR_           'Jay Tanner of Geneva, NY, USA';
   
$_PROGRAM_VERSION_  'v1.10 - '$at "&#97;&#116;&#32;&#84;&#105;&#109;&#101;&#32;"$LTC "&#85;&#84;&#67;";
   
$_SCRIPT_FILE_PATH_ Filter_Input(INPUT_SERVER'SCRIPT_FILENAME');
   
$_REVISION_DATE_    $_PROGRAM_VERSION_ .'Revised: 'GMDate("Y-F-d-l $at H:i:s   ($LTC"FileMTime($_SCRIPT_FILE_PATH_)).")";
   
$_BROWSER_TAB_TEXT_ "Basic Horizons Lunar Ephemeris";
   
$_INTERFACE_TITLE_  "<span style='font-size:15pt;'>Basic NASA/JPL Horizons Lunar Ephemeris</span><br><br><span style='font-size:10pt;'>Built Around The NASA/JPL Horizons API</span><br><span style='font-size:10pt;'>PHP Program by $_AUTHOR_</span>";


/* ----------------------------------------
   Define main TextArea text and background
   colors and HTML table row span. If an
   error is reported, then these colors
   will change internally to red/white.
*/
   
$TxColor 'black';
   
$BgColor 'white';

/* ---------------------------
   Default GPS coordinates are
   for Geneva, NY, USA.
*/
   
$DefaultLocLabel 'Geneva, NY, USA';
   
$DefaultTimeZone '-05:00';     // +Positive = East
   
$DefaultLonDeg   '-76.996491'// +Positive = East
   
$DefaultLatDeg   '+42.868473'// +Positive = North
   
$DefaultAltMet   '+0.0';

// Define the 3-letter month name abbreviations.
   
define('MONTH_NAMES''JanFebMarAprMayJunJulAugSepOctNovDec');

// --------------------------
// Get [SUBMIT] button state.
   
$SubmitButton Filter_Input(INPUT_POST'SubmitButton');

// Do this only if the [SUBMIT] button was NOT clicked.
   
if (!IsSet($SubmitButton))
  {

/* ----------------------------------------------------------------------
   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.
*/
   
$CookieState Filter_Input(INPUT_COOKIE$CookieName);

   if (IsSet(
$CookieState))
      {
       
$CookieDataString Filter_Input(INPUT_COOKIE$CookieName);
       list
      (
       
$BodyID,
       
$TimeScale,
       
$StartBCAD,
       
$StartYear,
       
$StartMonth,
       
$StartDay,
       
$StartTime,
       
$TimeZone,
       
$StopBCAD,
       
$StopYear,
       
$StopMonth,
       
$StopDay,
       
$StopTime,
       
$DaySumYN,
       
$StepSize,
       
$LocLabel,
       
$LonDeg,
       
$LatDeg,
       
$AltMet,
       
$RefractYN,
       
$ObjDataYN,
       
$HMSorDEG,
       
$JDateYN,
       
$SuppRangeRate,
       
$AUorKMorMI,
       
$Quantities
      
) = 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';
   
$TimeScale     'UT';
   
$StartBCAD     'AD';
   
$StartYear     date('Y');
   
$StartMonth    date('M');
   
$StartDay      '01';
   
$StartTime     '00:00:00';
   
$TimeZone      $DefaultTimeZone;
   
$DaySumYN      'No';
   
$StopBCAD      'AD';
   
$StopYear      date('Y');
   
$StopMonth     date('M');
   
$StopDay       date('t');
   
$StopTime      '00:00:01';
   
$StepSize      '1 day';
   
$LocLabel      $DefaultLocLabel;
   
$LonDeg        $DefaultLonDeg;
   
$LatDeg        $DefaultLatDeg;
   
$AltMet        $DefaultAltMet;
   
$RefractYN     'No';  // Yes|No
   
$ObjDataYN     'Yes'// Yes|No
   
$HMSorDEG      'HMS'// HMS|DEG
   
$JDateYN       'No';  // Yes|No
   
$SuppRangeRate 'Yes'// Yes|No
   
$AUorKMorMI    'Mi';  // AU|Km|Mi
   
$Quantities    '2,20,4,9';

// -------------------------------------------
// Store current interface settings in cookie.

   
$CookieDataString "$BodyID|$TimeScale|$StartBCAD|$StartYear|$StartMonth|$StartDay|$StartTime|$TimeZone|$StopBCAD|$StopYear|$StopMonth|$StopDay|$StopTime|$DaySumYN|$StepSize|$LocLabel|$LonDeg|$LatDeg|$AltMet|$RefractYN|$ObjDataYN|$HMSorDEG|$JDateYN|$SuppRangeRate|$AUorKMorMI|$Quantities";
   
SetCookie ($CookieName$CookieDataString$SetToExpireIn30Days);
  } 
// End of  else {...}

  
// End of  if (!isset(_POST['SubmitButton']))


/* ------------------------------------------
   Read values of all interface arguments and
   set any empty arguments to default values.
*/
   
$SubmitButton Filter_Input(INPUT_POST'SubmitButton');

   if (IsSet(
$SubmitButton))
{
   
$BodyID     trim(Filter_Input(INPUT_POST'BodyID'));
                 if (
$BodyID == '') {$BodyID '301';}

   
$TimeScale StrToUpper(trim(Filter_Input(INPUT_POST'TimeScale')));
                if (
$TimeScale == '') {$TimeScale 'UT';}
   
$TimeScale = (substr($TimeScale0,1) == 'T')? 'TT' 'UT';


// ----------------------------------------------------
// Get longitude input as decimal degrees or DMS string
// which will be converted into decimal degrees.

   
$LonDeg trim(Filter_Input(INPUT_POST'LonDeg'));
             if (
$LonDeg == '') {$LonDeg $DefaultLonDeg;}
   
$LonDeg DMS_to_Deg($LonDeg9);


// ---------------------------------------------------
// Get latitude input as decimal degrees or DMS string
// which will be converted into decimal degrees.

   
$LatDeg trim(Filter_Input(INPUT_POST'LatDeg'));
             if (
$LatDeg == '') {$LatDeg $DefaultLatDeg;}
   
$LatDeg DMS_to_Deg($LatDeg9);


   
$AltMet trim(Filter_Input(INPUT_POST'AltMet'));
             if (
$AltMet == '') {$AltMet '+0';}
   
$AltMet SPrintF("%+d"$AltMet);


   
$StartBCAD StrToUpper(trim(Filter_Input(INPUT_POST'StartBCAD')));
                if (
$StartBCAD == '') {$StartBCAD 'AD';}
   
$StartBCAD = (substr($StartBCAD,0,1) == 'B')? 'BC' 'AD';

   
$StartYear  trim(Filter_Input(INPUT_POST'StartYear'));
                 if (
$StartYear == '') {$StartYear date('Y');}
   
$StartYear  SPrintF("%04d"$StartYear);

/* -------------------------------------------
   Get START month as a number (1 to 12) or as
   a 3-letter abbreviation ('Jan' to 'Dec').
   NOT case-sensitive.
*/
   
$StartMonth UCFirst(substr(StrToLower(trim(Filter_Input(INPUT_POST'StartMonth'))),0,3));
                 if (
$StartMonth == '') {$StartMonth date('M');}

   if (
Is_Numeric($StartMonth) and $StartMonth == 0) {$StartMonth 'Jan';}

   for (
$ii=0;  $ii 1;  $ii++)
  {
   if (
Is_Numeric($StartMonth))
      {
       
$m IntVal($StartMonth);
            if (
$m or $m 12) {$StartMonth 0; break;}
       
$StartMonth substr(MONTH_NAMES3*($m-1), 3);
      }
        
$jj StrPos(MONTH_NAMES$StartMonth);
              if (
$jj === FALSE) {$StartMonth 0; break;}
        
$StartMonth substr(MONTH_NAMES$jj3);
  }

   
$StartDay  trim(Filter_Input(INPUT_POST'StartDay'));
                if (
$StartDay == '') {$StartDay '01';}
   
$StartDay  SPrintF("%02d"$StartDay);

   
$StartTime trim(Filter_Input(INPUT_POST'StartTime'));
                if (
$StartTime == '') {$StartTime date('H:i:s');}
   
$StartTime = (!Is_Numeric(substr($StartTime,0,1)))? '00:00:00'$StartTime;
                 
$w HMS_to_Hours($StartTime);
   
$StartTime Hours_to_HMS($w,0);


   
$TimeZone  trim(Filter_Input(INPUT_POST'TimeZone'));
                if (
$TimeZone == '') {$TimeZone $DefaultTimeZone;}

   
$TZHours   = @HMS_to_Hours($TimeZone); // suppress a harmless warning.

   
$TimeZone  Hours_to_HMS($TZHours);
   
$TZSign    = ($TZHours >= 0)? '+':'';
   
$TimeZone  substr($TZSign.$TimeZone,0,6);


   
$DaySumYN  trim(Filter_Input(INPUT_POST'DaySumYN'));
   
$DaySumYN  = (substr(StrToUpper($DaySumYN),0,1) == 'Y')? 'Yes':'No';

   
$StopBCAD  StrToUpper(trim(Filter_Input(INPUT_POST'StopBCAD')));
                if (
$StopBCAD == '') {$StopBCAD 'AD';}
   
$StopBCAD  = (substr($StopBCAD,0,1) == 'B')? 'BC' 'AD';

   
$StopYear  trim(Filter_Input(INPUT_POST'StopYear'));
                if (
$StopYear == '') {$StopYear date('Y');}
   
$StopYear  SPrintF("%04d"$StopYear);

/* ------------------------------------------
   Get STOP month as a number (1 to 12) or as
   a 3-letter abbreviation ('Jan' to 'Dec').
*/
   
$StopMonth UCFirst(substr(StrToLower(trim(Filter_Input(INPUT_POST'StopMonth'))),0,3));
                if (
$StopMonth == '') {$StopMonth date('M');}
   if (
Is_Numeric($StopMonth) and $StopMonth == 0) {$StopMonth 'Dec';}

   for (
$ii=0;  $ii 1;  $ii++)
  {
   if (
Is_Numeric($StopMonth))
      {
       
$m IntVal($StopMonth);
            if (
$m or $m 12) {$StopMonth 0; break;}
       
$StopMonth substr(MONTH_NAMES3*($m-1), 3);
      }
        
$jj StrPos(MONTH_NAMES$StopMonth);
              if (
$jj === FALSE) {$StopMonth 0; break;}
        
$StopMonth substr(MONTH_NAMES$jj3);
  }

   
$StopDay   trim(Filter_Input(INPUT_POST'StopDay'));
                if (
$StopDay == '') {$StopDay IntVal(date('t'));}
   
$StopDay   SPrintF("%02d"$StopDay);


   
$StopTime  trim(Filter_Input(INPUT_POST'StopTime'));
                if (
$StopTime == '') {$StopTime date('H:i:s');}
   
$StopTime  = (!Is_Numeric(substr($StopTime,0,1)))? '00:00:00'$StopTime;
                
$w HMS_to_Hours($StopTime);
   
$StopTime  Hours_to_HMS(HMS_to_Hours($w), 0);


   
$StepSize  trim(Filter_Input(INPUT_POST'StepSize'));
                if (
$StepSize == '') {$StepSize '1 hour';}
   
$StepSize .= (Is_Numeric($StepSize))? ' day' '';

   
$LocLabel  trim(Filter_Input(INPUT_POST'LocLabel'));
                if (
$LocLabel == '') {$LocLabel '---';}

// ---------------------------------------------------
// Get latitude input as decimal degrees or DMS string
// which will be converted into decimal degrees.

   
$LatDeg trim(Filter_Input(INPUT_POST'LatDeg'));
             if (
$LatDeg == '') {$LatDeg $DefaultLatDeg;}
   
$LatDeg DMS_to_Deg($LatDeg9);

   
$AltMet trim(Filter_Input(INPUT_POST'AltMet'));
             if (
$AltMet == '') {$AltMet '+0';}
   
$AltMet SPrintF("%+1.1f"$AltMet);

   
$RefractYN StrToUpper(trim(Filter_Input(INPUT_POST'RefractYN')));
                if (
$RefractYN == '') {$RefractYN 'No';}
   
$RefractYN = (substr($RefractYN,0,1) == 'Y')? 'Yes' 'No';

   
$ObjDataYN StrToUpper(trim(Filter_Input(INPUT_POST'ObjDataYN')));
                if (
$ObjDataYN == '') {$ObjDataYN 'No';}
   
$ObjDataYN = (substr($ObjDataYN,0,1) == 'Y')? 'Yes' 'No';

   
$HMSorDEG StrToUpper(trim(Filter_Input(INPUT_POST'HMSorDEG')));
                if (
$HMSorDEG == '') {$HMSorDEG 'HMS';}
   
$HMSorDEG = (substr($HMSorDEG,0,1) == 'D')? 'DEG' 'HMS';

   
$JDateYN StrToUpper(trim(Filter_Input(INPUT_POST'JDateYN')));
              if (
$JDateYN == '') {$JDateYN 'No';}
   
$JDateYN = (substr($JDateYN,0,1) == 'Y')? 'Yes' 'No';

   
$SuppRangeRate StrToUpper(trim(Filter_Input(INPUT_POST'SuppRangeRate')));
                    if (
$SuppRangeRate == '') {$SuppRangeRate 'Yes';}
   
$SuppRangeRate = (substr($SuppRangeRate,0,1) == 'Y')? 'Yes' 'No';

   
$AUorKMorMI StrToUpper(trim(Filter_Input(INPUT_POST'AUorKM')));
             if (
$AUorKMorMI == '') {$AUorKMorMI 'Mi';}
   
$AUorKMorMI substr($AUorKMorMI,0,1);

   if (
$AUorKMorMI == 'A') {$AUorKMorMI 'AU';}
   if (
$AUorKMorMI == 'K') {$AUorKMorMI 'Km';}
   if (
$AUorKMorMI == 'M') {$AUorKMorMI 'Mi';}

   if (
$AUorKMorMI <> 'AU' and $AUorKMorMI <> 'Km' and $AUorKMorMI <> 'Mi') {$AUorKMorMI 'AU';}

   
$Quantities StrToUpper(trim(Filter_Input(INPUT_POST'Quantities')));
                 if (
$Quantities == '') {$Quantities '2,20,4,9';}

// --------------------------------------
// Store interface arguments in a cookie.

   
$CookieDataString "$BodyID|$TimeScale|$StartBCAD|$StartYear|$StartMonth|$StartDay|$StartTime|$TimeZone|$StopBCAD|$StopYear|$StopMonth|$StopDay|$StopTime|$DaySumYN|$StepSize|$LocLabel|$LonDeg|$LatDeg|$AltMet|$RefractYN|$ObjDataYN|$HMSorDEG|$JDateYN|$SuppRangeRate|$AUorKMorMI|$Quantities";
   
SetCookie ($CookieName$CookieDataString$SetToExpireIn30Days);
}


/* -----------------------------------------------------------
   Put code here to optionally  check individual arguments for
   validity in reverse input order.  If errors found, then set
   error flag and message values accordingly.

   NOTE: Horizons will catch some errors, such as invalid date
   errors.
*/
   
$ErrorReported FALSE;
   
$ErrMssg '';



// -----------------------------
// Set initial uniform width for
// tables alignment in pixels.

   
$TableWidth '780';


// -------------------------------------------
// If error was reported (TRUE), then display
// the error message on a red background.

   
if ($ErrorReported)
  {
   
$TxColor 'white';
   
$BgColor '#CC0000';
   
$TextArea2Text '';

   
$TextArea1Text =
"=== ERROR ===

$ErrMssg";
  }

else


  {
// *********************************************************
// BEGIN MAIN COMPUTATIONS HERE IF NO ERRORS DETECTED ABOVE.
// *********************************************************



// ------------------------------------------------------
// Construct full date and time strings for Horizons API.

   
$StartDateTime "$StartBCAD $StartYear-$StartMonth-$StartDay  $StartTime $TimeScale";
   
$StopDateTime  "$StopBCAD $StopYear-$StopMonth-$StopDay  $StopTime";

// Compute the raw ephemeris.
   
$RawEphemeris Topocentric_Ephem ($BodyID,$TimeScale,$TimeZone,$StartDateTime,
                                      
$StopDateTime,$StepSize,$LonDeg,$LatDeg,$AltMet,
                                      
$DaySumYN,$RefractYN,$HMSorDEG,$ObjDataYN,
                                      
$SuppRangeRate,$JDateYN,$AUorKMorMI,$Quantities);

// Determine which of the six output formats modes to use.
   
if ($HMSorDEG == 'HMS' and $AUorKMorMI == 'AU') {$EphemFormat 1;} // = Default = OK
   
if ($HMSorDEG == 'DEG' and $AUorKMorMI == 'AU') {$EphemFormat 2;} // = OK
   
if ($HMSorDEG == 'HMS' and $AUorKMorMI == 'Km') {$EphemFormat 3;} // = OK
   
if ($HMSorDEG == 'DEG' and $AUorKMorMI == 'Km') {$EphemFormat 4;} // = OK
   
if ($HMSorDEG == 'HMS' and $AUorKMorMI == 'Mi') {$EphemFormat 5;} // = OK
   
if ($HMSorDEG == 'DEG' and $AUorKMorMI == 'Mi') {$EphemFormat 6;} // = OK

// Parse and compress the raw ephemeris.
   
$CustomEphemeris Parse_Raw_Ephemeris ($RawEphemeris$EphemFormat);

// *******************************************
// DROP THROUGH HERE AFTER COMPUTATIONS ABOVE
// TO PRINT OUT THE RESULTS OF THE OPERATIONS.
// *******************************************

   
$TextArea1Text =
"#############################################################################
BASIC NASA/JPL HORIZONS TOPOCENTRIC LUNAR EPHEMERIS

EPHEMERIS SPAN:
AD 1800-JAN-02 23:59:41.3795 UT  to  AD 2199-DEC-28 23:58:50.8163 UT

---------------------------------------------------
NASA/JPL Database ID     =  Moon (301)

Base Time Scale          =  
$TimeScale
Local Time Zone Offset   =  UT
$TimeZone (Ignored if using TT scale.)
Daylight / Summer Time   =  
$DaySumYN

Optional Location Label  =  
$LocLabel
GPS Longitude            =  
$LonDeg &deg;\t(+Pos = East)
GPS Latitude             =  
$LatDeg &deg;\t(+Pos = North)

START Era / Date / Time  =  
$StartBCAD $StartYear-$StartMonth-$StartDay  $StartTime
STOP  Era / Date / Time  =  
$StopBCAD $StopYear-$StopMonth-$StopDay  $StopTime
Ephemeris Step Size      =  
$StepSize

*******************************************************************
    NASA/JPL HORIZONS LUNAR EPHEMERIS DATA OUTPUT FOLLOWS BELOW
*******************************************************************

$CustomEphemeris





***********************************************************************
BELOW IS THE RAW HORIZONS EPHEMERIS FROM WHICH THE ABOVE WAS EXTRACTED.
***********************************************************************

$RawEphemeris
"
;
  }


// ****************************
// Define TextArea2 text block.

   
$TextArea2Text =
"
NOTE:
This ephemeris replaces the  older DE405 ephemeris model  and uses the newer
official NASA/JPL Horizons API to perform the ephemeris computations for im-
proved accuracy.

"
;



/* --------------------------------------------------------------------------
   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.
*/

// --------------------------------------------
// TextArea1 columns width and length.

   
$Text1Cols Max(Array_Map('StrLen'PReg_Split("[\n]"trim($TextArea1Text))));
                if (
$Text1Cols 80) {$Text1Cols 80;}  // Default
   
$Text1Rows Substr_Count($TextArea1Text"\n");

// --------------------------------------------
// TextArea2 columns width and length.

   
$Text2Cols Max(Array_Map('StrLen'PReg_Split("[\n]"trim($TextArea2Text))));
                if (
$Text2Cols 80) {$Text2Cols 80;} // Default
   
$Text2Rows 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; chrset=UTF-8">
<meta http-equiv="pragma"       content="no-cache">
<meta http-equiv="expires"      content="-1">
<meta name="description"        content="JPL Horizons Lunar Ephemeris">
<meta name="keywords"           content="NeoProgrammics  / PHP Science Labs">
<meta name="author"             content="Jay Tanner - https://www.NeoProgrammics.com">
<meta name="robots"             content="index,follow">
<meta name="googlebot"          content="index,follow">

<style>

 BODY {color:white; background:black; font-family:Verdana; font-size:12pt; line-height:125%;}

 TABLE
{font-size:13pt; border: 1px solid black;}


 TD
{
 color:black; background:white; line-height:150%; font-size:10pt;
 padding:6px; text-align:center;
}


 UL
{font-family:Verdana; font-size:12pt; line-height:150%; text-align:justify;}


 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:12pt;
 font-weight:bold; padding:4pt; white-space:pre; border-radius:8px;
 line-height:125%;
}


 INPUT[type='text']::-ms-clear {width:0; height:0; text-align:center;}

 INPUT[type='text']
{
 font-family:monospace; color:black; background:white; font-size:11pt;
 font-weight:bold; text-align:center; box-shadow:2px 2px 3px #666666;
 border:2px solid black; border-radius:4px;
}
 INPUT[type='text']:focus
{
 font-family:monospace; background:white; box-shadow:2px 2px 3px #666666;
 font-size:11pt; 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;
}





// Link states MUST be set in the following order:
// :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:DarkCyan; 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 1.0s ease 1.0s;
 text-align:left;
 visibility:visible;
}

[title-text]:after
{
 opacity:1.0;
 content:attr(title-text);
 text-align:left;
 left:50%;
 background-color:yellow;
 color:black;
 font-size:10pt;
 position:absolute;
 padding:1px 5px 2px 5px;
 white-space:pre;
 border:1px solid red;
 z-index:1;
 visibility:hidden;
}

[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="center" 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>

<!-- Define input (BodyID) text box  --->
<table width="
$TableWidth" align="center" border="0" cellspacing="1" cellpadding="3">

<!--
<tr>
<td colspan="999" style='line-height:175%; background:LightCyan;' width='25%' title=''>
Enter&nbsp;NASA/JPL&nbsp;Database&nbsp;Body&nbsp;ID#,&nbsp;Designation&nbsp;or&nbsp;Special&nbsp;Query<br>
<input name="BodyID"  type="text" value="
$BodyID" size="65" maxlength="64"></td>
</tr>

<tr>
<td style='line-height:175%; background:LightYellow;' width='25%'
title=''>Requested&nbsp;Quantities&nbsp;List<br>
<input name="Quantities"  type="text" value="
$Quantities" size="65" maxlength="64"></td>
</tr>
--->

<tr>
<td style='line-height:175%; background:#CCFFCC;' width='25%'
title=''>Optional&nbsp;Location&nbsp;Label<br>
<input name="LocLabel"  type="text" value="
$LocLabel" size="65" maxlength="64"></td>
</tr>
</table>


<!-- LONGITUDE/LATITUDE/ALTITUDE SETTINGS TABLE --->
<table width="
$TableWidth" align="center" border="0" cellspacing="1" cellpadding="3">
<tr title=" IMPORTANT:\n Make sure that the given Geographic Coordinates are&nbsp; within the \n given Time Zone or the computed times may not be accurate. \n\n">

<!-- GPS Longitude in Degrees or DMS --->
<td colspan="1" style='background:#CCFFCC; line-height:180%;'>&plusmn;&nbsp;GPS Longitude &#0176;<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>

<!-- GPS Latitude in Degrees or DMS --->
<td colspan="1" style='background:#CCFFCC; line-height:180%;'>&plusmn;&nbsp;GPS Latitude &#0176;<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>

<!-- Sea level altitude --->
<td colspan="2" style='background:#CCFFCC; line-height:180%;'>&plusmn; Sea Level Altitude in Meters<br>
<input  name="AltMet"  type="text" value="
$AltMet"  size="13" maxlength="12" title=' Altitude in meters relative to sea level. '></td>
</tr>

<tr>
<td width='25%' style='line-height:175%; background:LightYellow;' title=' UT = Universal Time (Default)\n TT = Terrestrial (Dynamical) Time '>Base&nbsp;Time&nbsp;Scale<br><input name="TimeScale"  type="text" value="
$TimeScale" size="3" maxlength="2"></td>

<td colspan='2' title=' START Date/Time must be EARLIER than STOP Date/Time '><b>START</b>&nbsp;Date&nbsp;and&nbsp;Time<br><input name="StartBCAD"  type="hidden" value="
$StartBCAD" size="3" maxlength="2">
<input name="StartYear"  type="text" value="
$StartYear" size="6" maxlength="5"><input name="StartMonth"  type="text" value="$StartMonth" size="4" maxlength="3"><input name="StartDay"  type="text" value="$StartDay" size="3" maxlength="2">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input name="StartTime"  type="text" value="
$StartTime" size="9" maxlength="8"></td>

<td width='25%' style='line-height:175%; background:LightYellow;' title=' Local Time Zone Offset From UT. \n &minus;Negative = W&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  &plus;Positive = E \n\n IMPORTANT:\n Make sure that the given Geographic Coordinates are&nbsp; within the \n given Time Zone or the computed times may not be accurate. \n\n'>Time Zone<br><input name="TimeZone"  type="text" value="
$TimeZone" size="7" maxlength="6"></td>
</tr>

<tr>
<td width='33%' style='line-height:175%; background:LightYellow;' title=' No = Standard Time (Default) \n Yes = Daylight / Summer Time '>Daylight&nbsp;/&nbsp;Summer&nbsp;Time<br>
<input name="DaySumYN"  type="text" value="
$DaySumYN" size="4" maxlength="3"></td>

<td colspan="2" title=' STOP Date/Time must be LATER than START Date/Time '><b>STOP</b>&nbsp;Date&nbsp;and&nbsp;Time<br>
<input name="StopBCAD"  type="hidden" value="
$StopBCAD"  size="3" maxlength="2">
<input name="StopYear"  type="text" value="
$StopYear"  size="6" maxlength="5"><input name="StopMonth" type="text" value="$StopMonth" size="4" maxlength="3"><input name="StopDay"   type="text" value="$StopDay"   size="3" maxlength="2">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<input name="StopTime"  type="text" value="
$StopTime"  size="9" maxlength="8">
</td>

<td width='33%' style='line-height:175%; background:LightYellow;' title=' Minutes | Days | Hours | Months | Years '>Step&nbsp;Size<br><input name="StepSize"  type="text" value="
$StepSize" size="17" maxlength="16"></td></tr>


<tr><td style='line-height:175%; background:LightYellow;' width='25%' title=' HMS = Hours, Minutes and Seconds = Default \n DEG = Decimal Hours / Degrees '>Coords Output Mode<br><input name="HMSorDEG" type="text" value="
$HMSorDEG" size="4" maxlength="3"></td><td style='line-height:175%; background:LightYellow;' width='25%' title=' Output Distance Units:\n\n AU = Astronomical Units \n Km = Kilometers \n Mi = Statute Miles '>Range (Distance) Units<br><input name="AUorKM"  type="text" value="$AUorKMorMI" size="3" maxlength="2"><br><input name="JDateYN" type="hidden" value="$JDateYN" size="4" maxlength="3"></td>


<td style='line-height:175%; background:LightYellow;' width='25%' title=' No = Airless Coordinates Without Refraction = Default \n Yes = Apply Standard Atmospheric Refraction \n'>Apply&nbsp;Refraction<br><input name="RefractYN"  type="text" value="
$RefractYN" size="4" maxlength="3"></td><td style='line-height:175%; background:LightYellow;' width='25%' title=' Yes = Include Optional Object Data \n No = Do Not Include Optional Data '>Include&nbsp;Object&nbsp;Data<br><input name="ObjDataYN"  type="text" value="$ObjDataYN" size="4" maxlength="3"></td>

</tr>

<!--
<tr><td style='line-height:175%; background:LightYellow;' width='25%' title=' AU = Astronomical Units \n Km = Kilometers \n Mi  = Statute Miles '>&nbsp;</td><td style='line-height:175%; background:LightYellow;' width='25%' title=' Yes = Include Range Rate \n No = Do Not Include Range Rate.\n\n The Range Rate is the radial velocity \n or the speed at which the object is \n receding or approaching the Earth. '>&nbsp;<br><input name="SuppRangeRate"  type="hidden" value="
$SuppRangeRate" size="4" maxlength="3"></td><td style='line-height:175%; background:LightYellow;' width='25%' title=' Not Yet Defined '>&nbsp;</td><td style='line-height:175%; background:LightYellow;' width='25%' title=' Not Yet Defined '>&nbsp;</td></tr>
--->

<!-- Template extension for 4-cells.
<tr><td style='line-height:175%; background:LightYellow;' width='25%' title=''>Not Yet Defined<br>[XXXXX]</td><td style='line-height:175%; background:LightYellow;' width='25%' title=''>Not Yet Defined<br>[XXXXX]</td><td style='line-height:175%; background:LightYellow;' width='25%' title=''>Not Yet Defined<br>[XXXXX]</td><td style='line-height:175%; background:LightYellow;' width='25%' title=''>Not Yet Defined<br>[XXXXX]</td></tr>
--->

<!-- Template extension for 3-cells.
<tr>
<td colspan='1' style='line-height:175%; background:LightYellow;' title=''>Not Yet Defined<br>[XXXXX]</td><td colspan='2' style='line-height:175%; background:LightYellow;' title=''>Not Yet Defined<br>[XXXXX]</td><td colspan='1' style='line-height:175%; background:LightYellow;' title=''>Not Yet Defined<br>[XXXXX]</td>
</tr>
--->

</table>




<!-- Define [SUBMIT] button --->
<table width="
$TableWidth" align="center" border="0" cellspacing="1" cellpadding="3">
<tr><td colspan="99" style="background-color:black;"><input type="submit" name="SubmitButton" value=" S U B M I T " OnClick="
$_COMPUTING_"
></td></tr>
</table>

<!-- Yellow source code view link. --->
<br>
<table width="
$TableWidth" align="center" cellspacing="1" cellpadding="3">
<tr>
<td colspan="1" style='font-size:10pt; color:black; background:black;
                       text-align:center;' title=' Tries to Open in a New Tab. '>
<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;
            border-radius:4px; font-weight:normal;'>
&nbsp;View/Copy Source Code&nbsp;</a></b>
</td>
</tr>
</table>


<!-- Define TextArea1 --->
<table width="
$TableWidth" align="center" border="0" cellspacing="1" cellpadding="3">
<tr>
<td colspan="99" style="text-align:center; color:GreenYellow; background-color:black;">Double-Click Within Text Area to Select ALL Text<br>
<textarea ID="TextArea1" 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>
</table>


<!-- Define TextArea2 --->
<table width="
$TableWidth" align="center" border="0" cellspacing="1" cellpadding="3">
<tr>
<td colspan="99" style="text-align:center; color:GreenYellow; background:black;">Double-Click Within Text Area to Select ALL Text<br>
<textarea ID="TextArea2" name="TextArea2" style="color:black; background:LightYellow; padding:6px;" cols="
$Text2Cols" rows="$Text2Rows" ReadOnly OnDblClick="this.select();" OnMouseUp="return true;">
$TextArea2Text
</textarea>
</tr>
</table>


<!-- Define page footer --->
<table width="
$TableWidth" align="center" border="0" cellspacing="1" cellpadding="3">
<tr>
<td colspan="99" style="color:GreenYellow; background:black;">PHP Program by 
$_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;


// ####################################################
// INPUT INTERFACE UTILITY FUNCTIONS
// ####################################################

/*
   ###########################################################################
   This function returns the decimal hours equivalent to the given HMS string.

   Generic. No special error checking is done.

   NO DEPENDENCIES
   ###########################################################################
*/

   
function HMS_to_Hours ($HMSString$Decimals=16)
{
   
$HHmmss   trim($HMSString);
   
$decimals trim($Decimals);

/* ------------------------------------------------
   Account for and preserve any numerical +/- sign.
   Internal work will use absolute values and any
   numerical sign will be reattached the output.
*/
   
$NumSign substr($HHmmss,0,1);
   if (
$NumSign == '-')
      {
$HHmmss substr($HHmmss,1,StrLen($HHmmss));}
   else
      {
       if (
$NumSign == '+')
          {
$HHmmss substr($HHmmss,1,StrLen($HHmmss));}

       
$NumSign '+';
      }

// ------------------------------------------------------------------
// Replace any colons : with blank spaces and remove any white space.

   
$HHmmss PReg_Replace("/\s+/"" "Str_Replace(":"" "$HHmmss));

// ----------------------------------------
// Count the HMS time elements from 1 to 3.

   
$n  Substr_Count($HHmmss' ');

   
$hh $mm $ss 0;

/* ----------------------------------------------------------------------
   Collect all given time element values.  They can be integer or decimal
   values. Only counts up to three HMS values and any values beyond those
   are simply ignored.
*/
   
for ($i=0;   $i 1;   $i++)
  {
   if (
$n == 1){list($hh)         = PReg_Split("[ ]"$HHmmss);}
   if (
$n == 2){list($hh,$mm)     = PReg_Split("[ ]"$HHmmss);}
   if (
$n == 3){list($hh,$mm,$ss) = PReg_Split("[ ]"$HHmmss);}
  }

// ------------------------------------------------------------------------
// Compute HMS equivalent in decimal hours to the given number of decimals.

   
return $NumSign.(round((3600*$hh 60*$mm $ss)/3600,$decimals));

// End of  HMS_to_Hours(...)



/*
   ###########################################################################
   This function returns an HMS string equivalent to an hours argument rounded
   to the specified number of decimals.

   Generic. No special error checking is done.

   NO DEPENDENCIES
   ###########################################################################
*/

   
function Hours_to_HMS ($Hours$Decimals=0)
{
   
$hours trim($Hours);  $NumSign = ($hours 0)? '-':'';
   
$hours Str_Replace('+'''Str_Replace('-'''$hours));

// ---------------------
// Set working decimals.

   
$Q 32;
   
$decimals floor(abs(trim($Decimals)));
   
$decimals = ($decimals $Q)? $Q $decimals;
   
$decimals = ($decimals <  0)?  $decimals;

// ------------------------------------
// Compute hours,minutes and seconds to
// the specified number of decimals.

   
$hh  bcAdd($hours'0');
   
$min bcMul('60'bcSub($hours$hh$Q),$Q);
   
$mm  bcAdd($min'0');
   
$sec bcMul('60'bcSub($min$mm$Q),$Q);
   
$ss  SPrintF("%1.$decimals"."f"$sec);
          if (
$ss 10){$ss "0$ss";}

// -------------------------------------------
// Try to account for that blasted 60s glitch.

   
if ($ss == 60) {$mm += 1;  $ss 0;}
   if (
$mm == 60) {$hh += 1;  $mm 0;}

// ------------------------------------------
// Construct and return time elements string.

   
$hh SPrintF("%02d"$hh);
   
$mm SPrintF("%02d"$mm);
   
$ss SPrintf("%1.$decimals"."f"$ss);
         if (
$ss 10){$ss "0$ss";}

   return 
"$NumSign$hh:$mm:$ss";

// End of  Hours_to_HMS (...)




/*
   ###########################################################################
   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.

   NO DEPENDENCIES
   ###########################################################################
*/

   
function DMS_to_Deg ($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  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_Deg(...)











/*
   ###########################################################################
   This function returns a simple ephemeris from the NASA/JPL Horizons API.

   If no ephemeris is returned, then the text from the API is returned as-is
   because it could be an error message or some other status or query text.

   NO DEPENDENCIES
   ###########################################################################
*/
   
function Topocentric_Ephem ($BodyID,$TimeScale,$TimeZone,$StartDateTime,
                               
$StopDateTime,$StepSize,$LonDeg,$LatDeg,$AltMet,
                               
$DaySumYN,$RefractYN,$HMSorDEG,$ObjDataYN,
                               
$SuppRangeRate,$JDateYN,$AUorKMorMI,$Quantities)
{

// ===========================================================================
// Construct query URL for the NASA/JPL Horizons API.

   
$BodyID  trim($BodyID);
   
$Command URLEncode($BodyID);
   
$AltKm   trim($AltMet) / 1000;

// --------------------------------------------------------------------------
// Adjust for TT scale.  Time Zone and Daylight/Summer Time mode are ignored.

   
$TimeScale = (StrToUpper(substr(trim($TimeScale),0,1)) == 'T')? 'TT':'UT';
                 if (
$TimeScale == 'TT')
                    {
$TimeZone '+00:00'$DaySumYN 'No';}

/* -----------------------------------------------------------
   Adjust for Daylight/Summer Time, if indicated. This assumes
   that the Time Zone string is given in the standard +-HH:mm
   format (Example: -05:00 or +05:30) or an error may occur.
*/

   
$DaySumYN  substr(StrToUpper(trim($DaySumYN)),0,1);
   
$DSSTAdj = ($DaySumYN == '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";


   
$AUorKMorMI StrToUpper(substr($AUorKMorMI,0,1));
                 if (
$AUorKMorMI == 'A') {$AUorKMorMI 'AU';}
                 if (
$AUorKMorMI == 'K') {$AUorKMorMI 'KM';}
                 if (
$AUorKMorMI == 'M') {$AUorKMorMI 'MI';}

   
$ObjDataYN = ((substr(StrToUpper(trim($ObjDataYN)),0,1)) == 'Y')? 'YES':'NO';

   
$SuppRangeRate = ((substr(StrToUpper(trim($SuppRangeRate)),0,1)) == 'Y')? 'YES':'NO';

   
$RefractYN substr(StrToUpper(trim($RefractYN)),0,1);
   
$REFRACTEDorAIRLESS = ($RefractYN == 'Y')? 'REFRACTED':'AIRLESS';

   
$HMSorDEG = (StrToUpper(substr(trim($HMSorDEG),0,1)) == 'D')? 'DEG':'HMS';

   
$CalFormat = ((substr(StrToUpper(trim($JDateYN)),0,1)) == 'Y')? 'BOTH':'CAL';

   
$Quantities trim($Quantities);

   if (
$AUorKMorMI == 'MI') {$AUorKMorMI 'AU';}

   
$From_Horizons_API =
   
"https://ssd.jpl.nasa.gov/api/horizons.api?format=text" .
   
"&COMMAND='$Command'"                     .
   
"&MAKE_EPHEM='YES'"                       .
   
"&EPHEM_TYPE='OBSERVER'"                  .
   
"&OBJ_DATA='$ObjDataYN'"                  .
   
"&CAL_FORMAT='$CalFormat'"                .
   
"&REF_SYSTEM='ICRF'"                      .
   
"&RANGE_UNITS='AU'"                       .
   
"&SUPPRESS_RANGE_RATE='$SuppRangeRate'"   .
   
"&ANG_FORMAT='$HMSorDEG'"                 .
   
"&APPARENT='$REFRACTEDorAIRLESS'"         .
   
"&CENTER='COORD@399'"                     .
   
"&COORD_TYPE='GEODETIC'"                  .
   
"&SITE_COORD='$LonDeg,$LatDeg,$AltKm'"    .
   
"&TIME_DIGITS='SECONDS'"                  .
   
"&TIME_ZONE='$TimeZone'"                  .
   
"&START_TIME='$StartDateTime '"           .
   
"&STOP_TIME='$StopDateTime'"              .
   
"&STEP_SIZE='$StepSize'"                  .
   
"&EXTRA_PREC='NO'"                        .
   
"&CSV_FORMAT='YES'"                       .
   
"&QUANTITIES='$Quantities'"               ;
// ===========================================

/* -----------------------------------------------------------------------
   Send query to Horizons API to obtain the apparent topocentric ephemeris
   data for the given body ID as a plain-text CSV ephemeris table.
*/
   
$TopoEphem Str_Replace(",\n"" \n"File_Get_Contents($From_Horizons_API));

/* --------------------------------------------------------
   If no ephemeris is found,  then return the text from the
   API as-is. It may be an error message or some other text.
*/
   
if (StrPos($TopoEphem'$$SOE') === FALSE) {return HTMLEntities($TopoEphem);}

   return 
HTMLEntities($TopoEphem);

// End of  Topocentric_Ephem(...)




/*
    This function parses the raw ephemeris table and generates a
    basic printable ephemeris table.
*/
   
function Parse_Raw_Ephemeris ($RawEphemeris$EphemFormat)
{
 GLOBAL 
$HMSorDEG$AUorKMorMI;

   
$RawEphem RTrim($RawEphemeris);
   
$EphemFormat trim($EphemFormat);

/* Extract the ephemris table from between the ephemeris markers
   $$SOE = Start Of Ephemeris
   $$EOE = End Of Ephemeris
*/
   
$w $RawEphem;

/* -----------------------------------------------------
   Set pointers to start and end of CSV ephemeris table.
   A value of FALSE means no ephemeris was found within
   the given source text.
*/
   
$i StrPos($w'$$SOE');
   
$j StrPos($w'$$EOE');

/* ----------------------------------------------------
   Extract ONLY the existing ephemeris SCV data line(s)
   from between the ephemeris pointers.
*/
   
$EphemCSVTable trim(substr($w$i+5$j-$i-5));

// Split ephemeris table into a text strin array.
// Each line is a separate text string.

   
$wArray PReg_Split("[\n]"$EphemCSVTable);
   
$wCount count($wArray);
   
$output '';

// Extract individual ephemeris statistics from each
// table line and reconstruct into a compact table.
   
for($i=0;   $i $wCount;   $i++)
  {
   
$CurrLine trim($wArray[$i]);

// Parse the current line.
   
list($DateTimeStr$w,$w$RA$Decl$Dist,$w,$w,$ApMag) = PReg_Split("[,]"$CurrLine);

// Separate the calendar date and time.
   
list($DateStr$TimeStr) = Preg_Split("[ ]"$DateTimeStr);

// Strip redundant spaces from returned atatistics.
   
$DateStr trim($DateStr);
   
$TimeStr trim($TimeStr);
   
$RA      trim($RA);
   
$Decl    trim($Decl);
   
$Dist    trim($Dist);
   
$ApMag   SPrintF("%+7.3f"trim($ApMag));

// Determine which display format and column header to apply.
   
if ($EphemFormat == 1)
{
   
$xDist AU_to_Km_Mi ($BodyID$Dist'AU');

   
$ColumnHeader =
   
"Calend_Date    Time      RA__HMS    Declin__DMS     Dist_AU     Vis_Mag
-----------  --------  -----------  -----------  -------------  -------"
;
$output  .= "$DateStr  $TimeStr  $RA  $Decl  $xDist  $ApMag\n";
}


   if (
$EphemFormat == 2)
{
   
$xDist AU_to_Km_Mi ($BodyID$Dist'AU');
   
$ColumnHeader =
   
"Calend_Date    Time       RA &deg;     Declin &deg;     Dist_AU    Vis_Mag
-----------  --------  ---------  ---------  -------------  -------"
;
$output  .= "$DateStr  $TimeStr  $RA  $Decl  $xDist  $ApMag\n";
}


   if (
$EphemFormat == 3)
{
   
$xDist AU_to_Km_Mi ($BodyID$Dist'KM');
   
$ColumnHeader =
   
"Calend_Date    Time      RA__HMS    Declin__DMS     Dist_Km     Vis_Mag
-----------  --------  -----------  -----------  -------------  -------"
;
$output  .= "$DateStr  $TimeStr  $RA  $Decl  $xDist  $ApMag\n";
}


   if (
$EphemFormat == 4)
{
   
$xDist AU_to_Km_Mi ($BodyID$Dist'MI');
   
$ColumnHeader =
   
"Calend_Date    Time       RA &deg;     Declin &deg;     Dist_Km     Vis_Mag
-----------  --------  ---------  ---------  -------------  -------"
;
  
$output  .= "$DateStr  $TimeStr  $RA  $Decl  $xDist  $ApMag\n";
}


   if (
$EphemFormat == 5)
{
   
$xDist AU_to_Km_Mi ($BodyID$Dist'MI');

   
$ColumnHeader =
   
"Calend_Date    Time       RA_HMS    Declin__DMS     Dist_Mi     Vis_Mag
-----------  --------  -----------  -----------  -------------  -------"
;
$output  .= "$DateStr  $TimeStr  $RA  $Decl  $xDist  $ApMag\n";
}


   if (
$EphemFormat == 6)
{
   
$xDist AU_to_Km_Mi ($BodyID$Dist'MI');

   
$ColumnHeader =
   
"Calend_Date    Time       RA &deg;     Declin &deg;       Dist_Mi     Vis_Mag
-----------  --------  ---------  -----------  -------------  -------"
;
$output  .= "$DateStr  $TimeStr  $RA   $Decl   $xDist  $ApMag\n";
}

  }
// DONE.
   
return "$ColumnHeader\n$output";
}




// ----------------------------------------
// This function converts AUs to kilometers
// and statute miles.
   
function AU_to_Km_Mi ($BodyID$AU$DistUnits='AU')
{
   
$BodyID trim($BodyID); // exit("BodyID = $BodyID");

   
$AU FloatVal(trim($AU));
   
$DistUnits StrToUpper(trim($DistUnits));

   
$RoundOff 11;

   if (
$DistUnits == 'KM' and $AU 0.05) {$RoundOff 3;}
   if (
$DistUnits == 'KM')
      {
       
$w Number_Format($AU 149597870.7$RoundOff);
            return(
substr("             $w", -13));
       }

   if (
$DistUnits == 'MI' and $AU 0.05) {$RoundOff 3;}
   if (
$DistUnits == 'MI') {return Number_Format($AU 149597870.7 1.609344$RoundOff);}

   if (
$DistUnits == 'AU') {return Number_Format($AU$RoundOff);}
}


// END OF PROGRAM

?>