]> ToastFreeware Gitweb - gregoa/zavai.git/blob - src/gps.vala
Draft gpx-based logging module
[gregoa/zavai.git] / src / gps.vala
1 /*
2  * gps - gps resource for zavai
3  *
4  * Copyright (C) 2009--2010  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     protected libgps.data_t data;
30     protected IOChannel gpsfd = null;
31     protected uint gpsfd_watch = 0;
32
33     protected int old_fix_status = libgps.STATUS_NO_FIX;
34     protected uint old_time = 0;
35
36     public signal void fix_status_changed(int status);
37     public signal void time_changed(uint time);
38
39     public GPS()
40     {
41         Object(name: "gps");
42         data = libgps.data_t();
43     }
44
45     public int fix_status() { return old_fix_status; }
46     public double time() { return old_time; }
47     public weak libgps.data_t info() { return data; }
48
49     protected bool on_input_data(IOChannel source, IOCondition condition)
50     {
51         while (libgps.waiting(ref data))
52         {
53             int res = libgps.poll(ref data);
54             if (res != 0)
55                 zavai.log.error(libgps.errstr(res));
56
57             if (data.status != old_fix_status)
58             {
59                 fix_status_changed(data.status);
60                 old_fix_status = data.status;
61             }
62
63             uint cur_time = (uint)data.fix.time;
64             if (data.status != libgps.STATUS_NO_FIX && old_time != cur_time)
65             {
66                 time_changed(cur_time);
67                 old_time = cur_time;
68             }
69
70             /*
71             stderr.printf("GPSMSG %d %d\n", (int)data.set, data.status);
72             stderr.printf("SATUSED %d\n", data.satellites_used);
73             stderr.printf("SWT %f\n", data.skyview_time);
74             stderr.printf("SATVIS %d\n", data.satellites_visible);
75             for (int i = 0; i < data.satellites_visible; ++i)
76             {
77                 stderr.printf("PRN %d ELE %d AZI %d SS %f\n",
78                     data.PRN[i], data.elevation[i], data.azimuth[i], data.ss[i]);
79             }
80             */
81         }
82         return true;
83     }
84
85     /// Request GPS resource
86     public override void start()
87     {
88         if (started) return;
89
90         try {
91             // Then run our own script
92             zavai.app.run_script(zavai.config.homedir + "/gps start");
93         } catch (Error e) {
94             zavai.log.error("Running " + zavai.config.homedir + "/gps start: " + e.message);
95             return;
96         }
97
98         int res = libgps.open_r("ciapino", "gpsd", ref data);
99         if (res != 0)
100         {
101             zavai.log.error(libgps.errstr(res));
102             return;
103         }
104
105         res = libgps.stream(ref data, libgps.WATCH_ENABLE, null);
106         if (res != 0)
107         {
108             zavai.log.error(libgps.errstr(res));
109             return;
110         }
111
112         res = libgps.send(ref data, "?SKY;");
113         //res = libgps.send(ref data, "?WATCH;");
114         //if (res != 0) zavai.log.error(libgps.errstr(res));
115
116         gpsfd = new IOChannel.unix_new(data.gps_fd);
117         try {
118             gpsfd.set_encoding(null);
119         } catch (Error e) {
120             zavai.log.error("Setting encoding to null on gpsd io channel: " + e.message);
121         }
122         //gpsfd.set_buffered(false);
123         gpsfd_watch = gpsfd.add_watch(IOCondition.IN, on_input_data);
124
125         zavai.log.info("GPS turned on");
126         base.start();
127     }
128
129     // Release usage of GPS
130     public override void stop()
131     {
132         if (!started) return;
133
134         Source.remove(gpsfd_watch);
135
136         int res = libgps.close(ref data);
137         if (res != 0)
138             zavai.log.error(libgps.errstr(res));
139
140         try {
141             zavai.app.run_script(zavai.config.homedir + "/gps stop");
142         } catch (Error e) {
143             zavai.log.error("Running device stop gps: " + e.message);
144         }
145
146         if (old_fix_status != libgps.STATUS_NO_FIX)
147         {
148             old_fix_status = libgps.STATUS_NO_FIX;
149             fix_status_changed(old_fix_status);
150         }
151
152         if (old_time != 0)
153         {
154             old_time = 0;
155             time_changed(old_time);
156         }
157
158         base.stop();
159     }
160 }
161
162 public class Position : zavai.Service
163 {
164         dynamic DBus.Object position;
165
166         public signal void position_changed(int fields, int tstamp, double lat, double lon, double alt);
167
168         public Position()
169         {
170                 Object(name: "gps.position");
171                 position = zavai.registry.sbus.get_object(
172                         "org.freesmartphone.ogpsd",
173                         "/org/freedesktop/Gypsy",
174                         "org.freedesktop.Gypsy.Position");
175         }
176
177         public void on_position_changed(dynamic DBus.Object pos, int fields, int tstamp, double lat, double lon, double alt)
178         {
179                 zavai.log.info("gps position: position changed");
180                 position_changed(fields, tstamp, lat, lon, alt);
181         }
182
183         public override void start()
184         {
185                 if (started) return;
186                 zavai.log.info("Starting GPS position tracking");
187                 gps.request("gps.position");
188                 position.PositionChanged += on_position_changed;
189                 base.start();
190         }
191
192         public override void stop()
193         {
194                 if (!started) return;
195                 zavai.log.info("Stopping GPS position tracking");
196                 position.PositionChanged -= on_position_changed;
197                 gps.release("gps.position");
198                 base.stop();
199         }
200 }
201
202 // #    def wait_for_fix(self, callback):
203 // #        status = self.gps.GetFixStatus()
204 // #        if status in [2, 3]:
205 // #            zavai.info("We already have a fix, good.")
206 // #            callback()
207 // #            return True
208 // #        else:
209 // #            zavai.info("Waiting for a fix...")
210 // #            self.waiting_for_fix = callback
211 // #            self.bus.add_signal_receiver(
212 // #                self.on_fix_status_changed, 'FixStatusChanged', 'org.freedesktop.Gypsy.Device',
213 // #                'org.freesmartphone.ogpsd', '/org/freedesktop/Gypsy')
214 // #            return False
215 // #
216 // #    def start_recording(self):
217 // #        if self.gps_monitor:
218 // #            self.gps_monitor.stop()
219 // #            self.gps_monitor = None
220 // #
221 // #        if not self.audio:
222 // #            return
223 // #
224 // #        # Sync system time
225 // #        gpstime = self.gps.gps_time.GetTime()
226 // #        subprocess.call(["date", "-s", "@%d" % gpstime])
227 // #        subprocess.call(["hwclock", "--systohc"])
228 // #
229 // #        # Compute basename for output files
230 // #        self.basename = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(gpstime))
231 // #        self.basename = os.path.join(AUDIODIR, self.basename)
232 // #
233 // #        # Start recording the GPX track
234 // #        self.gpx = GPX(self.basename)
235 // #        self.gps.track_position(self.on_position_changed)
236 // #
237 // #        # Start recording in background forking arecord
238 // #        self.audio.set_basename(self.basename)
239 // #        self.audio.start_recording()
240 // #
241
242 // Write GPX track and waypoint files
243 public class GPX : Service
244 {
245         public bool tracking {
246                 get { return trk != null; }
247                 set {}
248         }
249         public signal void tracking_changed(bool tracking);
250
251         FileStream trk = null;
252         FileStream wpt = null;
253         int wpt_seq = 1;
254         bool last_valid = false;
255         int last_fields;
256         time_t last_tstamp;
257         double last_lat;
258         double last_lon;
259         double last_alt;
260
261         public GPX()
262         {
263                 Object(name: "gps.gpx");
264         }
265
266         public override void start()
267         {       
268                 if (!started)
269                 {
270                         log.info("Starting GPX trace subsystem");
271                         position.request("gps.gpx");
272                         position.position_changed += on_position_changed;
273                         base.start();
274                 }
275         }
276
277         public override void stop()
278         {
279                 if (started)
280                 {
281                         log.info("Stopping GPX trace subsystem");
282                         position.release("gps.gpx");
283                         position.position_changed -= on_position_changed;
284                         stop_track();
285                         base.stop();
286                 }
287         }
288
289         public void on_position_changed(Position pos, int fields, int tstamp, double lat, double lon, double alt)
290         {
291                 last_fields = fields;
292                 last_tstamp = tstamp;
293                 last_lat = lat;
294                 last_lon = lon;
295                 last_alt = alt;
296                 last_valid = true;
297                 trackpoint();
298         }
299
300         public void start_track(time_t tstamp = 0, string? basename = null)
301         {
302                 string fname;
303                 if (basename != null)
304                         fname = basename;
305                 else
306                 {
307                         time_t now = tstamp == 0 ? time_t() : tstamp;
308
309                         // Compute basename for output files
310                         var t = Time.local(now);
311                         char[] res = new char[25];
312                         t.strftime(res, "%Y-%m-%d-%H-%M-%S");
313                         fname = zavai.config.homedir + "/" + (string)res;
314                 }
315  
316                 trk = FileStream.open(fname + "-trk.gpx", "wt");
317                 trk.puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
318                 trk.puts(" <gpx");
319                 trk.puts("     version=\"1.0\"");
320                 trk.printf("     creator=\"zavai %s\"\n", zavai.config.version);
321                 trk.puts("     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
322                 trk.puts("     xmlns=\"http://www.topografix.com/GPX/1/0\"");
323                 trk.puts("     xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">");
324                 trk.puts("   <trk>");
325                 trk.puts("     <trkseg>");
326  
327                 wpt = FileStream.open(fname + "-wpt.gpx", "wt");
328                 wpt.puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
329                 wpt.puts(" <gpx");
330                 wpt.puts("     version=\"1.0\"");
331                 wpt.printf("     creator=\"zavai %s\"", zavai.config.version);
332                 wpt.puts("     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
333                 wpt.puts("     xmlns=\"http://www.topografix.com/GPX/1/0\"");
334                 wpt.puts("     xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">");
335  
336                 wpt_seq = 1;
337                 tracking_changed(true);
338         }
339  
340         public void stop_track()
341         {
342                 if (trk != null)
343                 {
344                         trk.puts("</trkseg></trk></gpx>");
345                         trk.flush();
346                         trk = null;
347                 }
348                 if (wpt != null)
349                 {
350                         wpt.puts("</gpx>");
351                         wpt.flush();
352                         wpt = null;
353                 }
354                 last_valid = false;
355                 tracking_changed(false);
356         }
357
358         // Mark a track point
359         public void trackpoint()
360         {
361                 if (!last_valid) return;
362                 if (trk == null)
363                         start_track(last_tstamp);
364
365                 trk.printf("<trkpt lat=\"%f\" lon=\"%f\">\n", last_lat, last_lon);
366                 var t = Time.local(last_tstamp);
367                 char[] ts = new char[25];
368                 t.strftime(ts, "%Y-%m-%dT%H:%M:%SZ");
369                 trk.printf("  <time>%s</time>\n", (string)ts);
370                 trk.printf("  <ele>%f</ele>\n", last_alt);
371                 // if course is not None: print >>self.trk, "    <course>%f</course>" % course
372                 // if speed is not None: print >>self.trk, "    <speed>%f</speed>" % speed
373                 // if fix is not None: print >>self.trk, "    <fix>%f</fix>" % fix
374                 // if hdop is not None: print >>self.trk, "    <hdop>%f</hdop>" % hdop
375                 trk.puts("</trkpt>");
376         }
377
378         // Mark a waypoint
379         public void waypoint(string? name = null)
380         {
381                 if (!last_valid) return;
382                 if (wpt == null)
383                         start_track(last_tstamp);
384
385                 string wptname;
386                 if (name == null)
387                 {
388                         wptname = "wpt_%d".printf(wpt_seq);
389                         wpt_seq += 1;
390                 } else {
391                         wptname = name;
392                 }
393
394                 wpt.printf("<wpt lat=\"%f\" lon=\"%f\">\n", last_lat, last_lon);
395                 wpt.printf("  <name>%s</name>\n", wptname);
396                 var t = Time.local(last_tstamp);
397                 char[] ts = new char[25];
398                 t.strftime(ts, "%Y-%m-%dT%H:%M:%SZ");
399                 wpt.printf("  <time>%s</time>\n", (string)ts);
400                 wpt.printf("  <ele>%f</ele>\n", last_alt);
401                 wpt.puts("</wpt>");
402         }
403 }
404
405 public zavai.gps.GPS gps = null;
406 public zavai.gps.Position position = null;
407 public zavai.gps.GPX gpx = null;
408
409 public void init()
410 {
411     gps = new GPS();
412     position = new Position();
413     gpx = new GPX();
414
415 }
416
417 }
418 }