# # 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 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 = <<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 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 E = 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['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 = ""; 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 = << {$head} {$map_head} {$title} - GPSD Test Station {$lat}, {$lon} {$autorefresh}
EOF; if (!strlen($advertise)) $advertise = $server; if ($testmode && !$sock) $part2 = ""; else $part2 = << EOF; if (!$open) $part3 = ''; else $part3 = << EOF; if ($testmode && !$sock) $part4 = ""; else { $fix = $GPS['tpv'][0]; $sky = $GPS['sky'][0]; $sats = $sky['satellites']; $fixtype = array('Unknown' => 0, 'No Fix' => 1,'2D Fix' => 2, '3D Fix' => 3); $type = array_search($fix['mode'],$fixtype); $nsv = count($sats); $ts = $fix['time']; $sat = ''; foreach($sats as $s) $sat .= sprintf( "\t\n", $s['PRN'], $s['el'], $s['az'], $s['ss'], $s['used'] ? 'Y' : 'N' ); $part4 = << EOF; } $part5 = << {$footer}

This script is distributed by the GPSD project.

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 = <<gpsd server located someplace. The hardware is a hardware description and link. This machine is maintained by Your Name Goes Here.
EOT; ?> EOB; fwrite($f, $buf); fclose($f); } function gen_gmap_head() { global $gmap_key; return << EOT; } function gen_osm_head() { global $GPS; return << EOT; } function gen_map_code() { return <<
Loading...
EOT; } ?>
{$blurb}

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.

{$map_code}
To get real-time information, connect to telnet://{$advertise}:{$port}/ and type "?POLL;" or "?WATCH={"enable":true,"raw":true}".
Use a different server:
:

The gpsd instance that this page monitors is not running.
%d%d%d%d%s
Current Information
Time (UTC){$ts}
Latitude{$fix['lat']}
Longitude{$fix['lon']}
Altitude{$fix['alt']}
Fix Type{$type}
Satellites{$nsv}
HDOP{$sky['hdop']}
VDOP{$sky['vdop']}

$sat
Current Satellites
PRNElevationAzimuthSSUsed