<?php
/*
###########################################################################
This program gives the current ICRF coordinates, distances and light times
for both Voyager 1 and Voyager 2 via the NASA/JPL Horizons API.
AUTHOR : Jay Tanner - 2025
LANGUAGE : PHP v8.2.12
LICENSE : Public Domain
###########################################################################
When will the light time to the Voyagers equate to 24 hours?
24 hours = 1440 minutes
Both Voyagers are headed in different directions at different
speeds, so the dates where they both reach a light or signal
time of 24 hours are widely separated. In this case, the
difference between them is some 9 years.
=================================================================
VOYAGER 1:
Date__(UT)__HR:MN:SS, Date_________JDUT, , , 1-way_down_LT
2026-Nov-17 00:00:00, 2461361.500000000, , , 1439.74276855
2026-Nov-18 00:00:00, 2461362.500000000, , , 1439.86657188
2026-Nov-19 00:00:00, 2461363.500000000, , , 1439.98846530
2026-Nov-20 00:00:00, 2461364.500000000, , , 1440.10843736
2026-Nov-21 00:00:00, 2461365.500000000, , , 1440.22647741
2026-Nov-22 00:00:00, 2461366.500000000, , , 1440.34257554
2026-Nov-23 00:00:00, 2461367.500000000, , , 1440.45672244
-----------------------------------------
Derived Lagrangian Interpolation Table 1:
Light_Time vs JDate
1439.74276855 2461361.5
1439.86657188 2461362.5
1439.98846530 2461363.5
1440.10843736 2461364.5
1440.22647741 2461365.5
1440.34257554 2461366.5
1440.45672244 2461367.5
Light Time = 1440 Minutes (24 Hours)
at JDUT = 2461363.595452058
= 2026-Nov-19-Thu at 02:17:27.058 UT
=================================================================
VOYAGER 2:
Date__(UT)__HR:MN:SS, Date_________JDUT, , , 1-way_down_LT
2035-Oct-30 00:00:00, 2464630.500000000, , , 1439.51994751
2035-Oct-31 00:00:00, 2464631.500000000, , , 1439.69796512
2035-Nov-01 00:00:00, 2464632.500000000, , , 1439.87545799
2035-Nov-02 00:00:00, 2464633.500000000, , , 1440.05239208
2035-Nov-03 00:00:00, 2464634.500000000, , , 1440.22873372
2035-Nov-04 00:00:00, 2464635.500000000, , , 1440.40444970
2035-Nov-05 00:00:00, 2464636.500000000, , , 1440.57950730
-----------------------------------------
Derived Lagrangian Interpolation Table 2:
Light_Time vs JDate
1439.51994751 2464630.5
1439.69796512 2464631.5
1439.87545799 2464632.5
1440.05239208 2464633.5
1440.22873372 2464634.5
1440.40444970 2464635.5
1440.57950730 2464636.5
Light Time = 1440 Minutes (24 Hours)
at JDUT = 2464633.203548626
= 2035-Nov-01-Thu at 16:53:06.601 UT
###########################################################################
These Date/Time values were obtained by 7-point Lagrangian Interpolation
based on the above Light Times values vs. Julian Dates tabulated at 1-day
intervals.
--------------
FINAL RESULTS:
Voyager 1 Light Time reaches 24 hours on AD 2026-Nov-19-Thu at 02:17 UTC.
Voyager 2 Light Time reaches 24 hours on AD 2035-Nov-01-Thu at 16:53 UTC.
COMPUTED ON AD 2025-OCT-12-SUN via THE NASA/JPL HORIZONS API
###########################################################################
*/
// ---------------------
// Get current year UTC.
$cYear = GMDate('Y');
// ----------------------------
// Define ephemeris parameters.
$TimeScale = "UT";
$TimeZone = "+00:00";
$DaySumYN = 'No';
$StartDateTime = GMDate('Y-m-d H:i:s');
$StopDateTime = GMDate('Y-m-d H:i:') . FloatVal(GMDate('s')) . '.001';
$StepSize = '1 day';
// -----------------------
// RA, Declination (ICRF),
// Distance, Light Time.
$Quantities = '1,20,21';
// -----------------------------------------
// Compute JD Number for current date (UTC).
$Y = GMDate('Y');
$m = IntVal(GMDate('m'));
$d = IntVal(GMDate('d'));
$CurrJDNumUTC = GregorianToJD($m, $d, $Y);
/*
------------------------------------------------------
Compute time interval to date since initial launches.
Diff = 17 days between launches
*/
// ------------------------------------------------------------
// V2 launched first (1977-Aug-20 @ 14:29 UTC = JDNum 2443376).
$xDaysSinceV2L = $CurrJDNumUTC - 2443376;
$YearsV2L = SPrintF("%6.5f", $xDaysSinceV2L / 365.25);
$DaysSinceV2L = Number_Format($xDaysSinceV2L);
$Weeks = floor(($xDaysSinceV2L) / 7);
$Days = (($xDaysSinceV2L) % 7);
if ($Days == 0)
{
$YearsV2L = SPrintF("%6.5f", 7 * $Weeks / 365.25);
$Days = '';
$Weeks = Number_Format($Weeks);
$WeeksDaysV2 = "$Weeks weeks $Days";
}
else
{
$Ds = ($Days <> 1)? 's':'';
$Days = "and $Days day$Ds";
$Weeks = Number_Format($Weeks);
$WeeksDaysV2 = "$Weeks weeks $Days";
}
// ------------------------------------------------------------
// V1 launched later (1977-Sep-06 @ 12:56 UTC = JDNum 2443393).
$xDaysSinceV1L = $CurrJDNumUTC - 2443393;
$YearsV1L = SPrintF("%6.5f", $xDaysSinceV1L / 365.25);
$DaysSinceV1L = Number_Format($xDaysSinceV1L);
$Weeks = floor(($xDaysSinceV1L) / 7);
$Days = (($xDaysSinceV1L) % 7);
if ($Days == 0)
{
$Days = '';
$Weeks = Number_Format($Weeks);
$WeeksDaysV1 = "$Weeks weeks $Days";
}
else
{
$Ds = ($Days <> 1)? 's':'';
$Days = "and $Days day$Ds";
$Weeks = Number_Format($Weeks);
$WeeksDaysV1 = "$Weeks weeks $Days";
}
/*
-----------------------------------------------------------------------
WHAT IS THE CURRENT DISTANCE BETWEEN VOYAGER 1 AND VOYAGER 2 IN SPACE ?
We are simply computing the distance between two points in 3D classical
Euclidean space. In this case, the points in space are the Voyager 1
and the Voyager 2 spacecraft.
ICRF coordinates are used.
Distances are in AUs.
NOTE:
In most computer languages, you have to convert angles from
degrees to radians for use with the trigonometric functions.
DEFINITIONS:
1, 2 = Subscripts for Voyager numbers
RA, Decl = Right Ascension and Declination ICRF
D1, D2 = Earth distances to Voyagers 1, 2
D12 = Distance between the Voyagers
Then
Compute rectangular 3D XYZ-coordinates for Voyager 1.
x1 = D1 * cos(RA1) * cos(Decl1)
y1 = D1 * sin(RA1) * cos(Decl1)
z1 = D1 * sin(Decl1)
and
Compute rectangular 3D XYZ-coordinates for Voyager 2.
x2 = D2 * cos(RA2) * cos(Decl2)
y2 = D2 * sin(RA2) * cos(Decl2)
z2 = D2 * sin(Decl2)
Compute differences in their rectangular 3D coordinates.
dx = x2 - x1
dy = y2 - y1
dz = z2 - z1
Finally, compute Euclidean 3D distance between Voyagers.
D12 = SqRoot(dx*dx + dy*dy + dz*dz)
*/
// ---------------------------------
// Define output header text blocks.
$OutputTextHeader1 =
"GEOCENTRIC ASTROMETRIC EPHEMERIS
Target Object ID = Voyager 1
Start Date/Time = $StartDateTime UTC
------------------------------------------------------------------------------
";
$OutputTextHeader2 =
"GEOCENTRIC ASTROMETRIC COORDINATES
Target Object ID = Voyager 2
Start Date/Time = $StartDateTime UTC
------------------------------------------------------------------------------
";
// --------------------------
// Compute astrometric stats.
$RawEphem1 = Voyager('Voyager 1',$StartDateTime, $StopDateTime, '');
$RawEphem2 = Voyager('Voyager 2',$StartDateTime, $StopDateTime, '');
/*
Convert RA and Decl from HMS/DMS into equivalent radians.
x1 = D1 * cos(RA1) * cos(Decl1)
y1 = D1 * sin(RA1) * cos(Decl1)
z1 = D1 * sin(Decl1)
x2 = D2 * cos(RA2) * cos(Decl2)
y2 = D2 * sin(RA2) * cos(Decl2)
z2 = D2 * sin(Decl2)
dx = x2 - x1
dy = y2 - y1
dz = z2 - z1
D12 = SqRoot((dx*dx) + (dy*dy) + (dz*dz))
*/
// ----------------------------------------------
// Extract ONLY the required ephemeris CSV stats.
$V1BodyCSV = trim(Get_CSV_Body($RawEphem1));
$V2BodyCSV = trim(Get_CSV_Body($RawEphem2));
// ###########################################################################
// ###########################################################################
// FOR VOYAGER 1
list (
$DateTimeStr,
$w,$w,
$RAHMS,
$DeclDMS,
$DistAU,
$LTMins
) = Preg_Split("[,]", $V1BodyCSV);
$DateTimeStr = Str_Replace(' ', ' ', $DateTimeStr);
$RAHMS = trim($RAHMS);
$DeclDMS = trim($DeclDMS);
$DistAU = trim($DistAU);
// -------------------------------
// Compute RA and Decl in radians.
$RAHrs = HMS_to_Hours($RAHMS);
$RADeg = 15 * $RAHrs;
$RArad = Deg2Rad($RADeg);
$DeclDeg = DMS_to_Deg($DeclDMS);
$Declrad = Deg2Rad($DeclDeg);
// ----------------------------------------------
// Compute rectangular coordinates for Voyager 1.
$x1 = $DistAU * cos($RArad) * cos($Declrad);
$y1 = $DistAU * sin($RArad) * cos($Declrad);
$z1 = $DistAU * sin($Declrad);
$DistAU = trim($DistAU);
$DistKm = $DistAU * 149597870.7;
$DistMi = Number_Format($DistKm / 1.609344);
$DistKm = Number_Format($DistKm);
$RAHrs = SPrintF("%14.11f", $RAHrs);
$RADeg = SPrintF("%12.10f", 15*$RAHrs);
$DeclDeg = SPrintF("%+14.10f", $DeclDeg);
// -----------------------------
// Light time to nearest second.
$LTHrs = trim($LTMins) / 60;
$LTHMS = Hours_to_HMS ($LTHrs);
// =============
// FOR VOYAGER 1
$Stats1 =
"Right Ascension Declination Distance
h m s ° ' " AU | km | mi
$RAHMS $DeclDMS $DistAU AU
$RAHrsh $DeclDeg° $DistKm km
$RADeg° $DistMi mi
Light Time $LTHMS";
list (
$DateTimeStr,
$w,$w,
$RAHMS,
$DeclDMS,
$DistAU,
$LTMins
) = Preg_Split("[,]", $V2BodyCSV);
$DateTimeStr = Str_Replace(' ', ' ', $DateTimeStr);
$RAHMS = trim($RAHMS);
$DistAU = trim($DistAU);
// -------------------------------
// Compute RA and Decl in radians.
$RAHrs = HMS_to_Hours($RAHMS);
$RADeg = $RAHrs * 15;
$RArad = Deg2Rad($RADeg);
$DeclDeg = DMS_to_Deg($DeclDMS);
$Declrad = Deg2Rad($DeclDeg);
// ------------------------------------------------
// Compute rectangular coordinates for Voyager 2.
$x2 = $DistAU * cos($RArad) * cos($Declrad);
$y2 = $DistAU * sin($RArad) * cos($Declrad);
$z2 = $DistAU * sin($Declrad);
// -----------------------------------
// Compute rectangular coordinates and
// differences between the Voyagers.
$dx = $x2 - $x1;
$dy = $y2 - $y1;
$dz = $z2 - $z1;
// -----------------------------------------------------
// Compute linear spatial distance between the Voyagers.
$D12AU = SqRt(($dx*$dx) + ($dy*$dy) + ($dz*$dz));
$D12Km = $D12AU * 149597870.7;
$D12Mi = Number_Format($D12Km / 1.609344);
$D12Km = Number_Format($D12Km);
$D12AU = SPrintF("%14.10f", $D12AU);
// ---------------------
// Format output values.
$DistKm = $DistAU * 149597870.7;
$DistMi = Number_Format($DistKm / 1.609344);
$DistKm = Number_Format($DistKm);
$RAHrs = SPrintF("%14.11f", $RAHrs);
$RADeg = SPrintF("%12.10f", 15*$RAHrs);
$DeclDeg = SPrintF("%+14.10f", $DeclDeg);
// -----------------------------
// Light time to nearest second.
$LTHrs = trim($LTMins) / 60;
$LTHMS = Hours_to_HMS ($LTHrs);
// ===============
// VOYAGER 2 STATS
$Stats2 =
"Right Ascension Declination Distance
h m s ° ' " AU | km | mi
$RAHMS $DeclDMS $DistAU AU
$RAHrsh $DeclDeg° $DistKm km
$RADeg° $DistMi mi
Light Time $LTHMS";
// --------------------------------------
// Copy ephemeris stats into (TextArea1).
$TextArea1Text =
" VOYAGER SPACECRAFT 1 and 2
CURRENT APPARENT COORDINATES AND DISTANCES FROM EARTH
AND EACH OTHER IN SPACE
Computed via the NASA/JPL Horizons API
With a Little Extra High School Math
For Current Date and Time
$DateTimeStr UTC
**************************************************************
VOYAGER 1 SPACECRAFT - JPL DATABASE ID# -31
Launched from Kennedy Space Center
1977-Sep-06-Tue @ 12:56 UTC
Time since launch = $DaysSinceV1L days = $WeeksDaysV1
= $YearsV1L years ago
CURRENT APPARENT ICRF SKY COORDINATES
$Stats1
**************************************************************
VOYAGER 2 SPACECRAFT - JPL DATABASE ID# -32
Launched from Kennedy Space Center
1977-Aug-20-Sat @ 14:29 UTC
Time since launch = $DaysSinceV2L days = $WeeksDaysV2
= $YearsV2L years ago
CURRENT APPARENT ICRF SKY COORDINATES
$Stats2
Current linear distance between Voyagers 1 and 2 in space:
$D12AU AU = $D12Km km = $D12Mi mi
SOME APPLIED DEFINITIONS AND EXTRA INFO FROM NASA/JPL
ICRF = International Celestial Reference Frame
1 AU = 1 Astronomical Unit
= 149,597,870.700 km
= 92,955,807.273 mi
1 mi = 1.609344 km
1 Standard (Julian) Year = 365.25 days
Refs:
https://www.nasa.gov/image-article/voyager-2-launched-before-voyager-1
https://www.jpl.nasa.gov/missions/voyager-2
https://www.jpl.nasa.gov/missions/voyager-1
Voyager 2 launched on Aug. 20, 1977, about two weeks before
the Sept. 6 (UTC) launch of Voyager 1.
Why the reversal of order?
The two were sent on different trajectories, and Voyager 1
was put on a path to reach its planetary targets, Jupiter
and Saturn, ahead of Voyager 2.
";
$TextArea2Text =
"
=====================================
SPECIAL NOTE ABOUT VOYAGER DISTANCES:
When tracking the distances to the Voyager spacecraft, you may
notice that there are times when the distances seem to be de-
creasing with time, rather than increasing with time.
This is because when Earth is on one side of its orbit, it is
moving towards the spacecraft faster than the spacecraft are
moving away from us. When the Earth is on the other side of
its orbit, then the situation is reversed and the distance is
increasing over time.
The linear speed of the spacecraft receding from Earth does
not change. It is the speed of the Earth from which we are
making the observations that is moving at cyclically varying
relative speed over the year orbiting the sun.
So, approximately 1/2 of a year, the spacecraft are moving
away from Earth and the other 1/2 year towards Earth, but
not necessarily at the same time, since the spacecraft are
traveling in very different directions at different speeds.
https://earthsky.org/space/voyager-spacecraft-getting-closer-to-earth/
Voyager 1 moves at a speed of 38,210 miles per hour (17 km/s).
Voyager 2 moves at a speed of 35,000 miles per hour (15 km/s).
The speed of the Earth in orbit is about
18.5 mi/s = 66,600 mi/h
29.8 km/s = 107,200 km/h
---------------------------------------------------------------
Q:
When will the light time to the Voyagers equate to 24 hours?
A:
According to the NASA/Horizons API, the answers are, as computed
on 2025-Oct-12-Sun:
=========
Voyager 1
Light Time = 1440 Minutes (24 Hours) at JDUT = 2461363.595452058
= 2026-Nov-19-Thu at 02:17:27.058 UTC
= 2026-Nov-18-Wed at 21:17:27.058 New York Standard Time
=========
Voyager 2
Light Time = 1440 Minutes (24 Hours) at JDUT = 2464633.203548626
= 2035-Nov-01-Thu at 16:53:06.601 UTC
= 2035-Nov-01-Thu at 11:53:06.601 New York Standard Time
Difference = 8.9519 years
= 3269 days 14h 36m = 467 weeks 0 days 14h 36m
These values were obtained by 7-point Lagrangian Interpolation
at 1-day intervals. See top of source code comments listing
for the applied computational data and methodology.
";
/* ---------------------------------------------------------------------------
Determine number of text rows to use in the output text area. This value
may vary randomly according to the text block length. The idea is to
eliminate the need for vertical scroll-bars within the text area or
worry about the variable height (length) of a text display area.
*/
// ---------------------------
// TextArea1 columns and rows.
$Text1Cols = 2 + Max(Array_Map('StrLen', PReg_Split("[\n]", trim($TextArea1Text))));
// if ($Text1Cols > 80) {$Text1Cols = 78;}
$Text1Rows = 3 + Substr_Count($TextArea1Text, "\n");
// ---------------------------
// TextArea2 columns and rows.
$Text2Cols = 1 + Max(Array_Map('StrLen', PReg_Split("[\n]", trim($TextArea2Text))));
if ($Text2Cols < 80) {$Text2Cols = 75;}
$Text2Rows = 2 + Substr_Count($TextArea2Text, "\n");
// ---------------------------------
// Construct output table structure.
$HTMLTableOut =
"<br>
<!-- Define main page title/header. --->
<table width='550' align='center' border='0' cellspacing='1' cellpadding='3'>
<tr><td colspan='99' style='color:white; background:#000066; font-size:14pt; font-weight:normal; border:2px solid white; border-radius:8px 8px 0px 0px;'>Current Status of Voyagers 1 and 2 Spacecraft<br><span style='font-size:10.5pt;'>Computations via the NASA/JPL Horizons API</span><br><span style='font-size:8pt;'>PHP Program by Jay Tanner of Geneva, NY, USA - $cYear</span></td></tr>
<tr><td>
<span style='color:GreenYellow; font-size:9pt; font-weight:normal;'><b>Refresh Page to Update to Current Moment UTC</b></span>
<br><br>
<!-- Yellow source code view link. --->
<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;
font-size:8.5pt; border-radius:4px;'>
View/Copy PHP Source Code </a>
<br><br>
<span style='color:GreenYellow; font-size:9pt; font-weight:normal;'>Double-Click Within Text Area to Select ALL Text</span><br>
<textarea name='TextArea1' cols='$Text1Cols' rows='$Text1Rows' ReadOnly OnDblClick='this.select();' OnMouseUp='return true;'>
$TextArea1Text
</textarea>
<br>
<span style='color:GreenYellow; font-size:9pt; font-weight:normal;'>Double-Click Within Text Area to Select ALL Text</span><br>
<textarea name='TextArea2' cols='72' rows='$Text2Rows' ReadOnly OnDblClick='this.select();' OnMouseUp='return true;'>
$TextArea2Text
</textarea>
</td></tr>
</table>
<br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br><br>
";
print <<< END_OF_HTML
<!DOCTYPE HTML>
<HTML>
<head>
<title>Current Status of Voyagers 1 and 2</title>
<style>
BODY
{
background:black; color:white; font-family:Verdana;
font-size:10pt;
}
TD
{
background:black; color:silver; font-family:Verdana;
font-size:10.5pt; font-weight:normal; text-align:center;
padding:8px;
}
TEXTAREA
{
background:white; color:black; font-family:monospace; font-size:11pt;
font-weight:bold; padding:6pt; white-space:pre; border-radius:8px;
line-height:125%;
}
::selection{background-color:yellow !important; color:black !important;}
::-moz-selection{background-color:yellow !important; color:black !important;}
</style>
</head>
<body>
$HTMLTableOut
</body>
</HTML>
END_OF_HTML;
/*
###########################################################################
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 Voyager($TargObjID,$StartDateTime,$StopDateTime,$OutputTextHeader)
{
// ===========================================================================
// Construct query URL for the NASA/JPL Horizons API.
$TargObjID = trim($TargObjID);
$Command = URLEncode($TargObjID);
/* -----------------------------------------------------------
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);
$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";
*/
$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'" .
"&RANGE_UNITS='AU'" .
"&SUPPRESS_RANGE_RATE='YES'" .
"&ANG_FORMAT='HMS'" .
"&APPARENT='AIRLESS'" .
"&CENTER='500@399'" .
"&TIME_DIGITS='SECONDS'" .
"&TIME_ZONE='+00:00'" .
"&START_TIME='$StartDateTime'" .
"&STOP_TIME='$StopDateTime'" .
"&STEP_SIZE='1 day'" .
"&EXTRA_PREC='YES'" .
"&CSV_FORMAT='YES'" .
"&QUANTITIES='2,20,21'" ;
// ===========================================
/* -----------------------------------------------------------------------
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);}
$TopoEphem = "$OutputTextHeader\n$TopoEphem";
return HTMLEntities($TopoEphem);
} // End of Voyager(...)
// *******************************
// SPECIAL INNER UTILITY FUNCTIONS
// *******************************
/*
###########################################################################
This function can be used to extract any ephemeris table body from all of
the other extraneous text that surrounds it. An ephemeris table can consist
of 1 line up to thousands of lines of CSV data.
The ephemeris CSV data lines are located between two markers:
$$SOE = Start Of Ephemeris marker
and
$$EOE = End Of Ephemeris marker
Generic. No special error checking is done.
NO DEPENDENCIES
###########################################################################
*/
function Get_CSV_Body ($RawEphemText)
{
// ---------------------------------
// Read raw ephemeris text argument.
$w = trim($RawEphemText);
/* -------------------------------------------------
Set pointers to start and end of ephemeris table.
If there is no ephemeris found, then return the
raw text as-is, nothing is done. It may possibly
be an error or some other text instead.
*/
$i = StrPos($w, "\$\$SOE\n"); if ($i === FALSE) {return $w;}
$j = StrPos($w, "\$\$EOE\n");
/* --------------------------------------------
Extract ONLY the required ephemeris CSV data
line(s) from between the Start/End pointers.
*/
$EphemTableText = trim(substr($w, $i+5, $j-$i-5));
return (substr($EphemTableText,0,1) == 'b')? $EphemTableText : " $EphemTableText";
} // End of Get_CSV_Body (...)
/*
###########################################################################
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 = 1 + 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 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 = 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_Deg(...)
/*
###########################################################################
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)? 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 (...)
?>