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