cleanup specfile for packaging
[profile/ivi/gpsd.git] / gpsd.php
1 <?php
2
3 #$CSK: gpsd.php,v 1.39 2006/11/21 22:31:10 ckuethe Exp $
4
5 # Copyright (c) 2006,2010 Chris Kuethe <chris.kuethe@gmail.com>
6 #
7 # Permission to use, copy, modify, and distribute this software for any
8 # purpose with or without fee is hereby granted, provided that the above
9 # copyright notice and this permission notice appear in all copies.
10 #
11 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
19 global $head, $blurb, $title, $googlemap, $autorefresh, $footer, $gmap_key;
20 global $server, $advertise, $port, $open, $swap_ew, $testmode;
21 $testmode = 1; # leave this set to 1
22
23 # Public script parameters:
24 #   host: host name or address where GPSd runs. Default: from config file
25 #   port: port of GPSd. Default: from config file
26 #   op=view: show just the skyview image instead of the whole HTML page
27 #     sz=small: used with op=view, display a small (240x240px) skyview
28 #   op=json: respond with the GPSd POLL JSON structure
29 #     jsonp=prefix: used with op=json, wrap the POLL JSON in parentheses
30 #                   and prepend prefix
31
32 # If you're running PHP with the Suhosin patch (like the Debian PHP5 package),
33 # it may be necessary to increase the value of the
34 # suhosin.get.max_value_length parameter to 2048. The imgdata parameter used
35 # for displaying the skyview is longer than the default 512 allowed by Suhosin.
36 # Debian has the config file at /etc/php5/conf.d/suhosin.ini.
37
38 # this script shouldn't take more than a few seconds to run
39 set_time_limit(3);
40 ini_set('max_execution_time', 3);
41
42 if (!file_exists("gpsd_config.inc"))
43         write_config();
44
45 require_once("gpsd_config.inc");
46
47 # sample data
48 $resp = <<<EOF
49 {"class":"POLL","timestamp":1270517274.846,"active":1,
50  "fixes":[{"class":"TPV","tag":"MID41","device":"/dev/ttyUSB0",
51            "time":1270517264.240,"ept":0.005,"lat":40.035093060,
52            "lon":-75.519748733,"alt":31.1,"track":99.4319,
53            "speed":0.123,"mode":3}],
54  "skyviews":[{"class":"SKY","tag":"MID41","device":"/dev/ttyUSB0",
55               "time":1270517264.240,"hdop":9.20,"vdop":12.1,
56               "satellites":[{"PRN":16,"el":55,"az":42,"ss":36,"used":true},
57                             {"PRN":19,"el":25,"az":177,"ss":0,"used":false},
58                             {"PRN":7,"el":13,"az":295,"ss":0,"used":false},
59                             {"PRN":6,"el":56,"az":135,"ss":32,"used":true},
60                             {"PRN":13,"el":47,"az":304,"ss":0,"used":false},
61                             {"PRN":23,"el":66,"az":259,"ss":40,"used":true},
62                             {"PRN":20,"el":7,"az":226,"ss":0,"used":false},
63                             {"PRN":3,"el":52,"az":163,"ss":32,"used":true},
64                             {"PRN":31,"el":16,"az":102,"ss":0,"used":false}
65                            ]
66              }
67             ]
68 }
69 EOF;
70
71
72
73 # if we're passing in a query, let's unpack and use it
74 $op = isset($_GET['op']) ? $_GET['op'] : '';
75 if (isset($_GET['imgdata']) && $op == 'view'){
76         $resp = base64_decode($_GET['imgdata']);
77         if ($resp){
78                 gen_image($resp);
79                 exit(0);
80         }
81 } else {
82         if (isset($_GET['host']))
83                 if (!preg_match('/[^a-zA-Z0-9\.-]/', $_GET['host']))
84                         $server = $_GET['host'];
85
86         if (isset($_GET['port']))
87                 if (!preg_match('/\D/', $_GET['port']) && ($port>0) && ($port<65536))
88                         $port = $_GET['port'];
89
90         if ($testmode){
91                 $sock = @fsockopen($server, $port, $errno, $errstr, 2);
92                 @fwrite($sock, "?POLL;\n");
93                 for($tries = 0; $tries < 10; $tries++){
94                         $resp = @fread($sock, 1536); # SKY can be pretty big
95                         if (preg_match('/{"class":"POLL".+}/i', $resp, $m)){
96                                 $resp = $m[0];
97                                 break;
98                         }
99                 }
100                 @fclose($sock);
101                 if (!$resp)
102                         $resp = '{"class":"ERROR","message":"no response from GPS daemon"}';
103         }
104 }
105
106 if ($op == 'view')
107         gen_image($resp);
108 else if ($op == 'json')
109         write_json($resp);
110 else
111         write_html($resp);
112
113 exit(0);
114
115 ###########################################################################
116 function colorsetup($im){
117         $C['white']     = imageColorAllocate($im, 255, 255, 255);
118         $C['ltgray']    = imageColorAllocate($im, 191, 191, 191);
119         $C['mdgray']    = imageColorAllocate($im, 127, 127, 127);
120         $C['dkgray']    = imageColorAllocate($im, 63, 63, 63);
121         $C['black']     = imageColorAllocate($im, 0, 0, 0);
122         $C['red']       = imageColorAllocate($im, 255, 0, 0);
123         $C['brightgreen'] = imageColorAllocate($im, 0, 255, 0);
124         $C['darkgreen'] = imageColorAllocate($im, 0, 192, 0);
125         $C['blue']      = imageColorAllocate($im, 0, 0, 255);
126         $C['cyan']      = imageColorAllocate($im, 0, 255, 255);
127         $C['magenta']   = imageColorAllocate($im, 255, 0, 255);
128         $C['yellow']    = imageColorAllocate($im, 255, 255, 0);
129         $C['orange']    = imageColorAllocate($im, 255, 128, 0);
130
131         return $C;
132 }
133
134 function legend($im, $sz, $C){
135         $r = 30;
136         $fn = 5;
137         $x = $sz - (4*$r+7) - 2;
138         $y = $sz - $r - 3;
139
140         imageFilledRectangle($im, $x, $y, $x + 4*$r + 7, $y + $r +1, $C['dkgray']);
141         imageRectangle($im, $x+0*$r+1, $y+1, $x + 1*$r + 0, $y + $r, $C['red']);
142         imageRectangle($im, $x+1*$r+2, $y+1, $x + 2*$r + 2, $y + $r, $C['yellow']);
143         imageRectangle($im, $x+2*$r+4, $y+1, $x + 3*$r + 4, $y + $r, $C['darkgreen']);
144         imageRectangle($im, $x+4*$r+6, $y+1, $x + 3*$r + 6, $y + $r, $C['brightgreen']);
145         imageString($im, $fn, $x+3+0*$r, $y+$r/3, "<30", $C['red']);
146         imageString($im, $fn, $x+5+1*$r, $y+$r/3, "30+", $C['yellow']);
147         imageString($im, $fn, $x+7+2*$r, $y+$r/3, "35+", $C['darkgreen']);
148         imageString($im, $fn, $x+9+3*$r, $y+$r/3, "40+", $C['brightgreen']);
149 }
150
151 function radial($angle, $sz){
152         #turn into radians
153         $angle = deg2rad($angle);
154
155         # determine length of radius
156         $r = $sz * 0.5 * 0.95;
157
158         # and convert length/azimuth to cartesian
159         $x0 = sprintf("%d", (($sz * 0.5) - ($r * cos($angle))));
160         $y0 = sprintf("%d", (($sz * 0.5) - ($r * sin($angle))));
161         $x1 = sprintf("%d", (($sz * 0.5) + ($r * cos($angle))));
162         $y1 = sprintf("%d", (($sz * 0.5) + ($r * sin($angle))));
163
164         return array($x0, $y0, $x1, $y1);
165 }
166
167 function azel2xy($az, $el, $sz){
168         global $swap_ew;
169         #rotate coords... 90deg W = 180deg trig
170         $az += 270;
171
172         #turn into radians
173         $az = deg2rad($az);
174
175         # determine length of radius
176         $r = $sz * 0.5 * 0.95;
177         $r -= ($r * ($el/90));
178
179         # and convert length/azimuth to cartesian
180         $x = sprintf("%d", (($sz * 0.5) + ($r * cos($az))));
181         $y = sprintf("%d", (($sz * 0.5) + ($r * sin($az))));
182         if ($swap_ew == 0)
183                 $x = $sz - $x;
184
185         return array($x, $y);
186 }
187
188 function splot($im, $sz, $C, $e){
189         if ((0 == $e['PRN']) || (0 == $e['az'] + $e['el'] + $e['ss']) ||
190             ($e['az'] < 0) || ($e['el'] < 0))
191                 return;
192
193         $color = $C['brightgreen'];
194         if ($e['ss'] < 40)
195                 $color = $C['darkgreen'];
196         if ($e['ss'] < 35)
197                 $color = $C['yellow'];
198         if ($e['ss'] < 30)
199                 $color = $C['red'];
200         if ($e['el']<10)
201                 $color = $C['blue'];
202         if ($e['ss'] < 10)
203                 $color = $C['black'];
204
205         list($x, $y) = azel2xy($e['az'], $e['el'], $sz);
206
207         $r = 12;
208         if (isset($_GET['sz']) && ($_GET['sz'] == 'small'))
209                 $r = 8;
210
211         imageString($im, 3, $x+4, $y+4, $e['PRN'], $C['black']);
212         if ($e['used'] == true)
213                 if ($e['PRN'] > 32)
214                         imageFilledDiamond($im, $x, $y, $r, $color);
215                 else
216                         imageFilledArc($im, $x, $y, $r, $r, 0, 360, $color, 0);
217         else
218                 if ($e['PRN'] > 32)
219                         imageDiamond($im, $x, $y, $r, $color);
220                 else
221                         imageArc($im, $x, $y, $r, $r, 0, 360, $color);
222 }
223
224 function imageDiamond($im, $x, $y, $r, $color){
225         $t = $r/2;
226         # this lunacy is because imagesetthickness doesn't seem to work
227         $vx = array ( $x+$t, $y, $x, $y+$t, $x-$t, $y, $x, $y-$t );
228         imagepolygon($im, $vx, 4, $color);
229         $t--;
230         $vx = array ( $x+$t, $y, $x, $y+$t, $x-$t, $y, $x, $y-$t );
231         imagepolygon($im, $vx, 4, $color);
232         $t--;
233         $vx = array ( $x+$t, $y, $x, $y+$t, $x-$t, $y, $x, $y-$t );
234         imagepolygon($im, $vx, 4, $color);
235 }
236
237 function imageFilledDiamond($im, $x, $y, $r, $color){
238         $t = $r/2;
239         while($t){
240                 $vx = array ( $x+$t, $y, $x, $y+$t, $x-$t, $y, $x, $y-$t );
241                 imagepolygon($im, $vx, 4, $color);
242                 $t -= 0.5;
243         }
244 }       
245
246 function elevation($im, $sz, $C, $a){
247         $b = 90 - $a;
248         $a = $sz * 0.95 * ($a/180);
249         imageArc($im, $sz/2, $sz/2, $a*2, $a*2, 0, 360, $C['ltgray']);
250         $x = $sz/2 - 16;
251         $y = $sz/2 - $a;
252         imageString($im, 2, $x, $y, $b, $C['ltgray']);
253 }
254
255 function skyview($im, $sz, $C){
256         global $swap_ew;
257         $a = 90; $a = $sz * 0.95 * ($a/180);
258         imageFilledArc($im, $sz/2, $sz/2, $a*2, $a*2, 0, 360, $C['mdgray'], 0);
259         imageArc($im, $sz/2, $sz/2, $a*2, $a*2, 0, 360, $C['black']);
260         $x = $sz/2 - 16; $y = $sz/2 - $a;
261         imageString($im, 2, $x, $y, "0", $C['ltgray']);
262
263         $a = 85; $a = $sz * 0.95 * ($a/180);
264         imageFilledArc($im, $sz/2, $sz/2, $a*2, $a*2, 0, 360, $C['white'], 0);
265         imageArc($im, $sz/2, $sz/2, $a*2, $a*2, 0, 360, $C['ltgray']);
266         imageString($im, 1, $sz/2 - 6, $sz+$a, '5', $C['black']);
267         $x = $sz/2 - 16; $y = $sz/2 - $a;
268         imageString($im, 2, $x, $y, "5", $C['ltgray']);
269
270         for($i = 0; $i < 180; $i += 15){
271                 list($x0, $y0, $x1, $y1) = radial($i, $sz);
272                 imageLine($im, $x0, $y0, $x1, $y1, $C['ltgray']);
273         }
274
275         for($i = 15; $i < 90; $i += 15)
276                 elevation($im, $sz, $C, $i);
277
278         $x = $sz/2 - 16; $y = $sz/2 - 8;
279         /* imageString($im, 2, $x, $y, "90", $C['ltgray']); */
280
281         imageString($im, 4, $sz/2 + 4, 2        , 'N', $C['black']);
282         imageString($im, 4, $sz/2 + 4, $sz - 16 , 'S', $C['black']);
283         if ($swap_ew == 0){
284                 imageString($im, 4, 4        , $sz/2 + 4, 'E', $C['black']);
285                 imageString($im, 4, $sz - 10 , $sz/2 + 4, 'W', $C['black']);
286         } else {
287                 imageString($im, 4, 4        , $sz/2 + 4, 'W', $C['black']);
288                 imageString($im, 4, $sz - 10 , $sz/2 + 4, 'E', $C['black']);
289         }
290 }
291
292 function gen_image($resp){
293         $sz = 600;
294         if (isset($_GET['sz']) && ($_GET['sz'] == 'small'))
295                 $sz = 240;
296
297         $GPS = json_decode($resp, true);
298         if ($GPS['class'] != "POLL"){
299                 die("json_decode error: $resp");
300         }
301
302         $im = imageCreate($sz, $sz);
303         $C = colorsetup($im);
304         skyview($im, $sz, $C);
305         if ($sz > 240)
306                 legend($im, $sz, $C);
307
308         for($i = 0; $i < count($GPS['skyviews'][0]['satellites']); $i++){
309                 splot($im, $sz, $C, $GPS['skyviews'][0]['satellites'][$i]);
310         }
311
312         header("Content-type: image/png");
313         imagePNG($im);
314         imageDestroy($im);
315 }
316
317 function dfix($x, $y, $z){
318         if ($x < 0){
319                 $x = sprintf("%f %s", -1 * $x, $z);
320         } else {
321                 $x = sprintf("%f %s", $x, $y);
322         }
323         return $x;
324 }
325
326 function write_html($resp){
327         global $sock, $errstr, $errno, $server, $port, $head, $body, $open;
328         global $blurb, $title, $autorefresh, $googlemap, $gmap_key, $footer;
329         global $testmode, $advertise;
330
331         $GPS = json_decode($resp, true);
332         if ($GPS['class'] != 'POLL'){
333                 die("json_decode error: $resp");
334         }
335
336         header("Content-type: text/html; charset=UTF-8");
337
338         global $lat, $lon;
339         $lat = (float)$GPS['fixes'][0]['lat'];
340         $lon = (float)$GPS['fixes'][0]['lon'];
341         $x = $server; $y = $port;
342         $imgdata = base64_encode($resp);
343         $server = $x; $port = $y;
344
345         if ($autorefresh > 0)
346                 $autorefresh = "<meta http-equiv='Refresh' content='$autorefresh'/>";
347         else
348                 $autorefresh = '';
349
350         $gmap_head = $gmap_body = $gmap_code = '';
351         if ($googlemap){
352                 $gmap_head = gen_gmap_head();
353                 $gmap_body = 'onload="Load()" onunload="GUnload()"';
354                 $gmap_code = gen_gmap_code();
355         }
356         $part1 = <<<EOF
357 <?xml version="1.0" encoding="UTF-8"?>
358 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
359     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
360 <html xmlns="http://www.w3.org/1999/xhtml">
361 <head>
362 {$head}
363 {$gmap_head}
364 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
365 <meta http-equiv="Content-Language" content="en,en-us"/>
366 <title>{$title} - GPSD Test Station {$lat}, {$lon}</title>
367 {$autorefresh}
368 <style>
369 .warning {
370     color: #FF0000;
371  }
372
373 .fixed {
374     font-family: courier, fixed;
375 }
376
377 .caption {
378     text-align: left; 
379     margin: 1ex 3em 1ex 3em; /* top right bottom left */
380 }
381
382 .administrivia {
383     font-size: small; 
384     font-family: verdana, sans-serif;
385 }
386 </style>
387 </head>
388
389 <body {$body} {$gmap_body}>
390 <center>
391 <table border="0">
392 <tr><td align="justify">
393 {$blurb}
394 </td>
395 EOF;
396
397         if (!strlen($advertise))
398                 $advertise = $server;
399
400         if ($testmode && !$sock)
401                 $part2 = "";
402         else
403                 $part2 = <<<EOF
404 <!-- ------------------------------------------------------------ -->
405
406 <td rowspan="4" align="center" valign="top">
407 <img src="?op=view&amp;imgdata={$imgdata}"
408 width="600" height="600"/>
409 <br clear="all"/>
410 <p class="caption">A filled circle means the satellite was used in
411 the last fix. Green-yellow-red colors indicate signal strength in dB, 
412  green=most and red=least.  Diamonds indicate SBAS satellites.</p>
413 {$gmap_code}</td>
414 </tr>
415 EOF;
416
417         if (!$open)
418                 $part3 = '';
419         else
420                 $part3 = <<<EOF
421 <!-- ------------------------------------------------------------ -->
422
423 <tr><td align="justify">To get real-time information, connect to
424 <span class="fixed">telnet://{$advertise}:{$port}/</span> and type "?POLL;"
425 or "?WATCH={"enable":true,"raw":true}".<br/>
426 Use a different server:<br/>
427 <form method=GET action="${_SERVER['SCRIPT_NAME']}">
428 <input name="host" value="{$advertise}">:
429 <input name="port" value="{$port}" size="5" maxlength="5">
430 <input type=submit value="Get Position"><input type=reset></form>
431 <br/>
432 </td>
433 </tr>
434 EOF;
435
436         if ($testmode && !$sock)
437                 $part4 = "<tr><td><font color='red'>The gpsd instance that this page monitors is not running.</font></td></tr>";
438         else {
439                 $nsv = count($GPS['skyviews'][0]['satellites']);
440                 $ts = gmdate("r", $GPS['fixes'][0]['time']);
441                 $part4 = <<<EOF
442 <!-- ------------------------------------------------------------ -->
443         <tr><td align=center valign=top>
444         <table border=1>
445         <tr><td colspan=2 align=center><b>Current Information</b></td></tr>
446         <tr><td>Time (UTC)</td><td>{$ts}</td></tr>
447         <tr><td>Latitude</td><td>{$GPS['fixes'][0]['lat']}</td></tr>
448         <tr><td>Longitude</td><td>{$GPS['fixes'][0]['lon']}</td></tr>
449         <tr><td>Altitude</td><td>{$GPS['fixes'][0]['alt']}</td></tr>
450         <tr><td>Fix Type</td><td>{$GPS['fixes'][0]['mode']}</td></tr>
451         <tr><td>Satellites</td><td>{$nsv}</td></tr>
452         <tr><td>HDOP</td><td>{$GPS['skyviews'][0]['hdop']}</td></tr>
453         </table>
454 </tr>
455 <!-- raw response
456 {$resp}
457 -->
458 EOF;
459         }
460
461         $part5 = <<<EOF
462 </table>
463 </center>
464
465 {$footer}
466
467 <hr/>
468 <span class="administrivia">This script is distributed by the <a href="http://gpsd.berlios.de">GPSD project</a>.</span><br/>
469 </body>
470 </body>
471
472 EOF;
473
474 print $part1 . $part2 . $part3 . $part4 . $part5;
475
476 }
477
478 function write_json($resp){
479         header('Content-Type: text/javascript');
480         if (isset($_GET['jsonp']))
481                 print "{$_GET['jsonp']}({$resp})";
482         else
483                 print $resp;
484 }
485
486 function write_config(){
487         $f = fopen("gpsd_config.inc", "a");
488         if (!$f)
489                 die("can't generate prototype config file. try running this script as root in DOCUMENT_ROOT");
490
491         $buf = <<<EOB
492 <?PHP
493 \$title = 'My GPS Server';
494 \$server = 'localhost';
495 #\$advertise = 'localhost';
496 \$port = 2947;
497 \$autorefresh = 0; # number of seconds after which to refresh
498 \$googlemap = 0; # set to 1 if you want to have a google map
499 \$gmap_key = 'GetYourOwnGoogleKey'; # your google API key goes here
500 \$swap_ew = 0; # set to 1 if you don't understand projections
501 \$open = 0; # set to 1 to show the form to change the GPSd server
502
503 ## You can read the header, footer and blurb from a file...
504 # \$head = file_get_contents('/path/to/header.inc');
505 # \$body = file_get_contents('/path/to/body.inc');
506 # \$footer = file_get_contents('/path/to/footer.hinc');
507 # \$blurb = file_get_contents('/path/to/blurb.inc');
508
509 ## ... or you can just define them here
510 \$head = '';
511 \$body = '';
512 \$footer = '';
513 \$blurb = <<<EOT
514 This is a
515 <a href="http://gpsd.berlios.de">gpsd</a>
516 server <blink><font color="red">located someplace</font></blink>.
517
518 The hardware is a
519 <blink><font color="red">hardware description and link</font></blink>.
520
521 This machine is maintained by
522 <a href="mailto:you@example.com">Your Name Goes Here</a>.<br/>
523 EOT;
524
525 ?>
526
527 EOB;
528         fwrite($f, $buf);
529         fclose($f);
530 }
531
532 function gen_gmap_head() {
533 global $gmap_key;
534 return <<<EOT
535 <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key={$gmap_key}" type="text/javascript"></script>
536 <script type="text/javascript">
537     <!--
538     // Create a base icon for all of our markers that specifies the shadow, icon
539     // dimensions, etc.
540 function Load() {
541   if (GBrowserIsCompatible()) {
542     var map = new GMap2(document.getElementById("map"));
543     var point = new GLatLng( {$GLOBALS['lat']}, {$GLOBALS['lon']} );
544     map.setCenter( point, 14);
545     map.addControl(new GLargeMapControl());
546     map.addControl(new GMapTypeControl());
547
548     var baseIcon = new GIcon();
549     baseIcon.shadow = "http://www.google.com/mapfiles/shadow50.png";
550     baseIcon.iconSize = new GSize(20, 34);
551     baseIcon.shadowSize = new GSize(37, 34);
552     baseIcon.iconAnchor = new GPoint(9, 34);
553     baseIcon.infoWindowAnchor = new GPoint(9, 2);
554     baseIcon.infoShadowAnchor = new GPoint(18, 25);
555
556     var icon = new GIcon(baseIcon);
557     icon.image = "http://www.google.com/mapfiles/marker.png";
558     var marker = new GMarker(point, icon);
559     map.addOverlay(marker);
560   }
561 }
562
563     -->
564     </script>
565 EOT;
566
567 }
568 function gen_gmap_code() {
569 return <<<EOT
570 <br/>
571     <div id="map" style="width: 550px; height: 400px; border:1px; border-style: solid;">
572     Loading...
573     <noscript>
574 <span class='warning'>Sorry: you must enable javascript to view our maps.</span><br/>
575     </noscript>
576 </div>
577
578
579 EOT;
580 }
581
582 ?>