first show lad
authorRuss Handorf <rhandorf@handorf.org>
Tue, 24 May 2016 00:21:39 +0000 (20:21 -0400)
committerRuss Handorf <rhandorf@handorf.org>
Tue, 24 May 2016 00:21:39 +0000 (20:21 -0400)
README [new file with mode: 0644]
php/cssinfo [new file with mode: 0644]
php/gpsd/gpsd.php [new file with mode: 0644]
php/gpsd/gpsd_config.inc [new file with mode: 0644]
php/index.php [new file with mode: 0644]
php/ppsstatus.php [new file with mode: 0644]
php/time-ajax.php [new file with mode: 0644]
php/time1.php [new file with mode: 0644]
php/time2.php [new file with mode: 0644]

diff --git a/README b/README
new file mode 100644 (file)
index 0000000..fad8491
--- /dev/null
+++ b/README
@@ -0,0 +1,5 @@
+This is the code and notes for the Rb Clock.
+
+Two code paths:
+1. PHP- for web ui
+2. Python- for the LCD control
diff --git a/php/cssinfo b/php/cssinfo
new file mode 100644 (file)
index 0000000..f11009a
--- /dev/null
@@ -0,0 +1,10 @@
+<form class="form-container">
+<div class="form-title"><h2>Sign up</h2></div>
+<div class="form-title">Name</div>
+<input class="form-field" type="text" name="firstname" /><br />
+<div class="form-title">Email</div>
+<input class="form-field" type="text" name="email" /><br />
+<div class="submit-container">
+<input class="submit-button" type="submit" value="Submit" />
+</div>
+</form>
diff --git a/php/gpsd/gpsd.php b/php/gpsd/gpsd.php
new file mode 100644 (file)
index 0000000..a6f2045
--- /dev/null
@@ -0,0 +1,645 @@
+<?php
+
+# @MASTER@
+
+# Copyright (c) 2006,2010 Chris Kuethe <chris.kuethe@gmail.com>
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# Changed to Google Maps API v3, requires no API key, and shorter code
+# Sanjeev Gupta <ghane0@gmail.com> 2013-01-05
+
+global $head, $blurb, $title, $showmap, $autorefresh, $footer, $gmap_key;
+global $server, $advertise, $port, $open, $swap_ew, $testmode;
+$testmode = 1; # leave this set to 1
+
+# Public script parameters:
+#   host: host name or address where GPSd runs. Default: from config file
+#   port: port of GPSd. Default: from config file
+#   op=view: show just the skyview image instead of the whole HTML page
+#     sz=small: used with op=view, display a small (240x240px) skyview
+#   op=json: respond with the GPSd POLL JSON structure
+#     jsonp=prefix: used with op=json, wrap the POLL JSON in parentheses
+#                   and prepend prefix
+
+# If you're running PHP with the Suhosin patch (like the Debian PHP5 package),
+# it may be necessary to increase the value of the
+# suhosin.get.max_value_length parameter to 2048. The imgdata parameter used
+# for displaying the skyview is longer than the default 512 allowed by Suhosin.
+# Debian has the config file at /etc/php5/conf.d/suhosin.ini.
+
+# this script shouldn't take more than a few seconds to run
+set_time_limit(3);
+ini_set('max_execution_time', 3);
+
+if (!file_exists("gpsd_config.inc"))
+       write_config();
+
+require_once("gpsd_config.inc");
+
+# sample data
+$resp = <<<EOF
+{"class":"POLL","time":"2010-04-05T21:27:54.84Z","active":1,
+ "tpv":[{"class":"TPV","tag":"MID41","device":"/dev/ttyUSB0",
+           "time":1270517264.240,"ept":0.005,"lat":40.035093060,
+           "lon":-75.519748733,"alt":31.1,"track":99.4319,
+           "speed":0.123,"mode":3}],
+ "sky":[{"class":"SKY","tag":"MID41","device":"/dev/ttyUSB0",
+              "time":"2010-04-05T21:27:44.84Z","hdop":9.20,"vdop":12.1,
+              "satellites":[{"PRN":16,"el":55,"az":42,"ss":36,"used":true},
+                            {"PRN":19,"el":25,"az":177,"ss":0,"used":false},
+                            {"PRN":7,"el":13,"az":295,"ss":0,"used":false},
+                            {"PRN":6,"el":56,"az":135,"ss":32,"used":true},
+                            {"PRN":13,"el":47,"az":304,"ss":0,"used":false},
+                            {"PRN":23,"el":66,"az":259,"ss":40,"used":true},
+                            {"PRN":20,"el":7,"az":226,"ss":0,"used":false},
+                            {"PRN":3,"el":52,"az":163,"ss":32,"used":true},
+                            {"PRN":31,"el":16,"az":102,"ss":0,"used":false}
+                           ]
+             }
+            ]
+}
+EOF;
+
+
+
+# if we're passing in a query, let's unpack and use it
+$op = isset($_GET['op']) ? $_GET['op'] : '';
+if (isset($_GET['imgdata']) && $op == 'view'){
+       $resp = base64_decode($_GET['imgdata']);
+       if ($resp){
+               gen_image($resp);
+               exit(0);
+       }
+} else {
+       if (isset($_GET['host']))
+               if (!preg_match('/[^a-zA-Z0-9\.-]/', $_GET['host']))
+                       $server = $_GET['host'];
+
+       if (isset($_GET['port']))
+               if (!preg_match('/\D/', $_GET['port']) && ($port>0) && ($port<65536))
+                       $port = $_GET['port'];
+
+       if ($testmode){
+               $sock = @fsockopen($server, $port, $errno, $errstr, 2);
+               @fwrite($sock, "?WATCH={\"enable\":true}\n");
+               usleep(1000);
+               @fwrite($sock, "?POLL;\n");
+               usleep(1000);
+               for($tries = 0; $tries < 10; $tries++){
+                       $resp = @fread($sock, 2000); # SKY can be pretty big
+                       if (preg_match('/{"class":"POLL".+}/i', $resp, $m)){
+                               $resp = $m[0];
+                               break;
+                       }
+               }
+               @fclose($sock);
+               if (!$resp)
+                       $resp = '{"class":"ERROR","message":"no response from GPS daemon"}';
+       }
+}
+
+if ($op == 'view')
+       gen_image($resp);
+else if ($op == 'json')
+       write_json($resp);
+else
+       write_html($resp);
+
+exit(0);
+
+###########################################################################
+# Function to decide if a PRN is a true GPS bird or SBAS, GBAS, etc.
+# Sanjeev Gupta <ghane0@gmail.com> 20150408
+# Please refer to gps.h lines ~~ 95 , for a central definition
+function isGPS($PRN) {
+       if ($PRN <= 32) return TRUE ;                   # Navstar GPS
+       if ($PRN >= 64 && $PRN <= 96) return TRUE ;     # GLONASS
+       if ($PRN >= 159 ) return TRUE ;                 # BeiDou ?
+       return FALSE ;                                  # SBAS, GBAS, unknown
+       }
+
+function colorsetup($im){
+       $C['white']     = imageColorAllocate($im, 255, 255, 255);
+       $C['ltgray']    = imageColorAllocate($im, 191, 191, 191);
+       $C['mdgray']    = imageColorAllocate($im, 127, 127, 127);
+       $C['dkgray']    = imageColorAllocate($im, 63, 63, 63);
+       $C['black']     = imageColorAllocate($im, 0, 0, 0);
+       $C['red']       = imageColorAllocate($im, 255, 0, 0);
+       $C['brightgreen'] = imageColorAllocate($im, 0, 255, 0);
+       $C['darkgreen'] = imageColorAllocate($im, 0, 192, 0);
+       $C['blue']      = imageColorAllocate($im, 0, 0, 255);
+       $C['cyan']      = imageColorAllocate($im, 0, 255, 255);
+       $C['magenta']   = imageColorAllocate($im, 255, 0, 255);
+       $C['yellow']    = imageColorAllocate($im, 255, 255, 0);
+       $C['orange']    = imageColorAllocate($im, 255, 128, 0);
+
+       return $C;
+}
+
+function legend($im, $sz, $C){
+       $r = 30;
+       $fn = 5;
+       $x = $sz - (4*$r+7) - 2;
+       $y = $sz - $r - 3;
+
+       imageFilledRectangle($im, $x, $y, $x + 4*$r + 7, $y + $r +1, $C['dkgray']);
+       imageRectangle($im, $x+0*$r+1, $y+1, $x + 1*$r + 0, $y + $r, $C['red']);
+       imageRectangle($im, $x+1*$r+2, $y+1, $x + 2*$r + 2, $y + $r, $C['yellow']);
+       imageRectangle($im, $x+2*$r+4, $y+1, $x + 3*$r + 4, $y + $r, $C['darkgreen']);
+       imageRectangle($im, $x+4*$r+6, $y+1, $x + 3*$r + 6, $y + $r, $C['brightgreen']);
+       imageString($im, $fn, $x+3+0*$r, $y+$r/3, "<30", $C['red']);
+       imageString($im, $fn, $x+5+1*$r, $y+$r/3, "30+", $C['yellow']);
+       imageString($im, $fn, $x+7+2*$r, $y+$r/3, "35+", $C['darkgreen']);
+       imageString($im, $fn, $x+9+3*$r, $y+$r/3, "40+", $C['brightgreen']);
+}
+
+function radial($angle, $sz){
+       #turn into radians
+       $angle = deg2rad($angle);
+
+       # determine length of radius
+       $r = $sz * 0.5 * 0.95;
+
+       # and convert length/azimuth to cartesian
+       $x0 = sprintf("%d", (($sz * 0.5) - ($r * cos($angle))));
+       $y0 = sprintf("%d", (($sz * 0.5) - ($r * sin($angle))));
+       $x1 = sprintf("%d", (($sz * 0.5) + ($r * cos($angle))));
+       $y1 = sprintf("%d", (($sz * 0.5) + ($r * sin($angle))));
+
+       return array($x0, $y0, $x1, $y1);
+}
+
+function azel2xy($az, $el, $sz){
+       global $swap_ew;
+       #rotate coords... 90deg W = 180deg trig
+       $az += 270;
+
+       #turn into radians
+       $az = deg2rad($az);
+
+       # determine length of radius
+       $r = $sz * 0.5 * 0.95;
+       $r -= ($r * ($el/90));
+
+       # and convert length/azimuth to cartesian
+       $x = sprintf("%d", (($sz * 0.5) + ($r * cos($az))));
+       $y = sprintf("%d", (($sz * 0.5) + ($r * sin($az))));
+       if ($swap_ew == 0)
+               $x = $sz - $x;
+
+       return array($x, $y);
+}
+
+function splot($im, $sz, $C, $e){
+       if ((0 == $e['PRN']) || (0 == $e['az'] + $e['el'] + $e['ss']) ||
+           ($e['az'] < 0) || ($e['el'] < 0))
+               return;
+
+       $color = $C['brightgreen'];
+       if ($e['ss'] < 40)
+               $color = $C['darkgreen'];
+       if ($e['ss'] < 35)
+               $color = $C['yellow'];
+       if ($e['ss'] < 30)
+               $color = $C['red'];
+       if ($e['el']<10)
+               $color = $C['blue'];
+       if ($e['ss'] < 10)
+               $color = $C['black'];
+
+       list($x, $y) = azel2xy($e['az'], $e['el'], $sz);
+
+       $r = 12;
+       if (isset($_GET['sz']) && ($_GET['sz'] == 'small'))
+               $r = 8;
+
+       imageString($im, 3, $x+4, $y+4, $e['PRN'], $C['black']);
+        if ($e['used'] == true)
+               if (isGPS($e['PRN']))
+                       imageFilledArc($im, $x, $y, $r, $r, 0, 360, $color, 0);
+               else
+                       imageFilledDiamond($im, $x, $y, $r, $color);
+       else
+               if (isGPS($e['PRN']))
+                       imageArc($im, $x, $y, $r, $r, 0, 360, $color);
+               else
+                       imageDiamond($im, $x, $y, $r, $color);
+}
+
+
+function imageDiamond($im, $x, $y, $r, $color){
+       $t = $r/2;
+       # this lunacy is because imagesetthickness doesn't seem to work
+       $vx = array ( $x+$t, $y, $x, $y+$t, $x-$t, $y, $x, $y-$t );
+       imagepolygon($im, $vx, 4, $color);
+       $t--;
+       $vx = array ( $x+$t, $y, $x, $y+$t, $x-$t, $y, $x, $y-$t );
+       imagepolygon($im, $vx, 4, $color);
+       $t--;
+       $vx = array ( $x+$t, $y, $x, $y+$t, $x-$t, $y, $x, $y-$t );
+       imagepolygon($im, $vx, 4, $color);
+}
+
+function imageFilledDiamond($im, $x, $y, $r, $color){
+       $t = $r/2;
+       while($t){
+               $vx = array ( $x+$t, $y, $x, $y+$t, $x-$t, $y, $x, $y-$t );
+               imagepolygon($im, $vx, 4, $color);
+               $t -= 0.5;
+       }
+}
+
+function elevation($im, $sz, $C, $a){
+       $b = 90 - $a;
+       $a = $sz * 0.95 * ($a/180);
+       imageArc($im, $sz/2, $sz/2, $a*2, $a*2, 0, 360, $C['ltgray']);
+       $x = $sz/2 - 16;
+       $y = $sz/2 - $a;
+       imageString($im, 2, $x, $y, $b, $C['ltgray']);
+}
+
+function skyview($im, $sz, $C){
+       global $swap_ew;
+       $a = 90; $a = $sz * 0.95 * ($a/180);
+       imageFilledArc($im, $sz/2, $sz/2, $a*2, $a*2, 0, 360, $C['mdgray'], 0);
+       imageArc($im, $sz/2, $sz/2, $a*2, $a*2, 0, 360, $C['black']);
+       $x = $sz/2 - 16; $y = $sz/2 - $a;
+       imageString($im, 2, $x, $y, "0", $C['ltgray']);
+
+       $a = 85; $a = $sz * 0.95 * ($a/180);
+       imageFilledArc($im, $sz/2, $sz/2, $a*2, $a*2, 0, 360, $C['white'], 0);
+       imageArc($im, $sz/2, $sz/2, $a*2, $a*2, 0, 360, $C['ltgray']);
+       imageString($im, 1, $sz/2 - 6, $sz+$a, '5', $C['black']);
+       $x = $sz/2 - 16; $y = $sz/2 - $a;
+       imageString($im, 2, $x, $y, "5", $C['ltgray']);
+
+       for($i = 0; $i < 180; $i += 15){
+               list($x0, $y0, $x1, $y1) = radial($i, $sz);
+               imageLine($im, $x0, $y0, $x1, $y1, $C['ltgray']);
+       }
+
+       for($i = 15; $i < 90; $i += 15)
+               elevation($im, $sz, $C, $i);
+
+       $x = $sz/2 - 16; $y = $sz/2 - 8;
+       /* imageString($im, 2, $x, $y, "90", $C['ltgray']); */
+
+       imageString($im, 4, $sz/2 + 4, 2        , 'N', $C['black']);
+       imageString($im, 4, $sz/2 + 4, $sz - 16 , 'S', $C['black']);
+       if ($swap_ew == 0){
+               imageString($im, 4, 4        , $sz/2 + 4, 'E', $C['black']);
+               imageString($im, 4, $sz - 10 , $sz/2 + 4, 'W', $C['black']);
+       } else {
+               imageString($im, 4, 4        , $sz/2 + 4, 'W', $C['black']);
+               imageString($im, 4, $sz - 10 , $sz/2 + 4, 'E', $C['black']);
+       }
+}
+
+function gen_image($resp){
+       $sz = 600;
+       if (isset($_GET['sz']) && ($_GET['sz'] == 'small'))
+               $sz = 240;
+
+       $GPS = json_decode($resp, true);
+       if ($GPS['class'] != "POLL"){
+               die("json_decode error: $resp");
+       }
+
+       $im = imageCreate($sz, $sz);
+       $C = colorsetup($im);
+       skyview($im, $sz, $C);
+       if ($sz > 240)
+               legend($im, $sz, $C);
+
+       for($i = 0; $i < count($GPS['sky'][0]['satellites']); $i++){
+               splot($im, $sz, $C, $GPS['sky'][0]['satellites'][$i]);
+       }
+
+       header("Content-type: image/png");
+       imagePNG($im);
+       imageDestroy($im);
+}
+
+function dfix($x, $y, $z){
+       if ($x < 0){
+               $x = sprintf("%f %s", -1 * $x, $z);
+       } else {
+               $x = sprintf("%f %s", $x, $y);
+       }
+       return $x;
+}
+
+function write_html($resp){
+       global $sock, $errstr, $errno, $server, $port, $head, $body, $open;
+       global $blurb, $title, $autorefresh, $showmap, $gmap_key, $footer;
+       global $testmode, $advertise;
+
+       $GPS = json_decode($resp, true);
+       if ($GPS['class'] != 'POLL'){
+               die("json_decode error: $resp");
+       }
+
+       header("Content-type: text/html; charset=UTF-8");
+
+       global $lat, $lon;
+       $lat = (float)$GPS['tpv'][0]['lat'];
+       $lon = (float)$GPS['tpv'][0]['lon'];
+       $x = $server; $y = $port;
+       $imgdata = base64_encode($resp);
+       $server = $x; $port = $y;
+
+       if ($autorefresh > 0)
+               $autorefresh = "<meta http-equiv='Refresh' content='$autorefresh'/>";
+       else
+               $autorefresh = '';
+
+       $map_head = $map_body = $map_code = '';
+       if ($showmap == 1) {
+               $map_head = gen_gmap_head();
+               $map_body = 'onload="Load()" onunload="GUnload()"';
+               $map_code = gen_map_code();
+       } else if ($showmap == 2) {
+               $map_head = gen_osm_head();
+               $map_body = 'onload="Load()"';
+               $map_code = gen_map_code();
+       }
+       $part1 = <<<EOF
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+{$head}
+{$map_head}
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+<meta http-equiv="Content-Language" content="en,en-us"/>
+<title>{$title} - GPSD Test Station {$lat}, {$lon}</title>
+{$autorefresh}
+<style>
+.warning {
+    color: #FF0000;
+}
+
+.fixed {
+    font-family: mono-space;
+}
+
+.caption {
+    text-align: left;
+    margin: 1ex 3em 1ex 3em; /* top right bottom left */
+}
+
+.administrivia {
+    font-size: small;
+    font-family: verdana, sans-serif;
+}
+</style>
+</head>
+
+<body {$body} {$map_body}>
+<center>
+<table border="0">
+<tr><td align="justify">
+{$blurb}
+</td>
+EOF;
+
+       if (!strlen($advertise))
+               $advertise = $server;
+
+       if ($testmode && !$sock)
+               $part2 = "";
+       else
+               $part2 = <<<EOF
+<!-- ------------------------------------------------------------ -->
+
+<td rowspan="4" align="center" valign="top">
+<img src="?op=view&amp;imgdata={$imgdata}"
+width="600" height="600"/>
+<br clear="all"/>
+<p class="caption">A filled circle means the satellite was used in
+the last fix. Green-yellow-red colors indicate signal strength in dB,
+green=most and red=least.  Diamonds indicate Augmentation satellites.</p>
+{$map_code}</td>
+</tr>
+EOF;
+
+       if (!$open)
+               $part3 = '';
+       else
+               $part3 = <<<EOF
+<!-- ------------------------------------------------------------ -->
+
+<tr><td align="justify">To get real-time information, connect to
+<span class="fixed">telnet://{$advertise}:{$port}/</span> and type "?POLL;"
+or "?WATCH={"enable":true,"raw":true}".<br/>
+Use a different server:<br/>
+<form method=GET action="${_SERVER['SCRIPT_NAME']}">
+<input name="host" value="{$advertise}">:
+<input name="port" value="{$port}" size="5" maxlength="5">
+<input type=submit value="Get Position"><input type=reset></form>
+<br/>
+</td>
+</tr>
+EOF;
+
+       if ($testmode && !$sock)
+               $part4 = "<tr><td class='warning'>The gpsd instance that this page monitors is not running.</td></tr>";
+       else {
+               $fix = $GPS['tpv'][0];
+               $sky = $GPS['sky'][0];
+               $sats = $sky['satellites'];
+
+               $nsv = count($sats);
+                $ts = $fix['time'];
+                $sat = '';
+                foreach($sats as $s)
+                        $sat .= sprintf(
+                                "\t<tr><td>%d</td><td>%d</td><td>%d</td><td>%d</td><td>%s</td></tr>\n",
+                                $s['PRN'], $s['el'], $s['az'], $s['ss'], $s['used'] ? 'Y' : 'N'
+                        );
+               $part4 = <<<EOF
+<!-- ------------------------------------------------------------ -->
+<tr><td align=center valign=top>
+    <table border=1>
+        <tr><th colspan=2 align=center>Current Information</th></tr>
+        <tr><td>Time (UTC)</td><td>{$ts}</td></tr>
+        <tr><td>Latitude</td><td>{$fix['lat']}</td></tr>
+        <tr><td>Longitude</td><td>{$fix['lon']}</td></tr>
+        <tr><td>Altitude</td><td>{$fix['alt']}</td></tr>
+        <tr><td>Fix Type</td><td>{$fix['mode']}</td></tr>
+        <tr><td>Satellites</td><td>{$nsv}</td></tr>
+        <tr><td>HDOP</td><td>{$sky['hdop']}</td></tr>
+        <tr><td>VDOP</td><td>{$sky['vdop']}</td></tr>
+    </table>
+    <br/>
+    <table border=1>
+        <tr><th colspan=5 align=center>Current Satellites</th></tr>
+        <tr><th>PRN</th><th>Elevation</th><th>Azimuth</th><th>SS</th><th>Used</th></tr>
+$sat    </table>
+</td></tr>
+
+<!-- raw response:
+{$resp}
+-->
+EOF;
+       }
+
+       $part5 = <<<EOF
+
+</table>
+</center>
+{$footer}
+<hr/>
+<p class="administrivia">This script is distributed by the
+<a href="@WEBSITE@">GPSD project</a>.</p>
+
+</body>
+</html>
+EOF;
+
+print $part1 . $part2 . $part3 . $part4 . $part5;
+
+}
+
+function write_json($resp){
+       header('Content-Type: text/javascript');
+       if (isset($_GET['jsonp']))
+               print "{$_GET['jsonp']}({$resp})";
+       else
+               print $resp;
+}
+
+function write_config(){
+       $f = fopen("gpsd_config.inc", "a");
+       if (!$f)
+               die("can't generate prototype config file. try running this script as root in DOCUMENT_ROOT");
+
+       $buf = <<<EOB
+<?PHP
+\$title = 'My GPS Server';
+\$server = 'localhost';
+#\$advertise = 'localhost';
+\$port = 2947;
+\$autorefresh = 0; # number of seconds after which to refresh
+\$showmap = 0; # set to 1 if you want to have a google map, set it to 2 if you want a map based on openstreetmap
+\$gmap_key = 'GetYourOwnGoogleKey'; # your google API key goes here
+\$swap_ew = 0; # set to 1 if you don't understand projections
+\$open = 0; # set to 1 to show the form to change the GPSd server
+
+## You can read the header, footer and blurb from a file...
+# \$head = file_get_contents('/path/to/header.inc');
+# \$body = file_get_contents('/path/to/body.inc');
+# \$footer = file_get_contents('/path/to/footer.hinc');
+# \$blurb = file_get_contents('/path/to/blurb.inc');
+
+## ... or you can just define them here
+\$head = '';
+\$body = '';
+\$footer = '';
+\$blurb = <<<EOT
+This is a
+<a href="@WEBSITE@">gpsd</a>
+server <blink><font color="red">located someplace</font></blink>.
+
+The hardware is a
+<blink><font color="red">hardware description and link</font></blink>.
+
+This machine is maintained by
+<a href="mailto:you@example.com">Your Name Goes Here</a>.<br/>
+EOT;
+
+?>
+
+EOB;
+       fwrite($f, $buf);
+       fclose($f);
+}
+
+function gen_gmap_head() {
+global $gmap_key;
+return <<<EOT
+<script src="//maps.googleapis.com/maps/api/js?sensor=false"
+        type="text/javascript">
+</script>
+
+<script type="text/javascript">
+ <!--
+    function Load() {
+      var map = new google.maps.Map(
+        document.getElementById('map'), {
+          center: new google.maps.LatLng({$GLOBALS['lat']}, {$GLOBALS['lon']}),
+          zoom: 13,
+          mapTypeId: google.maps.MapTypeId.ROADMAP
+      });
+
+      var marker = new google.maps.Marker({
+            position: new google.maps.LatLng({$GLOBALS['lat']}, {$GLOBALS['lon']}),
+            map: map
+      });
+
+    }
+    google.maps.event.addDomListener(window, 'load', initialize);
+
+ -->
+</script>
+EOT;
+}
+
+function gen_osm_head() {
+global $GPS;
+return <<<EOT
+<script src="http://openlayers.org/api/OpenLayers.js" type="text/javascript"></script>
+<script type="text/javascript">
+    <!--
+function Load() {
+       document.getElementById("map").firstChild.data = "";
+       var map = new OpenLayers.Map("map", {
+               controls: [
+                       new OpenLayers.Control.Navigation(),
+                       new OpenLayers.Control.PanZoomBar(),
+                       new OpenLayers.Control.ScaleLine(),
+                       new OpenLayers.Control.LayerSwitcher()
+               ]
+       });
+       var layer = new OpenLayers.Layer.OSM("Open Street Map");
+       map.addLayer(layer);
+
+       var center = new OpenLayers.LonLat({$GLOBALS['lon']}, {$GLOBALS['lat']})
+               .transform(new OpenLayers.Projection("EPSG:4326"), map.getProjectionObject());
+       map.setCenter(center, 12);
+
+       var markers = new OpenLayers.Layer.Markers("Markers");
+       markers.addMarker(new OpenLayers.Marker(center));
+       map.addLayer(markers);
+}
+    -->
+    </script>
+EOT;
+}
+
+function gen_map_code() {
+return <<<EOT
+<br/>
+<div id="map" style="width: 550px; height: 400px; border:1px; border-style: solid;">
+    Loading...
+    <noscript>
+        <span class='warning'>Sorry: you must enable javascript to view our maps.</span><br/>
+    </noscript>
+</div>
+
+EOT;
+}
+
+?>
diff --git a/php/gpsd/gpsd_config.inc b/php/gpsd/gpsd_config.inc
new file mode 100644 (file)
index 0000000..71f1e7e
--- /dev/null
@@ -0,0 +1,34 @@
+<?PHP
+$title = 'My GPS Server';
+$server = 'localhost';
+#$advertise = 'localhost';
+$port = 2947;
+$autorefresh = 60; # number of seconds after which to refresh
+$showmap = 2; # set to 1 if you want to have a google map, set it to 2 if you want a map based on openstreetmap
+$gmap_key = 'GetYourOwnGoogleKey'; # your google API key goes here
+$swap_ew = 0; # set to 1 if you don't understand projections
+$open = 0; # set to 1 to show the form to change the GPSd server
+
+## You can read the header, footer and blurb from a file...
+# $head = file_get_contents('/path/to/header.inc');
+# $body = file_get_contents('/path/to/body.inc');
+# $footer = file_get_contents('/path/to/footer.hinc');
+# $blurb = file_get_contents('/path/to/blurb.inc');
+
+## ... or you can just define them here
+$head = '';
+$body = '';
+$footer = '';
+$blurb = <<<EOT
+This is a
+<a href="@WEBSITE@">gpsd</a>
+server <blink><font color="red">located someplace</font></blink>.
+
+The hardware is a
+<blink><font color="red">hardware description and link</font></blink>.
+
+This machine is maintained by
+<a href="mailto:you@example.com">Your Name Goes Here</a>.<br/>
+EOT;
+
+?>
diff --git a/php/index.php b/php/index.php
new file mode 100644 (file)
index 0000000..396204f
--- /dev/null
@@ -0,0 +1,257 @@
+<?php
+
+header("Cache-Control: no-cache, no-store, must-revalidate"); // HTTP 1.1.
+header("Pragma: no-cache"); // HTTP 1.0.
+header("Expires: 0"); // Proxies.
+
+?>
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title></title>
+<meta name="description" content="">
+<meta name="author" content="">
+<style type="text/css">
+.form-container {
+   border: 1px solid #f2e3d2;
+   background: #c9b7a2;
+   background: -webkit-gradient(linear, left top, left bottom, from(#f2e3d2), to(#c9b7a2));
+   background: -webkit-linear-gradient(top, #f2e3d2, #c9b7a2);
+   background: -moz-linear-gradient(top, #f2e3d2, #c9b7a2);
+   background: -ms-linear-gradient(top, #f2e3d2, #c9b7a2);
+   background: -o-linear-gradient(top, #f2e3d2, #c9b7a2);
+   background-image: -ms-linear-gradient(top, #f2e3d2 0%, #c9b7a2 100%);
+   -webkit-border-radius: 8px;
+   -moz-border-radius: 8px;
+   border-radius: 8px;
+   -webkit-box-shadow: rgba(000,000,000,0.9) 0 1px 2px, inset rgba(255,255,255,0.4) 0 0px 0;
+   -moz-box-shadow: rgba(000,000,000,0.9) 0 1px 2px, inset rgba(255,255,255,0.4) 0 0px 0;
+   box-shadow: rgba(000,000,000,0.9) 0 1px 2px, inset rgba(255,255,255,0.4) 0 0px 0;
+   font-family: 'Helvetica Neue',Helvetica,sans-serif;
+   text-decoration: none;
+   vertical-align: middle;
+   min-width:500px;
+   padding:20px;
+   width:500px;
+   margin: 0 auto;
+   }
+.form-field {
+   border: 1px solid #c9b7a2;
+   background: #e4d5c3;
+   -webkit-border-radius: 4px;
+   -moz-border-radius: 4px;
+   border-radius: 4px;
+   color: #c9b7a2;
+   -webkit-box-shadow: rgba(255,255,255,0.4) 0 1px 0, inset rgba(000,000,000,0.7) 0 0px 0px;
+   -moz-box-shadow: rgba(255,255,255,0.4) 0 1px 0, inset rgba(000,000,000,0.7) 0 0px 0px;
+   box-shadow: rgba(255,255,255,0.4) 0 1px 0, inset rgba(000,000,000,0.7) 0 0px 0px;
+   padding:8px;
+   margin-bottom:20px;
+   width:280px;
+   }
+.form-field:focus {
+   background: #fff;
+   color: #725129;
+   }
+.form-container h2 {
+   text-shadow: #fdf2e4 0 1px 0;
+   font-size:24px;
+   margin: 0 0 10px 0;
+   font-weight:bold;
+   text-align:center;
+    }
+.form-container h3 {
+   text-shadow: #fdf2e4 0 1px 0;
+   font-size:18px;
+   margin: 0 0 10px 0;
+   text-align:center;
+    }
+.form-title {
+   margin-bottom:10px;
+   color: #725129;
+   text-shadow: #fdf2e4 0 1px 0;
+   }
+.submit-container {
+   margin:8px 0;
+   text-align:right;
+   }
+.submit-button {
+   border: 1px solid #447314;
+   background: #6aa436;
+   background: -webkit-gradient(linear, left top, left bottom, from(#8dc059), to(#6aa436));
+   background: -webkit-linear-gradient(top, #8dc059, #6aa436);
+   background: -moz-linear-gradient(top, #8dc059, #6aa436);
+   background: -ms-linear-gradient(top, #8dc059, #6aa436);
+   background: -o-linear-gradient(top, #8dc059, #6aa436);
+   background-image: -ms-linear-gradient(top, #8dc059 0%, #6aa436 100%);
+   -webkit-border-radius: 4px;
+   -moz-border-radius: 4px;
+   border-radius: 4px;
+   -webkit-box-shadow: rgba(255,255,255,0.4) 0 1px 0, inset rgba(255,255,255,0.4) 0 1px 0;
+   -moz-box-shadow: rgba(255,255,255,0.4) 0 1px 0, inset rgba(255,255,255,0.4) 0 1px 0;
+   box-shadow: rgba(255,255,255,0.4) 0 1px 0, inset rgba(255,255,255,0.4) 0 1px 0;
+   text-shadow: #addc7e 0 1px 0;
+   color: #31540c;
+   font-family: helvetica, serif;
+   padding: 8.5px 18px;
+   font-size: 14px;
+   text-decoration: none;
+   vertical-align: middle;
+   }
+.submit-button:hover {
+   border: 1px solid #447314;
+   text-shadow: #31540c 0 1px 0;
+   background: #6aa436;
+   background: -webkit-gradient(linear, left top, left bottom, from(#8dc059), to(#6aa436));
+   background: -webkit-linear-gradient(top, #8dc059, #6aa436);
+   background: -moz-linear-gradient(top, #8dc059, #6aa436);
+   background: -ms-linear-gradient(top, #8dc059, #6aa436);
+   background: -o-linear-gradient(top, #8dc059, #6aa436);
+   background-image: -ms-linear-gradient(top, #8dc059 0%, #6aa436 100%);
+   color: #fff;
+   }
+.submit-button:active {
+   text-shadow: #31540c 0 1px 0;
+   border: 1px solid #447314;
+   background: #8dc059;
+   background: -webkit-gradient(linear, left top, left bottom, from(#6aa436), to(#6aa436));
+   background: -webkit-linear-gradient(top, #6aa436, #8dc059);
+   background: -moz-linear-gradient(top, #6aa436, #8dc059);
+   background: -ms-linear-gradient(top, #6aa436, #8dc059);
+   background: -o-linear-gradient(top, #6aa436, #8dc059);
+   background-image: -ms-linear-gradient(top, #6aa436 0%, #8dc059 100%);
+   color: #fff;
+   }
+
+html {
+    height: 100%;
+}
+body {
+    height: 100%;
+    margin: 0;
+    background-repeat: no-repeat;
+    background-attachment: fixed;
+    background: rgb(167,199,220); /* Old browsers */
+    background: -moz-radial-gradient(center, ellipse cover,  rgba(167,199,220,1) 0%, rgba(133,178,211,1) 100%); /* FF3.6-15 */
+    background: -webkit-radial-gradient(center, ellipse cover,  rgba(167,199,220,1) 0%,rgba(133,178,211,1) 100%); /* Chrome10-25,Safari5.1-6 */
+    background: radial-gradient(ellipse at center,  rgba(167,199,220,1) 0%,rgba(133,178,211,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
+    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#a7c7dc', endColorstr='#85b2d3',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
+}
+
+</style>
+<script type="text/javascript">
+get_time();
+var utcSeconds = 0;
+var date = new Date(0);
+date.setUTCSeconds(utcSeconds);
+var ntpcounter=0;
+
+function loadpage(page_request){
+  if (page_request.readyState == 4 && (page_request.status==200 || window.location.href.indexOf("http")==-1)) {
+    utcSeconds = parseInt(page_request.responseText,10);
+    var tmpdate = new Date(0);
+    tmpdate.setUTCSeconds(utcSeconds);
+    date = tmpdate;
+    //document.getElementById('times').innerHTML=utcSeconds;
+  }
+}
+
+function loadpagepps(page_request){
+  if (page_request.readyState == 4 && (page_request.status==200 || window.location.href.indexOf("http")==-1)) {
+    ppsstatus = page_request.responseText;
+    document.getElementById('ppsstatus').innerHTML='PPS STATUS: '+ppsstatus;
+  }
+}
+
+function get_time() {
+  var page_request = false
+  if (window.XMLHttpRequest) // if Mozilla, Safari etc
+    page_request = new XMLHttpRequest()
+  else if (window.ActiveXObject){ // if IE
+    try {
+      page_request = new ActiveXObject("Msxml2.XMLHTTP")
+    } 
+    catch (e){
+      try{
+        page_request = new ActiveXObject("Microsoft.XMLHTTP")
+      }
+    catch (e){}
+    }
+  }
+  else
+    return false
+  page_request.onreadystatechange=function(){
+    loadpage(page_request);
+  }
+  page_request.open('GET', '/time-ajax.php', true)
+  page_request.send(null)
+}
+
+function update_time() {
+  ntpcounter=ntpcounter+1;
+  date.setSeconds(date.getSeconds() + 1);
+  document.getElementById("updatetime").innerHTML = (date);
+  if (ntpcounter>3600) {
+    ntpcounter=0;
+    get_time();
+  }
+  setTimeout("update_time()", 1000);
+}
+
+function update_pps() {
+    var page_request = false
+  if (window.XMLHttpRequest) // if Mozilla, Safari etc
+    page_request = new XMLHttpRequest()
+  else if (window.ActiveXObject){ // if IE
+    try {
+      page_request = new ActiveXObject("Msxml2.XMLHTTP")
+    }
+    catch (e){
+      try{
+        page_request = new ActiveXObject("Microsoft.XMLHTTP")
+      }
+    catch (e){}
+    }
+  }
+  else
+    return false
+  page_request.onreadystatechange=function(){
+    loadpagepps(page_request);
+  }
+  page_request.open('GET', '/ppsstatus.php', true)
+  page_request.send(null)
+  setTimeout("update_pps()", 5000);
+}
+
+function update_satellite() {
+  var satellite = document.getElementById('satellite');
+  satellite.src = "http://ntp.dyn.spangdorfia.com/gpsd/gpsd.php?op=view&"+ new Date().getTime();
+  setTimeout("update_satellite()", 3000);
+}
+
+</script>
+</head>
+<body onload="update_time(); update_satellite(); update_pps();">
+<br><br>
+<form class="form-container">
+<div class="form-title"><h2>Atomic Clock Status</h2></div>
+<h3><div class="form-title" id="updatetime"></div></h3>
+<h3><div class="form-title">
+<?php
+$contents = file_get_contents("http://localhost/gpsd/gpsd.php?op=json");
+$contents = utf8_encode($contents);
+$results = json_decode($contents,true); 
+echo "Total Satellites: " . count($results['sky'][0]['satellites']);
+?>
+</div></h3>
+<div><h3><img id="satellite" name="satellite" src="http://ntp.dyn.spangdorfia.com/gpsd/gpsd.php?op=view" width="50%" height="50%"></h3></div>
+
+<h3><div class="form-title" id="rbstatus">*Rb LOCK: YES</div></h3>
+<h3><div class="form-title" id="ppsstatus">PPS STATUS: 0</div></h3>
+<h3><div class="form-title" id="freqstatus">*CURRENT FREQ: 10MHz</div></h3>
+</div>
+</form>
+
+</body>
+</html>
diff --git a/php/ppsstatus.php b/php/ppsstatus.php
new file mode 100644 (file)
index 0000000..059b335
--- /dev/null
@@ -0,0 +1,3 @@
+<?php
+echo shell_exec("ntpq -c pe -n | grep PPS | awk '{ print $10 }'");
+?>
diff --git a/php/time-ajax.php b/php/time-ajax.php
new file mode 100644 (file)
index 0000000..3241bf9
--- /dev/null
@@ -0,0 +1,169 @@
+<?php
+session_start();
+$please_wait = '';
+$last = time();
+if(isset($_SESSION['last'])) {
+$last = $_SESSION['last'];
+}
+else {
+$_SESSION['last'] = $last;
+}
+//wrap the whole thing in a test for the last hit-time on the page, to avoid abusing NTP servers
+#if (time() - $last < 1) {
+#  $wait = time() - $last;
+#  $please_wait = 'Request limit exceeded, please wait ' . (15 - $wait) . ' s.';
+#  $server = $vn_response = $mode_response = $stratum_response = $remote_originate = $remote_received
+#  = $remote_received = $remote_transmitted = $delay_ms = $ntp_time_formatted = $server_time_formatted = $please_wait;
+#}
+#else {
+  $_SESSION['last'] = time();
+
+  $bit_max = 4294967296;
+  $epoch_convert = 2208988800;
+  $vn = 3;
+  
+  $servers = array('localhost');
+  $server_count = count($servers);
+  
+  //see rfc5905, page 20
+  //first byte
+  //LI (leap indicator), a 2-bit integer. 00 for 'no warning'
+  $header = '00';
+  //VN (version number), a 3-bit integer.  011 for version 3
+  $header .= sprintf('%03d',decbin($vn));
+  //Mode (association mode), a 3-bit integer. 011 for 'client'
+  $header .= '011';
+  
+  //echo bindec($header);    
+      
+  //construct the packet header, byte 1
+  $request_packet = chr(bindec($header));
+  
+  //we'll use a for loop to try additional servers should one fail to respond
+  $i = 0;
+  for($i; $i < $server_count; $i++) {
+    $socket = @fsockopen('udp://'.$servers[$i], 123, $err_no, $err_str,1);
+    if ($socket) {
+      
+      //add nulls to position 11 (the transmit timestamp, later to be returned as originate)
+      //10 lots of 32 bits
+      for ($j=1; $j<40; $j++) {
+        $request_packet .= chr(0x0);
+      }
+      
+      //the time our packet is sent from our server (returns a string in the form 'msec sec')
+      $local_sent_explode = explode(' ',microtime());
+      $local_sent = $local_sent_explode[1] + $local_sent_explode[0];
+      
+      //add 70 years to convert unix to ntp epoch
+      $originate_seconds = $local_sent_explode[1] + $epoch_convert;
+        
+      //convert the float given by microtime to a fraction of 32 bits
+      $originate_fractional = round($local_sent_explode[0] * $bit_max);
+        
+      //pad fractional seconds to 32-bit length
+      $originate_fractional = sprintf('%010d',$originate_fractional);
+        
+      //pack to big endian binary string
+      $packed_seconds = pack('N', $originate_seconds);
+      $packed_fractional = pack("N", $originate_fractional);
+  
+      //add the packed transmit timestamp
+      $request_packet .= $packed_seconds;
+      $request_packet .= $packed_fractional;
+      
+      if (fwrite($socket, $request_packet)) {
+        $data = NULL;
+        stream_set_timeout($socket, 1);
+        
+        $response = fread($socket, 48);
+        
+        //the time the response was received
+        $local_received = microtime(true);
+        
+        //echo 'response was: '.strlen($response).$response;
+      }
+      fclose($socket);
+      
+      if (strlen($response) == 48) {
+        //the response was of the right length, assume it's valid and break out of the loop
+        break;
+      }
+      else {
+        if ($i == $server_count-1) {
+          //this was the last server on the list, so give up
+          die('unable to establish a connection');
+        }
+      }
+    }
+    else {
+      if ($i == $server_count-1) {
+        //this was the last server on the list, so give up
+        die('unable to establish a connection');
+      }
+    }
+  }
+  
+  //unpack the response to unsiged lonng for calculations
+  $unpack0 = unpack("N12", $response);
+  //print_r($unpack0);
+  
+  //present as a decimal number
+  $remote_originate_seconds = sprintf('%u', $unpack0[7])-$epoch_convert;
+  $remote_received_seconds = sprintf('%u', $unpack0[9])-$epoch_convert;
+  $remote_transmitted_seconds = sprintf('%u', $unpack0[11])-$epoch_convert;
+  
+  $remote_originate_fraction = sprintf('%u', $unpack0[8]) / $bit_max;
+  $remote_received_fraction = sprintf('%u', $unpack0[10]) / $bit_max;
+  $remote_transmitted_fraction = sprintf('%u', $unpack0[12]) / $bit_max;
+  
+  $remote_originate = $remote_originate_seconds + $remote_originate_fraction;
+  $remote_received = $remote_received_seconds + $remote_received_fraction;
+  $remote_transmitted = $remote_transmitted_seconds + $remote_transmitted_fraction;
+  
+  //unpack to ascii characters for the header response
+  $unpack1 = unpack("C12", $response);
+  //print_r($unpack1);
+  
+  //echo 'byte 1: ' . $unpack1[1] . ' | ';
+  
+  //the header response in binary (base 2)
+  $header_response =  base_convert($unpack1[1], 10, 2);
+  
+  //pad with zeros to 1 byte (8 bits)
+  $header_response = sprintf('%08d',$header_response);
+  
+  //Mode (the last 3 bits of the first byte), converting to decimal for humans;
+  $mode_response = bindec(substr($header_response, -3));
+  
+  //VN
+  $vn_response = bindec(substr($header_response, -6, 3));
+  
+  //the header stratum response in binary (base 2)
+  $stratum_response =  base_convert($unpack1[2], 10, 2);
+  $stratum_response = bindec($stratum_response);
+  //echo 'stratum: ' . bindec($stratum_response);
+  
+  //calculations assume a symmetrical delay, fixed point would give more accuracy
+  $delay = (($local_received - $local_sent) / 2)  - ($remote_transmitted - $remote_received);
+  $delay_ms = round($delay * 1000) . ' ms';
+  //echo 'delay: ' . $delay * 1000 . 'ms';
+  
+  $server = $servers[$i];
+  
+  $ntp_time =  $remote_transmitted - $delay;
+  $ntp_time_explode = explode('.',$ntp_time);
+  
+  $ntp_time_formatted = date('Y-m-d H:i:s', $ntp_time_explode[0]).'.'.$ntp_time_explode[1];
+  
+  //compare with the current server time
+  $server_time =  microtime();
+  $server_time_explode = explode(' ', $server_time);
+  $server_time_micro = round($server_time_explode[0],4);
+  
+  $server_time_formatted = date('Y-m-d H:i:s', time()) .'.'. substr($server_time_micro,2);
+
+#}
+?>
+
+<?php echo $remote_received;?>
diff --git a/php/time1.php b/php/time1.php
new file mode 100644 (file)
index 0000000..f4bd546
--- /dev/null
@@ -0,0 +1,199 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title></title>
+<meta name="description" content="">
+<meta name="author" content="">
+<style type="text/css">
+.form-container {
+   border: 1px solid #f2e3d2;
+   background: #c9b7a2;
+   background: -webkit-gradient(linear, left top, left bottom, from(#f2e3d2), to(#c9b7a2));
+   background: -webkit-linear-gradient(top, #f2e3d2, #c9b7a2);
+   background: -moz-linear-gradient(top, #f2e3d2, #c9b7a2);
+   background: -ms-linear-gradient(top, #f2e3d2, #c9b7a2);
+   background: -o-linear-gradient(top, #f2e3d2, #c9b7a2);
+   background-image: -ms-linear-gradient(top, #f2e3d2 0%, #c9b7a2 100%);
+   -webkit-border-radius: 8px;
+   -moz-border-radius: 8px;
+   border-radius: 8px;
+   -webkit-box-shadow: rgba(000,000,000,0.9) 0 1px 2px, inset rgba(255,255,255,0.4) 0 0px 0;
+   -moz-box-shadow: rgba(000,000,000,0.9) 0 1px 2px, inset rgba(255,255,255,0.4) 0 0px 0;
+   box-shadow: rgba(000,000,000,0.9) 0 1px 2px, inset rgba(255,255,255,0.4) 0 0px 0;
+   font-family: 'Helvetica Neue',Helvetica,sans-serif;
+   text-decoration: none;
+   vertical-align: middle;
+   min-width:500px;
+   padding:20px;
+   width:500px;
+   margin: 0 auto;
+   }
+.form-field {
+   border: 1px solid #c9b7a2;
+   background: #e4d5c3;
+   -webkit-border-radius: 4px;
+   -moz-border-radius: 4px;
+   border-radius: 4px;
+   color: #c9b7a2;
+   -webkit-box-shadow: rgba(255,255,255,0.4) 0 1px 0, inset rgba(000,000,000,0.7) 0 0px 0px;
+   -moz-box-shadow: rgba(255,255,255,0.4) 0 1px 0, inset rgba(000,000,000,0.7) 0 0px 0px;
+   box-shadow: rgba(255,255,255,0.4) 0 1px 0, inset rgba(000,000,000,0.7) 0 0px 0px;
+   padding:8px;
+   margin-bottom:20px;
+   width:280px;
+   }
+.form-field:focus {
+   background: #fff;
+   color: #725129;
+   }
+.form-container h2 {
+   text-shadow: #fdf2e4 0 1px 0;
+   font-size:24px;
+   margin: 0 0 10px 0;
+   font-weight:bold;
+   text-align:center;
+    }
+.form-container h3 {
+   text-shadow: #fdf2e4 0 1px 0;
+   font-size:18px;
+   margin: 0 0 10px 0;
+   text-align:center;
+    }
+.form-title {
+   margin-bottom:10px;
+   color: #725129;
+   text-shadow: #fdf2e4 0 1px 0;
+   }
+.submit-container {
+   margin:8px 0;
+   text-align:right;
+   }
+.submit-button {
+   border: 1px solid #447314;
+   background: #6aa436;
+   background: -webkit-gradient(linear, left top, left bottom, from(#8dc059), to(#6aa436));
+   background: -webkit-linear-gradient(top, #8dc059, #6aa436);
+   background: -moz-linear-gradient(top, #8dc059, #6aa436);
+   background: -ms-linear-gradient(top, #8dc059, #6aa436);
+   background: -o-linear-gradient(top, #8dc059, #6aa436);
+   background-image: -ms-linear-gradient(top, #8dc059 0%, #6aa436 100%);
+   -webkit-border-radius: 4px;
+   -moz-border-radius: 4px;
+   border-radius: 4px;
+   -webkit-box-shadow: rgba(255,255,255,0.4) 0 1px 0, inset rgba(255,255,255,0.4) 0 1px 0;
+   -moz-box-shadow: rgba(255,255,255,0.4) 0 1px 0, inset rgba(255,255,255,0.4) 0 1px 0;
+   box-shadow: rgba(255,255,255,0.4) 0 1px 0, inset rgba(255,255,255,0.4) 0 1px 0;
+   text-shadow: #addc7e 0 1px 0;
+   color: #31540c;
+   font-family: helvetica, serif;
+   padding: 8.5px 18px;
+   font-size: 14px;
+   text-decoration: none;
+   vertical-align: middle;
+   }
+.submit-button:hover {
+   border: 1px solid #447314;
+   text-shadow: #31540c 0 1px 0;
+   background: #6aa436;
+   background: -webkit-gradient(linear, left top, left bottom, from(#8dc059), to(#6aa436));
+   background: -webkit-linear-gradient(top, #8dc059, #6aa436);
+   background: -moz-linear-gradient(top, #8dc059, #6aa436);
+   background: -ms-linear-gradient(top, #8dc059, #6aa436);
+   background: -o-linear-gradient(top, #8dc059, #6aa436);
+   background-image: -ms-linear-gradient(top, #8dc059 0%, #6aa436 100%);
+   color: #fff;
+   }
+.submit-button:active {
+   text-shadow: #31540c 0 1px 0;
+   border: 1px solid #447314;
+   background: #8dc059;
+   background: -webkit-gradient(linear, left top, left bottom, from(#6aa436), to(#6aa436));
+   background: -webkit-linear-gradient(top, #6aa436, #8dc059);
+   background: -moz-linear-gradient(top, #6aa436, #8dc059);
+   background: -ms-linear-gradient(top, #6aa436, #8dc059);
+   background: -o-linear-gradient(top, #6aa436, #8dc059);
+   background-image: -ms-linear-gradient(top, #6aa436 0%, #8dc059 100%);
+   color: #fff;
+   }
+
+html {
+    height: 100%;
+}
+body {
+    height: 100%;
+    margin: 0;
+    background-repeat: no-repeat;
+    background-attachment: fixed;
+    background: rgb(167,199,220); /* Old browsers */
+    background: -moz-radial-gradient(center, ellipse cover,  rgba(167,199,220,1) 0%, rgba(133,178,211,1) 100%); /* FF3.6-15 */
+    background: -webkit-radial-gradient(center, ellipse cover,  rgba(167,199,220,1) 0%,rgba(133,178,211,1) 100%); /* Chrome10-25,Safari5.1-6 */
+    background: radial-gradient(ellipse at center,  rgba(167,199,220,1) 0%,rgba(133,178,211,1) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
+    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#a7c7dc', endColorstr='#85b2d3',GradientType=1 ); /* IE6-9 fallback on horizontal gradient */
+}
+
+</style>
+<script type="text/javascript">
+get_time();
+var utcSeconds = 0;
+var date = new Date(0);
+date.setUTCSeconds(utcSeconds);
+var ntpcounter=0;
+
+function loadpage(page_request){
+  if (page_request.readyState == 4 && (page_request.status==200 || window.location.href.indexOf("http")==-1)) {
+    utcSeconds = parseInt(page_request.responseText,10);
+    var tmpdate = new Date(0);
+    tmpdate.setUTCSeconds(utcSeconds);
+    date = tmpdate;
+    //document.getElementById('times').innerHTML=utcSeconds;
+  }
+}
+
+function get_time() {
+  var page_request = false
+  if (window.XMLHttpRequest) // if Mozilla, Safari etc
+    page_request = new XMLHttpRequest()
+  else if (window.ActiveXObject){ // if IE
+    try {
+      page_request = new ActiveXObject("Msxml2.XMLHTTP")
+    } 
+    catch (e){
+      try{
+        page_request = new ActiveXObject("Microsoft.XMLHTTP")
+      }
+    catch (e){}
+    }
+  }
+  else
+    return false
+  page_request.onreadystatechange=function(){
+    loadpage(page_request);
+  }
+  page_request.open('GET', '/time-ajax.php', true)
+  page_request.send(null)
+}
+
+function update_time() {
+  ntpcounter=ntpcounter+1;
+  date.setSeconds(date.getSeconds() + 1);
+  document.getElementById("updatetime").innerHTML = (date);
+  if (ntpcounter>3600) {
+    ntpcounter=0;
+    get_time();
+  }
+  setTimeout("update_time()", 1000);
+}
+
+</script>
+</head>
+<body onload="update_time();">
+<br><br>
+<form class="form-container">
+<div class="form-title"><h2>Atomic Clock Status</h2></div>
+<h3><div class="form-title" id="updatetime"></div></h3>
+</div>
+</form>
+
+</body>
+</html>
diff --git a/php/time2.php b/php/time2.php
new file mode 100644 (file)
index 0000000..db82a74
--- /dev/null
@@ -0,0 +1,241 @@
+<?php
+session_start();
+$please_wait = '';
+$last = time();
+if(isset($_SESSION['last'])) {
+$last = $_SESSION['last'];
+}
+else {
+$_SESSION['last'] = $last;
+}
+//wrap the whole thing in a test for the last hit-time on the page, to avoid abusing NTP servers
+if (time() - $last < 1) {
+  $wait = time() - $last;
+  $please_wait = 'Request limit exceeded, please wait ' . (15 - $wait) . ' s.';
+  $server = $vn_response = $mode_response = $stratum_response = $remote_originate = $remote_received
+  = $remote_received = $remote_transmitted = $delay_ms = $ntp_time_formatted = $server_time_formatted = $please_wait;
+}
+else {
+  $_SESSION['last'] = time();
+
+  $bit_max = 4294967296;
+  $epoch_convert = 2208988800;
+  $vn = 3;
+  
+  $servers = array('localhost');
+  $server_count = count($servers);
+  
+  //see rfc5905, page 20
+  //first byte
+  //LI (leap indicator), a 2-bit integer. 00 for 'no warning'
+  $header = '00';
+  //VN (version number), a 3-bit integer.  011 for version 3
+  $header .= sprintf('%03d',decbin($vn));
+  //Mode (association mode), a 3-bit integer. 011 for 'client'
+  $header .= '011';
+  
+  //echo bindec($header);    
+      
+  //construct the packet header, byte 1
+  $request_packet = chr(bindec($header));
+  
+  //we'll use a for loop to try additional servers should one fail to respond
+  $i = 0;
+  for($i; $i < $server_count; $i++) {
+    $socket = @fsockopen('udp://'.$servers[$i], 123, $err_no, $err_str,1);
+    if ($socket) {
+      
+      //add nulls to position 11 (the transmit timestamp, later to be returned as originate)
+      //10 lots of 32 bits
+      for ($j=1; $j<40; $j++) {
+        $request_packet .= chr(0x0);
+      }
+      
+      //the time our packet is sent from our server (returns a string in the form 'msec sec')
+      $local_sent_explode = explode(' ',microtime());
+      $local_sent = $local_sent_explode[1] + $local_sent_explode[0];
+      
+      //add 70 years to convert unix to ntp epoch
+      $originate_seconds = $local_sent_explode[1] + $epoch_convert;
+        
+      //convert the float given by microtime to a fraction of 32 bits
+      $originate_fractional = round($local_sent_explode[0] * $bit_max);
+        
+      //pad fractional seconds to 32-bit length
+      $originate_fractional = sprintf('%010d',$originate_fractional);
+        
+      //pack to big endian binary string
+      $packed_seconds = pack('N', $originate_seconds);
+      $packed_fractional = pack("N", $originate_fractional);
+  
+      //add the packed transmit timestamp
+      $request_packet .= $packed_seconds;
+      $request_packet .= $packed_fractional;
+      
+      if (fwrite($socket, $request_packet)) {
+        $data = NULL;
+        stream_set_timeout($socket, 1);
+        
+        $response = fread($socket, 48);
+        
+        //the time the response was received
+        $local_received = microtime(true);
+        
+        //echo 'response was: '.strlen($response).$response;
+      }
+      fclose($socket);
+      
+      if (strlen($response) == 48) {
+        //the response was of the right length, assume it's valid and break out of the loop
+        break;
+      }
+      else {
+        if ($i == $server_count-1) {
+          //this was the last server on the list, so give up
+          die('unable to establish a connection');
+        }
+      }
+    }
+    else {
+      if ($i == $server_count-1) {
+        //this was the last server on the list, so give up
+        die('unable to establish a connection');
+      }
+    }
+  }
+  
+  //unpack the response to unsiged lonng for calculations
+  $unpack0 = unpack("N12", $response);
+  //print_r($unpack0);
+  
+  //present as a decimal number
+  $remote_originate_seconds = sprintf('%u', $unpack0[7])-$epoch_convert;
+  $remote_received_seconds = sprintf('%u', $unpack0[9])-$epoch_convert;
+  $remote_transmitted_seconds = sprintf('%u', $unpack0[11])-$epoch_convert;
+  
+  $remote_originate_fraction = sprintf('%u', $unpack0[8]) / $bit_max;
+  $remote_received_fraction = sprintf('%u', $unpack0[10]) / $bit_max;
+  $remote_transmitted_fraction = sprintf('%u', $unpack0[12]) / $bit_max;
+  
+  $remote_originate = $remote_originate_seconds + $remote_originate_fraction;
+  $remote_received = $remote_received_seconds + $remote_received_fraction;
+  $remote_transmitted = $remote_transmitted_seconds + $remote_transmitted_fraction;
+  
+  //unpack to ascii characters for the header response
+  $unpack1 = unpack("C12", $response);
+  //print_r($unpack1);
+  
+  //echo 'byte 1: ' . $unpack1[1] . ' | ';
+  
+  //the header response in binary (base 2)
+  $header_response =  base_convert($unpack1[1], 10, 2);
+  
+  //pad with zeros to 1 byte (8 bits)
+  $header_response = sprintf('%08d',$header_response);
+  
+  //Mode (the last 3 bits of the first byte), converting to decimal for humans;
+  $mode_response = bindec(substr($header_response, -3));
+  
+  //VN
+  $vn_response = bindec(substr($header_response, -6, 3));
+  
+  //the header stratum response in binary (base 2)
+  $stratum_response =  base_convert($unpack1[2], 10, 2);
+  $stratum_response = bindec($stratum_response);
+  //echo 'stratum: ' . bindec($stratum_response);
+  
+  //calculations assume a symmetrical delay, fixed point would give more accuracy
+  $delay = (($local_received - $local_sent) / 2)  - ($remote_transmitted - $remote_received);
+  $delay_ms = round($delay * 1000) . ' ms';
+  //echo 'delay: ' . $delay * 1000 . 'ms';
+  
+  $server = $servers[$i];
+  
+  $ntp_time =  $remote_transmitted - $delay;
+  $ntp_time_explode = explode('.',$ntp_time);
+  
+  $ntp_time_formatted = date('Y-m-d H:i:s', $ntp_time_explode[0]).'.'.$ntp_time_explode[1];
+  
+  //compare with the current server time
+  $server_time =  microtime();
+  $server_time_explode = explode(' ', $server_time);
+  $server_time_micro = round($server_time_explode[0],4);
+  
+  $server_time_formatted = date('Y-m-d H:i:s', time()) .'.'. substr($server_time_micro,2);
+
+}
+?>
+
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8">
+<title></title>
+<meta name="description" content="">
+<meta name="author" content="">
+<style type="text/css">
+td{
+    width: 160px; height: 20px;
+    padding: 4px;
+    border: 1px solid #000;
+    font-size: 12px;
+}
+.ntp_response{
+    width: 240px;
+}
+</style>
+</head>
+<body onload="update_time();">
+<table border="0">
+<tr>
+<td>Server:
+<td class="ntp_response"><?php echo $server;?></td>
+</tr>
+<tr>
+<td>VN (version number):</td>
+<td class="ntp_response"><?php echo $vn_response;?></td>
+</tr>
+<tr>
+<td>Mode:</td>
+<td class="ntp_response"><?php echo $mode_response;?></td>
+</tr>
+<tr>
+<td>Stratum:</td>
+<td class="ntp_response"><?php echo $stratum_response;?></td>
+</tr>
+<tr>
+<td>Origin time:</td>
+<td class="ntp_response"><?php echo $remote_originate;?></td>
+</tr>
+<td>Received:</td>
+<td class="ntp_response"><?php echo $remote_received;?></td>
+</tr>
+<td>Transmitted:</td>
+<td class="ntp_response"><?php echo $remote_transmitted;?></td>
+</tr>
+<td>Delay:</td>
+<td class="ntp_response"><?php echo $delay_ms;?></td>
+</tr>
+<td>NTP time:</td>
+<td class="ntp_response"><?php echo $ntp_time_formatted;?></td>
+</tr>
+<td>Server time:</td>
+<td class="ntp_response"><?php echo $server_time_formatted;?></td>
+</tr>
+</table>
+
+<div id="updatetime"></div>
+<script type="text/javascript">
+var utcSeconds = parseInt(<?php echo $remote_received;?>,10);
+var date = new Date(0);
+date.setUTCSeconds(utcSeconds);
+
+function update_time() {
+  date.setSeconds(date.getSeconds() + 1);
+  document.getElementById("updatetime").innerHTML = (date);
+  setTimeout("update_time()", 1000);
+}
+
+</script>
+</body>
+</html>