No need to fullscreen: matchbox does it for us
[gregoa/zavai.git] / zavai / gps.py
index 22e3a6dc86f030c8ea71c6f6193ad18eb56bff2a..47af68fb9f76e6b7970c3cba824be53e12ffa571 100755 (executable)
 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
 #import sys
-#import os
+import os
 import os.path
-#import time
-#import struct
-#import signal
-#import optparse
-#import dbus
-#import dbus.mainloop.glib
-#import gobject
+import time
+import dbus
 import zavai
 
 class GPSMonitor():
@@ -69,9 +64,11 @@ class GPSMonitor():
         self.debug_update()
 
     def on_satellites_changed(self, satellites):
-        self.gps_debug.request()
+        zavai.info("gps monitor: satellites changed")
+        self.debug_request()
 
     def on_ubxdebug_packet(self, clid, length, data):
+        zavai.info("gps monitor: UBX debug packet")
         for c in self.callbacks:
             c(clid, length, data)
 
@@ -109,56 +106,42 @@ class GPSMonitor():
         if not self.callbacks:
             self._stop_listening()
 
-#        # In zhone it is cbUBXDebugPacket
-#        #if clid == "NAV-STATUS" and data:
-#        #    i = ["%s: %d" % (k, data[0][k]) for k in sorted(data[0].keys())]
-#        #    zavai.info("Status:", " ".join(i))
-#        ##    if data[0]['TTFF']:
-#        ##        zavai.info("TTFF: %f", data[0]['TTFF']/1000.0)
-#        if clid == "NAV-SVINFO":
-#            self.print_ubx_sat_data(data[1:])
-#        #else:
-#        #    zavai.info("gps got ubxdebug packet", clid)
-#        #    zavai.info("DATA:", data)
-#
-#    def print_ubx_sat_data(self, ubxinfo):
-#        zavai.info("CH ID SN ELE AZI Used Diff Alm Eph Bad Status")
-#        for sv in ubxinfo:
-#            if sv["CNO"] == 0: continue
-#            svid = sv["SVID"]
-#            used = sv["Flags"] & 0x01
-#            diff = sv["Flags"] & 0x02
-#            almoreph = sv["Flags"] & 0x04
-#            eph = sv["Flags"] & 0x08
-#            bad = sv["Flags"] & 0x10
-#            qi = ("%i: " % sv["QI"]) + {
-#                0: "idle",
-#                1: "searching",
-#                2: "signal acquired",
-#                3: "signal unusable",
-#                4: "code lock",
-#                5: "code&carrier lock",
-#                6: "code&carrier lock",
-#                7: "receiving data"
-#            }[sv["QI"]]
-#            zavai.info("%2d %2d %2d %3d %3d %s %s %s %s %s %s" % (
-#                sv["chn"], sv["SVID"],
-#                sv["CNO"], sv["Elev"], sv["Azim"],
-#                used and "used" or "----",
-#                diff and "diff" or "----",
-#                almoreph and "alm" or "---",
-#                eph and "eph" or "---",
-#                bad and "bad" or "---",
-#                qi))
-#
-#    def print_sat_data(self, satellites):
-#        for sat in satellites:
-#            if sat[4] == 0: continue
-#            zavai.info("PRN %u" % sat[0],
-#                 sat[1] and "used" or "unused",
-#                 "el %u" % sat[2],
-#                 "az %u" % sat[3],
-#                 "snr %u" % sat[4])
+class GPSPosition():
+    def __init__(self, gps):
+        self.gps = gps
+        self.callbacks = set()
+
+    def on_position_changed(self, fields, tstamp, lat, lon, alt):
+        zavai.info("gps position: position changed")
+        for c in self.callbacks:
+            c(fields, tstamp, lat, lon, alt)
+
+    def _start_listening(self):
+        self.gps.request(self)
+        self.gps.bus.add_signal_receiver(
+            self.on_position_changed, 'PositionChanged', 'org.freedesktop.Gypsy.Position',
+            'org.freesmartphone.ogpsd', '/org/freedesktop/Gypsy')
+
+    def _stop_listening(self):
+        self.gps.bus.remove_signal_receiver(
+            self.on_position_changed, 'PositionChanged', 'org.freedesktop.Gypsy.Position',
+            'org.freesmartphone.ogpsd', '/org/freedesktop/Gypsy')
+        self.gps.release(self)
+
+    def connect(self, callback):
+        "Send position changed messages to the given callback"
+        do_start = not self.callbacks
+        self.callbacks.add(callback)
+        if do_start:
+            self._start_listening()
+
+    def disconnect(self, callback):
+        "Stop sending position changed messages to the given callback"
+        if not self.callbacks: return
+        self.callbacks.discard(callback)
+        if not self.callbacks:
+            self._stop_listening()
+
 
 # For a list of dbus services, look in /etc/dbus-1/system.d/
 class GPS(zavai.Resource):
