GPS status icon synchronised with menu entry
[gregoa/zavai.git] / vala / gps.vala
1 /*
2  * gps - gps resource for zavai
3  *
4  * Copyright (C) 2009  Enrico Zini <enrico@enricozini.org>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 using GLib;
22
23 namespace zavai {
24 namespace gps {
25
26 // For a list of dbus services, look in /etc/dbus-1/system.d/
27 public class GPS: zavai.Service
28 {
29         public dynamic DBus.Object usage;
30         public dynamic DBus.Object device;
31
32         public GPS()
33         {
34                 name = "gps";
35
36                 // see mdbus -s org.freesmartphone.ousaged /org/freesmartphone/Usage
37                 usage = zavai.registry.sbus.get_object(
38                         "org.freesmartphone.ousaged",
39                         "/org/freesmartphone/Usage",
40                         "org.freesmartphone.Usage");
41
42                 device = zavai.registry.sbus.get_object(
43                         "org.freesmartphone.ogpsd", 
44                         "/org/freedesktop/Gypsy",
45                         "org.freedesktop.Gypsy.Device");
46
47 //         # see mdbus -s org.freesmartphone.ogpsd /org/freedesktop/Gypsy
48 //         gps = self.bus.get_object('org.freesmartphone.ogpsd', '/org/freedesktop/Gypsy') 
49 //         self.gps = dbus.Interface(gps, "org.freedesktop.Gypsy.Device")
50 //         self.gps_time = dbus.Interface(gps, "org.freedesktop.Gypsy.Time")
51 //         self.gps_position = dbus.Interface(gps, 'org.freedesktop.Gypsy.Position')
52 //         self.gps_ubx = dbus.Interface(gps, 'org.freesmartphone.GPS.UBX')
53                 usage.ResourceChanged += on_resourcechanged;
54         }
55
56         public void on_resourcechanged(dynamic DBus.Object pos, string name, bool state, HashTable<string, Value?> attributes)
57         {
58                 zavai.log.info("RESOURCE CHANGED " + name);
59         }
60
61         /// Request GPS resource
62         public override void start()
63         {
64                 if (started) return;
65                 try {
66                         usage.RequestResource("GPS");
67                         zavai.log.info("Acquired GPS");
68                         base.start();
69                 } catch (GLib.Error e) {
70                         zavai.log.error(e.message);
71                 }
72                 base.start();
73         }
74
75         // Release usage of GPS
76         public override void stop()
77         {
78                 if (!started) return;
79                 try {
80                         usage.ReleaseResource("GPS");
81                         zavai.log.info("Released GPS");
82                         base.stop();
83                 } catch (GLib.Error e) {
84                         zavai.log.error(e.message);
85                 }
86                 base.stop();
87         }
88 }
89
90 public class Monitor : zavai.Service
91 {
92         static const int NAV_STATUS=0;
93         static const int NAV_SVINFO=1;
94         static const int NAV_MAX=2;
95
96         dynamic DBus.Object ubx;
97         dynamic DBus.Object time;
98         /*
99         string[] filters = { "NAV-STATUS", "NAV_SVINFO" };
100
101         int debug_busy;
102         int debug_want;
103         int debug_have;
104         int debug_error;
105         */
106
107
108         public Monitor()
109         {
110                 name = "gps.monitor";
111
112                 ubx = zavai.registry.sbus.get_object(
113                         "org.freesmartphone.ogpsd",
114                         "/org/freedesktop/Gypsy",
115                         "org.freesmartphone.GPS.UBX");
116                 time = zavai.registry.sbus.get_object(
117                         "org.freesmartphone.ogpsd",
118                         "/org/freedesktop/Gypsy",
119                         "org.freedesktop.Gypsy.Time");
120
121                 zavai.log.info("SETSIG1");
122                 time.TimeChanged += timechanged;
123                 zavai.log.info("SETSIG2");
124
125                 zavai.log.info("DEBUG1");
126                 ubx.DebugPacket += on_ubxdebug_packet;
127                 zavai.log.info("DEBUG2");
128
129 /*
130                 // This piece of machinery is taken from Zhone
131                 debug_busy = -1;
132                 debug_want = (1 << NAV_STATUS) | (1 << NAV_SVINFO);
133                 debug_have = 0;
134                 debug_error = 0;
135 */
136         }
137
138         protected void timechanged(dynamic DBus.Object pos, int t)
139         {
140                 zavai.log.info("TIMECHANGED");
141         }
142
143 /*
144         protected void debug_update() throws GLib.Error
145         {
146                 zavai.log.debug("UPDATE");
147                 if (debug_busy != -1)
148                         return;
149                 zavai.log.debug("UPD1");
150                 int pending = debug_want & (~debug_have) & (~debug_error);
151                 if (pending == 0)
152                         return;
153                 zavai.log.debug("UPD2");
154                 for (int i = 0; i < NAV_MAX; ++i)
155                         if ((pending & (1<<i)) != 0)
156                         {
157                                 debug_busy = i;
158                                 break;
159                         }
160                 zavai.log.debug("UPD3 " + filters[debug_busy]);
161
162                 ubx.SetDebugFilter(
163                         filters[debug_busy],
164                         true,
165                         on_debug_reply
166 //                      on_debug_error
167                 );
168                 zavai.log.debug("UPD4");
169         }
170
171         protected void debug_request() throws GLib.Error
172         {
173                 debug_have = 0;
174                 debug_update();
175         }
176
177         protected void on_debug_reply() throws GLib.Error
178         {
179                 debug_have |= (1<<debug_busy);
180                 debug_busy = -1;
181                 debug_update();
182         }
183
184         protected void on_debug_error(string e) throws GLib.Error
185         {
186                 string name = debug_busy == -1 ? "none" : filters[debug_busy];
187                 zavai.log.error("error while requesting debug packet " + name + ": " + e);
188                 debug_error |= (1<<debug_busy);
189                 debug_busy = -1;
190                 debug_update();
191         }
192 */
193
194         /*
195         protected void on_satellites_changed(satellites)
196         {
197 //         zavai.info("gps monitor: satellites changed")
198 //         self.debug_request()
199         }
200         */
201
202         protected void on_ubxdebug_packet(dynamic DBus.Object ubx, string clid, int length,
203                         HashTable<string, Value?>[] data)
204         {
205                 zavai.log.info("gps monitor: UBX debug packet");
206                 message("ZAZA %s %d", clid, length);
207                 message("ZAZA %u %lu", data.length, sizeof(HashTable<string, Value?>));
208                 message("ZAZA %p %p", (void*)data.length, this);
209                 message("ZAZA %p", data[0]);
210                 message("ZAZA %p", data[1]);
211                 message("ZAZA %p", data[2]);
212                 /*
213                 message("ZAZA %u", data[0].size());
214                 foreach (string k in data[0].get_keys())
215                         message("ZAZA %s", k);
216                 */
217                 PtrArray< HashTable<string, Value?> >* prova = (PtrArray< HashTable<string, Value?> >)data;
218                 message("ZAZA %u", prova->len);
219                 /*
220                 foreach (string k in prova[0].get_keys())
221                         message("ZAZA %s", k);
222                         */
223                 /*
224                 for (int i = 0; data[i] != null; ++i)
225                 {
226                         zavai.log.info("ZAZA");
227                 }
228                 */
229                 //message("Size: %d", data.size());
230 //         self.notify("satellites", clid, length, data)
231         }
232
233         protected override void start()
234         {
235                 if (started) return;
236
237                 zavai.log.info("Starting GPS Monitor");
238                 gps.request("gps.monitor");
239 //         # TODO: find out how come sometimes these events are not sent
240 //         self.gps.bus.add_signal_receiver(
241 //             self.on_satellites_changed, 'SatellitesChanged', 'org.freedesktop.Gypsy.Satellite',
242 //             'org.freesmartphone.ogpsd', '/org/freedesktop/Gypsy')
243                 //ubx.DebugPacket += prova;
244                 //try {
245                         //ubx.SetDebugFilter("NAV-STATUS", true);
246                         //ubx.SetDebugFilter("NAV-SVINFO", true);
247                         //debug_request();
248                         base.start();
249                 //} catch (GLib.Error e) {
250                         //zavai.log.error(e.message);
251                 //}
252         }
253
254         protected override void stop()
255         {
256                 if (!started) return;
257                 zavai.log.info("Stopping GPS Monitor");
258 //         self.gps.bus.remove_signal_receiver(
259 //             self.on_satellites_changed, 'SatellitesChanged', 'org.freedesktop.Gypsy.Satellite',
260 //             'org.freesmartphone.ogpsd', '/org/freedesktop/Gypsy')
261                 try {
262                         ubx.SetDebugFilter("NAV-STATUS", false);
263                         ubx.SetDebugFilter("NAV-SVINFO", false);
264                 } catch (GLib.Error e) {
265                         zavai.log.error(e.message);
266                 }
267                 ubx.DebugPacket -= on_ubxdebug_packet;
268                 //ubx.DebugPacket -= prova;
269                 gps.release("gps.monitor");
270                 base.stop();
271         }
272 }
273
274
275 public class Position : zavai.Service
276 {
277         dynamic DBus.Object position;
278
279         public signal void position_changed(int fields, int tstamp, double lat, double lon, double alt);
280
281         public Position()
282         {
283                 name = "gps.position";
284                 position = zavai.registry.sbus.get_object(
285                         "org.freesmartphone.ogpsd",
286                         "/org/freedesktop/Gypsy",
287                         "org.freedesktop.Gypsy.Position");
288         }
289
290         public void on_position_changed(dynamic DBus.Object pos, int fields, int tstamp, double lat, double lon, double alt)
291         {
292                 zavai.log.info("gps position: position changed");
293                 position_changed(fields, tstamp, lat, lon, alt);
294         }
295
296         public override void start()
297         {
298                 if (started) return;
299                 zavai.log.info("Starting GPS position tracking");
300                 gps.request("gps.position");
301                 position.PositionChanged += on_position_changed;
302                 base.start();
303         }
304
305         public override void stop()
306         {
307                 if (!started) return;
308                 zavai.log.info("Stopping GPS position tracking");
309                 position.PositionChanged -= on_position_changed;
310                 gps.release("gps.position");
311                 base.stop();
312         }
313 }
314
315 // #    def wait_for_fix(self, callback):
316 // #        status = self.gps.GetFixStatus()
317 // #        if status in [2, 3]:
318 // #            zavai.info("We already have a fix, good.")
319 // #            callback()
320 // #            return True
321 // #        else:
322 // #            zavai.info("Waiting for a fix...")
323 // #            self.waiting_for_fix = callback
324 // #            self.bus.add_signal_receiver(
325 // #                self.on_fix_status_changed, 'FixStatusChanged', 'org.freedesktop.Gypsy.Device',
326 // #                'org.freesmartphone.ogpsd', '/org/freedesktop/Gypsy')
327 // #            return False
328 // #
329 // #    def on_fix_status_changed(self, status):
330 // #        if status not in [2, 3]: return
331 // #
332 // #        zavai.info("Got GPS fix")
333 // #        self.bus.remove_signal_receiver(
334 // #            self.on_fix_status_changed, 'FixStatusChanged', 'org.freedesktop.Gypsy.Device',
335 // #            'org.freesmartphone.ogpsd', '/org/freedesktop/Gypsy')
336 // #
337 // #        if self.waiting_for_fix:
338 // #            self.waiting_for_fix()
339 // #            self.waiting_for_fix = None
340 // #
341 // 
342 // #    def start_recording(self):
343 // #        if self.gps_monitor:
344 // #            self.gps_monitor.stop()
345 // #            self.gps_monitor = None
346 // #
347 // #        if not self.audio:
348 // #            return
349 // #
350 // #        # Sync system time
351 // #        gpstime = self.gps.gps_time.GetTime()
352 // #        subprocess.call(["date", "-s", "@%d" % gpstime])
353 // #        subprocess.call(["hwclock", "--systohc"])
354 // #
355 // #        # Compute basename for output files
356 // #        self.basename = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(gpstime))
357 // #        self.basename = os.path.join(AUDIODIR, self.basename)
358 // #
359 // #        # Start recording the GPX track
360 // #        self.gpx = GPX(self.basename)
361 // #        self.gps.track_position(self.on_position_changed)
362 // #
363 // #        # Start recording in background forking arecord
364 // #        self.audio.set_basename(self.basename)
365 // #        self.audio.start_recording()
366 // #
367
368 // Write GPX track and waypoint files
369 public class GPX : Service
370 {
371         public bool tracking {
372                 get { return trk != null; }
373                 set {}
374         }
375         public signal void tracking_changed(bool tracking);
376
377         FileStream trk = null;
378         FileStream wpt = null;
379         int wpt_seq = 1;
380         bool last_valid = false;
381         int last_fields;
382         time_t last_tstamp;
383         double last_lat;
384         double last_lon;
385         double last_alt;
386
387         public GPX()
388         {
389                 name = "gps.gpx";
390         }
391
392         public override void start()
393         {       
394                 if (!started)
395                 {
396                         log.info("Starting GPX trace subsystem");
397                         position.request("gps.gpx");
398                         position.position_changed += on_position_changed;
399                         base.start();
400                 }
401         }
402
403         public override void stop()
404         {
405                 if (started)
406                 {
407                         log.info("Stopping GPX trace subsystem");
408                         position.release("gps.gpx");
409                         position.position_changed -= on_position_changed;
410                         stop_track();
411                         base.stop();
412                 }
413         }
414
415         public void on_position_changed(Position pos, int fields, int tstamp, double lat, double lon, double alt)
416         {
417                 last_fields = fields;
418                 last_tstamp = tstamp;
419                 last_lat = lat;
420                 last_lon = lon;
421                 last_alt = alt;
422                 last_valid = true;
423                 trackpoint();
424         }
425
426         public void start_track(time_t tstamp = 0, string? basename = null)
427         {
428                 string fname;
429                 if (basename != null)
430                         fname = basename;
431                 else
432                 {
433                         time_t now = tstamp == 0 ? time_t() : tstamp;
434
435                         // Compute basename for output files
436                         var t = Time.local(now);
437                         char[] res = new char[25];
438                         t.strftime(res, "%Y-%m-%d-%H-%M-%S");
439                         fname = zavai.config.homedir + "/" + (string)res;
440                 }
441  
442                 trk = FileStream.open(fname + "-trk.gpx", "wt");
443                 trk.puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
444                 trk.puts(" <gpx");
445                 trk.puts("     version=\"1.0\"");
446                 trk.printf("     creator=\"zavai %s\"\n", zavai.config.version);
447                 trk.puts("     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
448                 trk.puts("     xmlns=\"http://www.topografix.com/GPX/1/0\"");
449                 trk.puts("     xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">");
450                 trk.puts("   <trk>");
451                 trk.puts("     <trkseg>");
452  
453                 wpt = FileStream.open(fname + "-wpt.gpx", "wt");
454                 wpt.puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
455                 wpt.puts(" <gpx");
456                 wpt.puts("     version=\"1.0\"");
457                 wpt.printf("     creator=\"zavai %s\"", zavai.config.version);
458                 wpt.puts("     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
459                 wpt.puts("     xmlns=\"http://www.topografix.com/GPX/1/0\"");
460                 wpt.puts("     xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">");
461  
462                 wpt_seq = 1;
463                 tracking_changed(true);
464         }
465  
466         public void stop_track()
467         {
468                 if (trk != null)
469                 {
470                         trk.puts("</trkseg></trk></gpx>");
471                         trk.flush();
472                         trk = null;
473                 }
474                 if (wpt != null)
475                 {
476                         wpt.puts("</gpx>");
477                         wpt.flush();
478                         wpt = null;
479                 }
480                 last_valid = false;
481                 tracking_changed(false);
482         }
483
484         // Mark a track point
485         public void trackpoint()
486         {
487                 if (!last_valid) return;
488                 if (trk == null)
489                         start_track(last_tstamp);
490
491                 trk.printf("<trkpt lat=\"%f\" lon=\"%f\">\n", last_lat, last_lon);
492                 var t = Time.local(last_tstamp);
493                 char[] ts = new char[25];
494                 t.strftime(ts, "%Y-%m-%dT%H:%M:%SZ");
495                 trk.printf("  <time>%s</time>\n", (string)ts);
496                 trk.printf("  <ele>%f</ele>\n", last_alt);
497                 // if course is not None: print >>self.trk, "    <course>%f</course>" % course
498                 // if speed is not None: print >>self.trk, "    <speed>%f</speed>" % speed
499                 // if fix is not None: print >>self.trk, "    <fix>%f</fix>" % fix
500                 // if hdop is not None: print >>self.trk, "    <hdop>%f</hdop>" % hdop
501                 trk.puts("</trkpt>");
502         }
503
504         // Mark a waypoint
505         public void waypoint(string? name = null)
506         {
507                 if (!last_valid) return;
508                 if (wpt == null)
509                         start_track(last_tstamp);
510
511                 string wptname;
512                 if (name == null)
513                 {
514                         wptname = "wpt_%d".printf(wpt_seq);
515                         wpt_seq += 1;
516                 } else {
517                         wptname = name;
518                 }
519
520                 wpt.printf("<wpt lat=\"%f\" lon=\"%f\">\n", last_lat, last_lon);
521                 wpt.printf("  <name>%s</name>\n", wptname);
522                 var t = Time.local(last_tstamp);
523                 char[] ts = new char[25];
524                 t.strftime(ts, "%Y-%m-%dT%H:%M:%SZ");
525                 wpt.printf("  <time>%s</time>\n", (string)ts);
526                 wpt.printf("  <ele>%f</ele>\n", last_alt);
527                 wpt.puts("</wpt>");
528         }
529 }
530
531 // #    def record(self):
532 // #        self.audio = Audio(self.make_waypoint)
533 // #        self.gps = GPS()
534 // #        # Get a fix and start recording
535 // #        if not self.gps.wait_for_fix(self.start_recording):
536 // #            self.gps_monitor = GPSMonitor(self.gps)
537 // #            self.gps_monitor.start()
538 // #
539 // #    def monitor(self):
540 // #        self.audio = None
541 // #        self.gps = GPS()
542 // #        self.gps_monitor = GPSMonitor(self.gps)
543 // #        self.gps_monitor.start()
544 // #
545 // #
546 // #    def make_waypoint(self):
547 // #        if self.gpx is None:
548 // #            return
549 // #        if self.last_pos is None:
550 // #            self.last_pos = self.gps.gps_position.GetPosition()
551 // #        (fields, tstamp, lat, lon, alt) = self.last_pos
552 // #        self.gpx.waypoint(tstamp, lat, lon, alt)
553 // #        zavai.info("Making waypoint at %s: %f, %f, %f" % (
554 // #            time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(tstamp)), lat, lon, alt))
555
556 public zavai.gps.GPS gps = null;
557 public zavai.gps.Monitor monitor = null;
558 public zavai.gps.Position position = null;
559 public zavai.gps.GPX gpx = null;
560
561 public void init()
562 {
563         gps = new GPS();
564         monitor = new Monitor();
565         position = new Position();
566         gpx = new GPX();
567
568         zavai.registry.register_service(gps);
569         zavai.registry.register_service(position);
570         zavai.registry.register_service(monitor);
571         zavai.registry.register_service(gpx);
572 }
573
574 }
575 }