Code cleanup
[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                 Object(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
48     public void power_cycle(bool aggressive)
49     {
50         // "Have you tried turning it off and on again?"
51                 try {
52             usage.SetResourcePolicy("GPS", "disabled");
53                 } catch (GLib.Error e) {
54                         zavai.log.error(e.message);
55                 }
56         Thread.usleep(500000);
57         if (aggressive)
58             // Sometimes the GPS crashes because its state contains something
59             // that makes it crash, so we need to remove the saved state or it
60             // crashes again quite soon
61             FileUtils.unlink("/var/lib/freesmartphone/ogpsd.pickle");
62                 try {
63             usage.SetResourcePolicy("GPS", "auto");
64                 } catch (GLib.Error e) {
65                         zavai.log.error(e.message);
66                 }
67     }
68
69         /// Request GPS resource
70         public override void start()
71         {
72                 if (started) return;
73                 bool done = false;
74 /*              
75                 try {
76                         // First try with FSO
77                         usage.RequestResource("GPS");
78                         done = true;
79                 } catch (GLib.Error e) {
80                         zavai.log.error("Requesting resource GPS: " + e.message);
81                 }
82                 if (!done)
83                 {
84                         try {
85                                 // Then make it work for fil
86                                 // TODO: switch fil to om
87                                 zavai.app.run_script("om-device start gps");
88                                 done = true;
89                         } catch (Error e) {
90                                 zavai.log.error("Running om-device start gps: " + e.message);
91                         }
92                 }
93 */
94                 if (!done)
95                 {
96                         try {
97                                 // Then run our own script
98                                 zavai.app.run_script(zavai.config.homedir + "/gps start");
99                                 done = true;
100                         } catch (Error e) {
101                                 zavai.log.error("Running " + zavai.config.homedir + "/gps on: " + e.message);
102                         }
103                 }
104                 if (done)
105                         zavai.log.info("GPS turned on");
106                 else
107                         zavai.log.error("Could not turn on GPS");
108                 base.start();
109         }
110
111         // Release usage of GPS
112         public override void stop()
113         {
114                 if (!started) return;
115                 bool done = false;
116 /*
117                 try {
118                         usage.ReleaseResource("GPS");
119                         done = true;
120                 } catch (GLib.Error e) {
121                         zavai.log.error("Releasing resource GPS: " + e.message);
122                 }
123                 if (!done)
124                 {
125                         try {
126                                 zavai.app.run_script("om-device stop gps");
127                                 done = true;
128                         } catch (Error e) {
129                                 zavai.log.error("Running device stop gps: " + e.message);
130                         }
131                 }
132 */
133                 if (!done)
134                 {
135                         try {
136                                 zavai.app.run_script(zavai.config.homedir + "/gps stop");
137                                 done = true;
138                         } catch (Error e) {
139                                 zavai.log.error("Running device stop gps: " + e.message);
140                         }
141                 }
142                 if (done)
143                         zavai.log.info("GPS turned off");
144                 else
145                         zavai.log.error("Could not turn off GPS");
146                 base.stop();
147         }
148 }
149
150 public class Position : zavai.Service
151 {
152         dynamic DBus.Object position;
153
154         public signal void position_changed(int fields, int tstamp, double lat, double lon, double alt);
155
156         public Position()
157         {
158                 Object(name: "gps.position");
159                 position = zavai.registry.sbus.get_object(
160                         "org.freesmartphone.ogpsd",
161                         "/org/freedesktop/Gypsy",
162                         "org.freedesktop.Gypsy.Position");
163         }
164
165         public void on_position_changed(dynamic DBus.Object pos, int fields, int tstamp, double lat, double lon, double alt)
166         {
167                 zavai.log.info("gps position: position changed");
168                 position_changed(fields, tstamp, lat, lon, alt);
169         }
170
171         public override void start()
172         {
173                 if (started) return;
174                 zavai.log.info("Starting GPS position tracking");
175                 gps.request("gps.position");
176                 position.PositionChanged += on_position_changed;
177                 base.start();
178         }
179
180         public override void stop()
181         {
182                 if (!started) return;
183                 zavai.log.info("Stopping GPS position tracking");
184                 position.PositionChanged -= on_position_changed;
185                 gps.release("gps.position");
186                 base.stop();
187         }
188 }
189
190 // #    def wait_for_fix(self, callback):
191 // #        status = self.gps.GetFixStatus()
192 // #        if status in [2, 3]:
193 // #            zavai.info("We already have a fix, good.")
194 // #            callback()
195 // #            return True
196 // #        else:
197 // #            zavai.info("Waiting for a fix...")
198 // #            self.waiting_for_fix = callback
199 // #            self.bus.add_signal_receiver(
200 // #                self.on_fix_status_changed, 'FixStatusChanged', 'org.freedesktop.Gypsy.Device',
201 // #                'org.freesmartphone.ogpsd', '/org/freedesktop/Gypsy')
202 // #            return False
203 // #
204 // #    def start_recording(self):
205 // #        if self.gps_monitor:
206 // #            self.gps_monitor.stop()
207 // #            self.gps_monitor = None
208 // #
209 // #        if not self.audio:
210 // #            return
211 // #
212 // #        # Sync system time
213 // #        gpstime = self.gps.gps_time.GetTime()
214 // #        subprocess.call(["date", "-s", "@%d" % gpstime])
215 // #        subprocess.call(["hwclock", "--systohc"])
216 // #
217 // #        # Compute basename for output files
218 // #        self.basename = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(gpstime))
219 // #        self.basename = os.path.join(AUDIODIR, self.basename)
220 // #
221 // #        # Start recording the GPX track
222 // #        self.gpx = GPX(self.basename)
223 // #        self.gps.track_position(self.on_position_changed)
224 // #
225 // #        # Start recording in background forking arecord
226 // #        self.audio.set_basename(self.basename)
227 // #        self.audio.start_recording()
228 // #
229
230 // Write GPX track and waypoint files
231 public class GPX : Service
232 {
233         public bool tracking {
234                 get { return trk != null; }
235                 set {}
236         }
237         public signal void tracking_changed(bool tracking);
238
239         FileStream trk = null;
240         FileStream wpt = null;
241         int wpt_seq = 1;
242         bool last_valid = false;
243         int last_fields;
244         time_t last_tstamp;
245         double last_lat;
246         double last_lon;
247         double last_alt;
248
249         public GPX()
250         {
251                 Object(name: "gps.gpx");
252         }
253
254         public override void start()
255         {       
256                 if (!started)
257                 {
258                         log.info("Starting GPX trace subsystem");
259                         position.request("gps.gpx");
260                         position.position_changed += on_position_changed;
261                         base.start();
262                 }
263         }
264
265         public override void stop()
266         {
267                 if (started)
268                 {
269                         log.info("Stopping GPX trace subsystem");
270                         position.release("gps.gpx");
271                         position.position_changed -= on_position_changed;
272                         stop_track();
273                         base.stop();
274                 }
275         }
276
277         public void on_position_changed(Position pos, int fields, int tstamp, double lat, double lon, double alt)
278         {
279                 last_fields = fields;
280                 last_tstamp = tstamp;
281                 last_lat = lat;
282                 last_lon = lon;
283                 last_alt = alt;
284                 last_valid = true;
285                 trackpoint();
286         }
287
288         public void start_track(time_t tstamp = 0, string? basename = null)
289         {
290                 string fname;
291                 if (basename != null)
292                         fname = basename;
293                 else
294                 {
295                         time_t now = tstamp == 0 ? time_t() : tstamp;
296
297                         // Compute basename for output files
298                         var t = Time.local(now);
299                         char[] res = new char[25];
300                         t.strftime(res, "%Y-%m-%d-%H-%M-%S");
301                         fname = zavai.config.homedir + "/" + (string)res;
302                 }
303  
304                 trk = FileStream.open(fname + "-trk.gpx", "wt");
305                 trk.puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
306                 trk.puts(" <gpx");
307                 trk.puts("     version=\"1.0\"");
308                 trk.printf("     creator=\"zavai %s\"\n", zavai.config.version);
309                 trk.puts("     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
310                 trk.puts("     xmlns=\"http://www.topografix.com/GPX/1/0\"");
311                 trk.puts("     xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">");
312                 trk.puts("   <trk>");
313                 trk.puts("     <trkseg>");
314  
315                 wpt = FileStream.open(fname + "-wpt.gpx", "wt");
316                 wpt.puts("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
317                 wpt.puts(" <gpx");
318                 wpt.puts("     version=\"1.0\"");
319                 wpt.printf("     creator=\"zavai %s\"", zavai.config.version);
320                 wpt.puts("     xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"");
321                 wpt.puts("     xmlns=\"http://www.topografix.com/GPX/1/0\"");
322                 wpt.puts("     xsi:schemaLocation=\"http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd\">");
323  
324                 wpt_seq = 1;
325                 tracking_changed(true);
326         }
327  
328         public void stop_track()
329         {
330                 if (trk != null)
331                 {
332                         trk.puts("</trkseg></trk></gpx>");
333                         trk.flush();
334                         trk = null;
335                 }
336                 if (wpt != null)
337                 {
338                         wpt.puts("</gpx>");
339                         wpt.flush();
340                         wpt = null;
341                 }
342                 last_valid = false;
343                 tracking_changed(false);
344         }
345
346         // Mark a track point
347         public void trackpoint()
348         {
349                 if (!last_valid) return;
350                 if (trk == null)
351                         start_track(last_tstamp);
352
353                 trk.printf("<trkpt lat=\"%f\" lon=\"%f\">\n", last_lat, last_lon);
354                 var t = Time.local(last_tstamp);
355                 char[] ts = new char[25];
356                 t.strftime(ts, "%Y-%m-%dT%H:%M:%SZ");
357                 trk.printf("  <time>%s</time>\n", (string)ts);
358                 trk.printf("  <ele>%f</ele>\n", last_alt);
359                 // if course is not None: print >>self.trk, "    <course>%f</course>" % course
360                 // if speed is not None: print >>self.trk, "    <speed>%f</speed>" % speed
361                 // if fix is not None: print >>self.trk, "    <fix>%f</fix>" % fix
362                 // if hdop is not None: print >>self.trk, "    <hdop>%f</hdop>" % hdop
363                 trk.puts("</trkpt>");
364         }
365
366         // Mark a waypoint
367         public void waypoint(string? name = null)
368         {
369                 if (!last_valid) return;
370                 if (wpt == null)
371                         start_track(last_tstamp);
372
373                 string wptname;
374                 if (name == null)
375                 {
376                         wptname = "wpt_%d".printf(wpt_seq);
377                         wpt_seq += 1;
378                 } else {
379                         wptname = name;
380                 }
381
382                 wpt.printf("<wpt lat=\"%f\" lon=\"%f\">\n", last_lat, last_lon);
383                 wpt.printf("  <name>%s</name>\n", wptname);
384                 var t = Time.local(last_tstamp);
385                 char[] ts = new char[25];
386                 t.strftime(ts, "%Y-%m-%dT%H:%M:%SZ");
387                 wpt.printf("  <time>%s</time>\n", (string)ts);
388                 wpt.printf("  <ele>%f</ele>\n", last_alt);
389                 wpt.puts("</wpt>");
390         }
391 }
392
393 public zavai.gps.GPS gps = null;
394 public zavai.gps.Position position = null;
395 public zavai.gps.GPX gpx = null;
396
397 public void init()
398 {
399         gps = new GPS();
400         position = new Position();
401         gpx = new GPX();
402
403         zavai.registry.register_service(gps);
404         zavai.registry.register_service(position);
405         zavai.registry.register_service(gpx);
406 }
407
408 }
409 }