@@ -177,6 +160,7 @@ class GPS(zavai.Resource):
         self.gps_ubx = dbus.Interface(gps, 'org.freesmartphone.GPS.UBX')
 
         self.monitor = GPSMonitor(self)
+        self.position = GPSPosition(self)
 
         self.requestors = set()
 
@@ -229,23 +213,90 @@ class GPS(zavai.Resource):
 #            self.waiting_for_fix()
 #            self.waiting_for_fix = None
 #
-#    def track_position(self, callback):
-#        self.bus.add_signal_receiver(
-#            callback, 'PositionChanged', 'org.freedesktop.Gypsy.Position',
-#            'org.freesmartphone.ogpsd', '/org/freedesktop/Gypsy')
 
+#    def start_recording(self):
+#        if self.gps_monitor:
+#            self.gps_monitor.stop()
+#            self.gps_monitor = None
+#
+#        if not self.audio:
+#            return
+#
+#        # Sync system time
+#        gpstime = self.gps.gps_time.GetTime()
+#        subprocess.call(["date", "-s", "@%d" % gpstime])
+#        subprocess.call(["hwclock", "--systohc"])
+#
+#        # Compute basename for output files
+#        self.basename = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(gpstime))
+#        self.basename = os.path.join(AUDIODIR, self.basename)
+#
+#        # Start recording the GPX track
+#        self.gpx = GPX(self.basename)
+#        self.gps.track_position(self.on_position_changed)
+#
+#        # Start recording in background forking arecord
+#        self.audio.set_basename(self.basename)
+#        self.audio.start_recording()
+#
 
 class GPX(zavai.Resource):
     "Write GPX track and waypoint files"
-    def __init__(self, resource, name):
-        self.resource = resource
+    def __init__(self, registry, name):
+        self.registry = registry
         self.trk = None
         self.wpt = None
+        self.last_pos = None
+        self.requestors = set()
+        conf = registry.resource("conf")
+        self.trackdir = os.path.expanduser("~/.zavai")
+        if conf.has_section("gps"):
+            if conf.has_option("gps", "trackdir"):
+                self.trackdir = conf.get("gps", "trackdir")
+        if not os.path.isdir(self.trackdir):
+            zavai.info("Creating directory", self.trackdir)
+            os.makedirs(self.trackdir)
+        self.activity_monitors = set()
+
+    def add_activity_monitor(self, cb):
+        self.activity_monitors.add(cb)
+        cb(self, self.last_pos is not None)
+
+    def del_activity_monitor(self, cb):
+        self.activity_monitors.discard(cb)
+
+    def notify_activity_monitors(self):
+        for mon in self.activity_monitors:
+            mon(self, self.last_pos is not None)
+
+    def request(self, tag):
+        "Request the GPX trace to be taken"
+        do_start = not self.requestors
+        self.requestors.add(tag)
+        if do_start:
+            zavai.info("Starting GPX trace subsystem")
+            gps = self.registry.resource("gps")
+            gps.position.connect(self.on_position_changed)
+
+    def release(self, tag):
+        "Release a GPX trace request"
+        if not self.requestors: return
+        self.requestors.discard(tag)
+        if not self.requestors:
+            zavai.info("Stopping GPX trace subsystem")
+            gps = self.registry.resource("gps")
+            gps.position.disconnect(self.on_position_changed)
+            self.stop()
+
+    def on_position_changed(self, fields, tstamp, lat, lon, alt):
+        self.last_pos = (fields, tstamp, lat, lon, alt)
+        self.trackpoint()
 
     def start(basename = None, tstamp = None):
         if basename is None:
-            # compute it from GPS
-            pass
+            # Compute basename for output files
+            basename = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(tstamp))
+            basename = os.path.join(self.trackdir, self.basename)
 
         self.trk = open(basename + "-trk.gpx", "wt")
         print >>self.trk, """<?xml version="1.0" encoding="UTF-8"?>
@@ -268,6 +319,7 @@ class GPX(zavai.Resource):
     xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd">""" % VERSION
 
         self.wpt_seq = 1;
