From 839596a4b2e3d6891d0f5fac2067aeb30707bc3c Mon Sep 17 00:00:00 2001 From: Nick Foster Date: Sat, 23 Oct 2010 16:05:08 -0700 Subject: [PATCH] Derp. Forgot to git-add the KML parser. Also added an LPF to the main app. Some other cleanups in prep for release. --- AUTHORS | 6 ++ src/lib/modes_parity.cc | 3 + src/python/modes_kml.py | 148 ++++++++++++++++++++++++++++++++++++++++ src/python/uhd_modes.py | 27 ++++++-- 4 files changed, 177 insertions(+), 7 deletions(-) create mode 100644 src/python/modes_kml.py diff --git a/AUTHORS b/AUTHORS index ee4560a..c59dafe 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,7 @@ +Mode S receiver: +Nick Foster + +Parts of the ECC algorithm are from Eric Cottrell's gr-air program. + +gr-howto-write-a-block: Eric Blossom diff --git a/src/lib/modes_parity.cc b/src/lib/modes_parity.cc index 66d5098..cd3c0a0 100644 --- a/src/lib/modes_parity.cc +++ b/src/lib/modes_parity.cc @@ -181,6 +181,9 @@ bruteResultTypeDef modes_ec_brute(modes_packet &err_packet) //here we basically crib EC's air_ms_ec_brute algorithm, because wherever he got it, it's perfect, and that comparison thing is fast to boot. //we assume that the syndrome result has already been calculated //how many bits shall we attempt to flip? let's say a max of 8 bits, to start. remember we're only going after long packets here. + + //well, in practice, you almost never successfully recover a packet with more than 4 LCBs. so let's put the limit there to save wasting CPU time + //on hopeless packets. //want to speed things up? instead of going through the "search codes" in numeric order, let's find a way to order them probablistically. //that is, right now, EC's algorithm uses a "search order" which starts with ALL possible low-confidence bits flipped, and goes down counting in binary. diff --git a/src/python/modes_kml.py b/src/python/modes_kml.py new file mode 100644 index 0000000..7068bf4 --- /dev/null +++ b/src/python/modes_kml.py @@ -0,0 +1,148 @@ +# +# Copyright 2010 Nick Foster +# +# This file is part of gr-air-modes +# +# gr-air-modes is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# gr-air-modes is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with gr-air-modes; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import sqlite3 +import string, math, threading, time + +class modes_kml(threading.Thread): + def __init__(self, dbfile, filename, timeout=5): + threading.Thread.__init__(self) + self._filename = filename + self._dbfile = dbfile + self.my_coords = [37.76225, -122.44254] #there has got to be a better way of getting your coords in here + self._timeout = timeout + self.done = False + self.setDaemon(1) + self.start() + + def run(self): + while self.done is False: + self.output() + time.sleep(self._timeout) + + self.done = True + + def output(self): + self._db = sqlite3.connect(self._dbfile) + kmlstr = self.genkml() + if kmlstr is not None: + f = open(self._filename, 'w') + f.write(kmlstr) + f.close() + self._db.close() + + def draw_circle(self, center, rng): + retstr = "" + steps=30 + #so we're going to do this by computing a bearing angle based on the steps, and then compute the coordinate of a line extended from the center point to that range. + [center_lat, center_lon] = center + esquared = (1/298.257223563)*(2-(1/298.257223563)) + earth_radius_mi = 3963.19059 + + #here we figure out the circumference of the latitude ring + #which tells us how wide one line of longitude is at our latitude + lat_circ = earth_radius_mi * math.cos(center_lat) + #the circumference of the longitude ring will be equal to the circumference of the earth + + lat_rad = math.radians(center_lat) + lon_rad = math.radians(center_lon) + + tmp0 = rng / earth_radius_mi + + for i in range(0, steps+1): + bearing = i*(2*math.pi/steps) #in radians + lat_out = math.degrees(math.asin(math.sin(lat_rad)*math.cos(tmp0) + math.cos(lat_rad)*math.sin(tmp0)*math.cos(bearing))) + lon_out = center_lon + math.degrees(math.atan2(math.sin(bearing)*math.sin(tmp0)*math.cos(lat_rad), math.cos(tmp0)-math.sin(lat_rad)*math.sin(math.radians(lat_out)))) + retstr += " %.8f, %.8f, 0" % (lon_out, lat_out,) + + retstr = string.lstrip(retstr) + return retstr + + def genkml(self): + #first let's draw the static content + retstr="""\n\n\n\t\n\t\n\t""" + retstr += """\t\n\t\tRange rings\n\t\t0""" + + for rng in [100, 200, 300]: + retstr += """\n\t\t\n\t\t\t%inm\n\t\t\t#rangering\n\t\t\t\n\t\t\t\t%s\n\t\t\t\n\t\t""" % (rng, self.draw_circle(self.my_coords, rng),) + + retstr += """\t\n\t\n\t\tAircraft locations\n\t\t0""" + + #read the database and add KML + q = "select distinct icao from positions where seen > datetime('now', '-5 minute')" + c = self._db.cursor() + c.execute(q) + icaolist = c.fetchall() + #now we have a list icaolist of all ICAOs seen in the last 5 minutes + + for icao in icaolist: + #print "ICAO: %x" % icao + q = "select * from positions where icao=%i and seen > datetime('now', '-2 hour') ORDER BY seen DESC" % icao + c.execute(q) + track = c.fetchall() + #print "Track length: %i" % len(track) + if len(track) != 0: + lat = track[0][3] + if lat is None: lat = 0 + lon = track[0][4] + if lon is None: lon = 0 + alt = track[0][2] + if alt is None: alt = 0 + + metric_alt = alt * 0.3048 #google earth takes meters, the commie bastards + + trackstr = "" + + for pos in track: + trackstr += " %f, %f, %f" % (pos[4], pos[3], pos[2]*0.3048) + + trackstr = string.lstrip(trackstr) + + #now get metadata + q = "select ident from ident where icao=%i" % icao + c.execute(q) + r = c.fetchall() + if len(r) != 0: + ident = r[0][0] + else: ident="" + #if ident is None: ident = "" + #get most recent speed/heading/vertical + q = "select seen, speed, heading, vertical from vectors where icao=%i order by seen desc limit 1" % icao + c.execute(q) + r = c.fetchall() + if len(r) != 0: + seen = r[0][0] + speed = r[0][1] + heading = r[0][2] + vertical = r[0][3] + + else: + seen = 0 + speed = 0 + heading = 0 + vertical = 0 + #now generate some KML + retstr+= "\n\t\t\n\t\t\t%s\n\t\t\t#airplane\n\t\t\t\n\t\t\t\tHeading: %i
Speed: %i
Vertical speed: %i
ICAO: %x
Last seen: %s]]>\n\t\t\t
\n\t\t\t\n\t\t\t\tabsolute\n\t\t\t\t1\n\t\t\t\t%s,%s,%i\n\t\t\t\n\t\t
" % (ident, alt, heading, speed, vertical, icao[0], seen, lon, lat, metric_alt, ) + + retstr+= "\n\t\t\n\t\t\t#track\n\t\t\t\n\t\t\t\t0\n\t\t\t\tabsolute\n\t\t\t\t%s\n\t\t\t\n\t\t" % (trackstr,) + + retstr+= '\n\t
\n
\n
' + return retstr diff --git a/src/python/uhd_modes.py b/src/python/uhd_modes.py index cab0dc0..839701e 100755 --- a/src/python/uhd_modes.py +++ b/src/python/uhd_modes.py @@ -84,21 +84,34 @@ class adsb_rx_block (gr.top_block): self.demod = gr.complex_to_mag() self.avg = gr.moving_average_ff(100, 1.0/100, 400); -# self.filtcoeffs = gr.firdes.band_reject(1, rate, -691e3, -646e3, 10e3, WIN_HANN) -# self.filter = gr.fir_filter_ccc(1, self.filtcoeffs) + + #the DBSRX especially tends to be spur-prone; the LPF keeps out the + #spur multiple that shows up at 2MHz + self.filtcoeffs = gr.firdes.low_pass(1, rate, 1.8e6, 200e3) + self.filter = gr.fir_filter_fff(1, self.filtcoeffs) self.preamble = air.modes_preamble(rate, options.threshold) self.framer = air.modes_framer(rate) self.slicer = air.modes_slicer(rate, queue) - self.connect(self.u, self.demod) - self.connect(self.demod, self.avg) - self.connect(self.demod, (self.preamble, 0)) + self.connect(self.u, self.demod, self.filter) + self.connect(self.filter, self.avg) + self.connect(self.filter, (self.preamble, 0)) self.connect(self.avg, (self.preamble, 1)) - self.connect(self.demod, (self.framer, 0)) + self.connect(self.filter, (self.framer, 0)) self.connect(self.preamble, (self.framer, 1)) - self.connect(self.demod, (self.slicer, 0)) + self.connect(self.filter, (self.slicer, 0)) self.connect(self.framer, (self.slicer, 1)) + +#use this flowgraph instead to omit the filter +# self.connect(self.u, self.demod) +# self.connect(self.demod, self.avg) +# self.connect(self.demod, (self.preamble, 0)) +# self.connect(self.avg, (self.preamble, 1)) +# self.connect(self.demod, (self.framer, 0)) +# self.connect(self.preamble, (self.framer, 1)) +# self.connect(self.demod, (self.slicer, 0)) +# self.connect(self.framer, (self.slicer, 1)) def tune(self, freq): result = self.u.set_center_freq(freq)