+        self.notify_activity_monitors()
 
     def stop(self):
         if self.trk is not None:
@@ -278,35 +330,51 @@ class GPX(zavai.Resource):
             print >>self.wpt, "</gpx>"
             self.wpt.close()
             self.wpt = None
+        self.last_pos = None
+        self.notify_activity_monitors()
 
     def shutdown(self):
         self.stop()
 
-    def waypoint(self, tstamp, lat, lon, ele = None, name = None):
+    def trackpoint(self):
+        "Mark a track point"
+        if self.last_pos is None:
+            return
+
+        fields, tstamp, lat, lon, ele = self.last_pos
+
+        if not self.trk:
+            self.start(tstamp)
+
+        print >>self.trk, """<trkpt lat="%f" lon="%f">
+  <time>%s</time>
+  <ele>%f</ele>""" % (lat, lon, time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(tstamp)), ele)
+        #if course is not None: print >>self.trk, "    <course>%f</course>" % course
+        #if speed is not None: print >>self.trk, "    <speed>%f</speed>" % speed
+        #if fix is not None: print >>self.trk, "    <fix>%f</fix>" % fix
+        #if hdop is not None: print >>self.trk, "    <hdop>%f</hdop>" % hdop
+        print >>self.trk, "</trkpt>"
+
+    def waypoint(self, name = None):
         "Mark a waypoint"
+        if self.last_pos is None:
+            return
+
+        fields, tstamp, lat, lon, ele = self.last_pos
+
+        if not self.wpt:
+            self.start(tstamp)
+
         if name is None:
             name = "wpt_%d" % self.wpt_seq
             self.wpt_seq += 1
 
         print >>self.wpt, """<wpt lat="%f" lon="%f">
     <name>%s</name>
-    <time>%s</time>""" % (
-            lat, lon, name, time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(tstamp)))
-        if ele is not None:
-            print >>self.wpt, "    <ele>%f</ele>""" % ele
-        print >>self.wpt, "</wpt>"
-
-    def trackpoint(self, tstamp, lat, lon, ele = None, course = None, speed = None, fix = None, hdop = None):
-        print >>self.trk, """<trkpt lat="%f" lon="%f">
-  <time>%s</time>""" % (lat, lon, time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(tstamp)))
-        if ele is not None: print >>self.trk, "    <ele>%f</ele>" % ele
-        if course is not None: print >>self.trk, "    <course>%f</course>" % course
-        if speed is not None: print >>self.trk, "    <speed>%f</speed>" % speed
-        if fix is not None: print >>self.trk, "    <fix>%f</fix>" % fix
-        if hdop is not None: print >>self.trk, "    <hdop>%f</hdop>" % hdop
-        print >>self.trk, "</trkpt>"
-
-
+    <time>%s</time>
+    <ele>%f</ele>
+</wpt>""" % (
+            lat, lon, name, time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime(tstamp)), ele)
 
 
 #    def record(self):
@@ -323,35 +391,6 @@ class GPX(zavai.Resource):
 #        self.gps_monitor = GPSMonitor(self.gps)
 #        self.gps_monitor.start()
 #
-#    def start_recording(self):
-#        if self.gps_monitor:
-#            self.gps_monitor.stop()
-#            self.gps_monitor = None
-#
-#        if not self.audio:
-#            return
-#
-#        # Sync system time
-#        gpstime = self.gps.gps_time.GetTime()
-#        subprocess.call(["date", "-s", "@%d" % gpstime])
-#        subprocess.call(["hwclock", "--systohc"])
-#
-#        # Compute basename for output files
-#        self.basename = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(gpstime))
-#        self.basename = os.path.join(AUDIODIR, self.basename)
-#
-#        # Start recording the GPX track
-#        self.gpx = GPX(self.basename)
-#        self.gps.track_position(self.on_position_changed)
-#
-#        # Start recording in background forking arecord
-#        self.audio.set_basename(self.basename)
-#        self.audio.start_recording()
-#
-#    def on_position_changed(self, fields, tstamp, lat, lon, alt):
-#        self.last_pos = (fields, tstamp, lat, lon, alt)
-#        if self.gpx:
-#            self.gpx.trackpoint(tstamp, lat, lon, alt)
 #
 #    def make_waypoint(self):
 #        if self.gpx is None: