diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f6c77b..c5c5373 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,7 +22,9 @@ # Project setup ######################################################################## cmake_minimum_required(VERSION 2.6) -project(gr-air-modes CXX) +project(gr-gr-air-modes CXX) +set(gr-gr-air-modes_VERSION_MAJOR 0) +set(gr-gr-air-modes_VERSION_MINOR 0) enable_testing() #select the release build type by default to get optimization flags diff --git a/cmake/Modules/FindGnuradioCore.cmake b/cmake/Modules/FindGnuradioCore.cmake index 34e85db..3773588 100644 --- a/cmake/Modules/FindGnuradioCore.cmake +++ b/cmake/Modules/FindGnuradioCore.cmake @@ -1,11 +1,11 @@ INCLUDE(FindPkgConfig) -PKG_CHECK_MODULES(PC_GNURADIO_CORE gnuradio-core QUIET) +PKG_CHECK_MODULES(PC_GNURADIO_CORE gnuradio-core) FIND_PATH( GNURADIO_CORE_INCLUDE_DIRS NAMES gr_random.h HINTS $ENV{GNURADIO_CORE_DIR}/include/gnuradio - ${PC_GNURADIO_CORE_INCLUDE_DIR} + ${PC_GNURADIO_CORE_INCLUDEDIR} PATHS /usr/local/include/gnuradio /usr/include/gnuradio ) diff --git a/cmake/Modules/FindGruel.cmake b/cmake/Modules/FindGruel.cmake index 190c2e3..c7a10a4 100644 --- a/cmake/Modules/FindGruel.cmake +++ b/cmake/Modules/FindGruel.cmake @@ -1,11 +1,11 @@ INCLUDE(FindPkgConfig) -PKG_CHECK_MODULES(PC_GRUEL gnuradio-core QUIET) +PKG_CHECK_MODULES(PC_GRUEL gnuradio-core) FIND_PATH( GRUEL_INCLUDE_DIRS NAMES gruel/attributes.h HINTS $ENV{GRUEL_DIR}/include - ${PC_GRUEL_INCLUDE_DIR} + ${PC_GRUEL_INCLUDEDIR} PATHS /usr/local/include /usr/include ) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 5a9504f..9ed79ae 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -29,6 +29,8 @@ add_library(air_modes SHARED ) target_link_libraries(air_modes ${Boost_LIBRARIES} ${GRUEL_LIBRARIES} ${GNURADIO_CORE_LIBRARIES}) set_target_properties(air_modes PROPERTIES DEFINE_SYMBOL "AIR_MODES_EXPORTS") +set_target_properties(air_modes PROPERTIES SOVERSION "${gr-gr-air-modes_VERSION_MAJOR}") +set_target_properties(air_modes PROPERTIES VERSION "${gr-gr-air-modes_VERSION_MAJOR}.${gr-gr-air-modes_VERSION_MINOR}") ######################################################################## # Install built library files diff --git a/lib/air_modes_preamble.cc b/lib/air_modes_preamble.cc index 33dd033..157707a 100644 --- a/lib/air_modes_preamble.cc +++ b/lib/air_modes_preamble.cc @@ -53,7 +53,7 @@ air_modes_preamble::air_modes_preamble(int channel_rate, float threshold_db) : str << name() << unique_id(); d_me = pmt::pmt_string_to_symbol(str.str()); d_key = pmt::pmt_string_to_symbol("preamble_found"); - set_history(d_check_width); + set_history(d_samples_per_symbol); } static void integrate_and_dump(float *out, const float *in, int chips, int samps_per_chip) { @@ -101,7 +101,13 @@ int air_modes_preamble::general_work(int noutput_items, { const float *in = (const float *) input_items[0]; const float *inavg = (const float *) input_items[1]; - const int ninputs = std::min(ninput_items[0], ninput_items[1]); //just in case + + int mininputs = std::min(ninput_items[0], ninput_items[1]); //they should be matched but let's be safe + //round number of input samples down to nearest d_samples_per_chip + //we also subtract off d_samples_per_chip to allow the bit center finder some leeway + const int ninputs = std::max(mininputs - (mininputs % d_samples_per_chip) - d_samples_per_chip, 0); + if (ninputs <= 0) { consume_each(0); return 0; } + float *out = (float *) output_items[0]; if(0) std::cout << "Preamble called with " << ninputs << " samples" << std::endl; @@ -132,15 +138,18 @@ int air_modes_preamble::general_work(int noutput_items, //get a more accurate bit center by finding the correlation peak across all four preamble bits bool late, early; + int how_late = 0; do { double now_corr = correlate_preamble(in+i, d_samples_per_chip); double late_corr = correlate_preamble(in+i+1, d_samples_per_chip); double early_corr = correlate_preamble(in+i-1, d_samples_per_chip); late = (late_corr > now_corr); //early = (early_corr > now_corr); - if(late) i++; + if(late) { i++; how_late++; } //if(early && i>0) { std::cout << "EARLY " << i << std::endl; i--; } - } while(late);// xor early); + } while(late and how_late < d_samples_per_chip);// xor early); + + if(0) std::cout << "We were " << how_late << " samples late" << std::endl; //now check to see that the non-peak symbols in the preamble //are below the peaks by threshold dB @@ -160,6 +169,7 @@ int air_modes_preamble::general_work(int noutput_items, //be sure we've got enough room in the input buffer to copy out a whole packet if(ninputs-i < 240*d_samples_per_chip) { consume_each(std::max(i-1,0)); + if(0) std::cout << "Preamble consumed " << std::max(i-1,0) << ", returned 0 (no room)" << std::endl; return 0; } @@ -190,7 +200,8 @@ int air_modes_preamble::general_work(int noutput_items, //std::cout << "PREAMBLE" << std::endl; //produce only one output per work call -- TODO this should probably change - if(0) std::cout << "Preamble consumed " << i+240*d_samples_per_chip << ", returned 240" << std::endl; + if(0) std::cout << "Preamble consumed " << i+240*d_samples_per_chip << "with i=" << i << ", returned 240" << std::endl; + consume_each(i+240*d_samples_per_chip); return 240; } diff --git a/python/kml.py b/python/kml.py index 3182113..521630d 100644 --- a/python/kml.py +++ b/python/kml.py @@ -93,14 +93,14 @@ class output_kml(threading.Thread): #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) + self.locked_execute(c, 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) + self.locked_execute(c, q) track = c.fetchall() #print "Track length: %i" % len(track) if len(track) != 0: @@ -128,7 +128,7 @@ class output_kml(threading.Thread): #now get metadata q = "select ident from ident where icao=%i" % icao - c.execute(q) + self.locked_execute(c, q) r = c.fetchall() if len(r) != 0: ident = r[0][0] @@ -136,7 +136,7 @@ class output_kml(threading.Thread): #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) + self.locked_execute(c, q) r = c.fetchall() if len(r) != 0: seen = r[0][0] diff --git a/python/msprint.py b/python/msprint.py index 169337f..d613fa4 100644 --- a/python/msprint.py +++ b/python/msprint.py @@ -180,7 +180,7 @@ class output_print(air_modes.parse): retstr = "Type 17 BDS6,2 (emergency) from %x type %s" % (icao24, emerg_str) else: - retstr = "Type 17 subtype %i from %x not implemented" % (subtype, icao24) + retstr = "Type 17 with FTC=%i from %x not implemented" % (data["ftc"], icao24) return retstr diff --git a/python/sbs1.py b/python/sbs1.py index 0f9638d..480eff5 100644 --- a/python/sbs1.py +++ b/python/sbs1.py @@ -131,9 +131,9 @@ class output_sbs1(air_modes.parse): aircraft_id = self.get_aircraft_id(ecc) retstr = "MSG,7,0,%i,%06X,%i,%s,%s,%s,%s,,%s,,,,,,,,,," % (aircraft_id, ecc, aircraft_id+100, datestr, timestr, datestr, timestr, altitude) if vs: - retstr += "1\n" + retstr += "1\r\n" else: - retstr += "0\n" + retstr += "0\r\n" return retstr def pp4(self, shortdata, ecc): @@ -141,7 +141,7 @@ class output_sbs1(air_modes.parse): [fs, dr, um, altitude] = self.parse4(shortdata) aircraft_id = self.get_aircraft_id(ecc) retstr = "MSG,5,0,%i,%06X,%i,%s,%s,%s,%s,,%s,,,,,,," % (aircraft_id, ecc, aircraft_id+100, datestr, timestr, datestr, timestr, altitude) - return retstr + self.decode_fs(fs) + "\n" + return retstr + self.decode_fs(fs) + "\r\n" def pp5(self, shortdata, ecc): # I'm not sure what to do with the identiifcation shortdata & 0x1FFF @@ -149,13 +149,13 @@ class output_sbs1(air_modes.parse): [fs, dr, um, ident] = self.parse5(shortdata) aircraft_id = self.get_aircraft_id(ecc) retstr = "MSG,6,0,%i,%06X,%i,%s,%s,%s,%s,,,,,,,,," % (aircraft_id, ecc, aircraft_id+100, datestr, timestr, datestr, timestr) - return retstr + self.decode_fs(fs) + "\n" + return retstr + self.decode_fs(fs) + "\r\n" def pp11(self, shortdata, ecc): [datestr, timestr] = self.current_time() [icao24, interrogator, ca] = self.parse11(shortdata, ecc) aircraft_id = self.get_aircraft_id(icao24) - return "MSG,8,0,%i,%06X,%i,%s,%s,%s,%s,,,,,,,,,,,,\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr) + return "MSG,8,0,%i,%06X,%i,%s,%s,%s,%s,,,,,,,,,,,,\r\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr) def pp17(self, data): icao24 = data["aa"] @@ -170,7 +170,7 @@ class output_sbs1(air_modes.parse): if bdsreg == 0x08: # Aircraft Identification (msg, typestring) = self.parseBDS08(data) - retstr = "MSG,1,0,%i,%06X,%i,%s,%s,%s,%s,%s,,,,,,,,,,,\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr, msg) + retstr = "MSG,1,0,%i,%06X,%i,%s,%s,%s,%s,%s,,,,,,,,,,,\r\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr, msg) elif bdsreg == 0x06: # Surface position measurement @@ -179,7 +179,7 @@ class output_sbs1(air_modes.parse): if decoded_lat is None: #no unambiguously valid position available retstr = None else: - retstr = "MSG,2,0,%i,%06X,%i,%s,%s,%s,%s,,%i,,,%.5f,%.5f,,,,0,0,0\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr, altitude, decoded_lat, decoded_lon) + retstr = "MSG,2,0,%i,%06X,%i,%s,%s,%s,%s,,%i,,,%.5f,%.5f,,,,0,0,0\r\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr, altitude, decoded_lat, decoded_lon) elif bdsreg == 0x05: # Airborne position measurements @@ -188,7 +188,7 @@ class output_sbs1(air_modes.parse): if decoded_lat is None: #no unambiguously valid position available retstr = None else: - retstr = "MSG,3,0,%i,%06X,%i,%s,%s,%s,%s,,%i,,,%.5f,%.5f,,,,0,0,0\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr, altitude, decoded_lat, decoded_lon) + retstr = "MSG,3,0,%i,%06X,%i,%s,%s,%s,%s,,%i,,,%.5f,%.5f,,,,0,0,0\r\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr, altitude, decoded_lat, decoded_lon) elif bdsreg == 0x09: # Airborne velocity measurements @@ -197,6 +197,6 @@ class output_sbs1(air_modes.parse): if subtype == 0 or subtype == 1: parser = self.parseBDS09_0 if subtype == 0 else self.parseBDS09_1 [velocity, heading, vert_spd] = parser(data) - retstr = "MSG,4,0,%i,%06X,%i,%s,%s,%s,%s,,,%.1f,%.1f,,,%i,,,,,\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr, velocity, heading, vert_spd) + retstr = "MSG,4,0,%i,%06X,%i,%s,%s,%s,%s,,,%.1f,%.1f,,,%i,,,,,\r\n" % (aircraft_id, icao24, aircraft_id+100, datestr, timestr, datestr, timestr, velocity, heading, vert_spd) return retstr diff --git a/python/sql.py b/python/sql.py index 589f30d..fb97673 100644 --- a/python/sql.py +++ b/python/sql.py @@ -19,7 +19,7 @@ # Boston, MA 02110-1301, USA. # -import time, os, sys +import time, os, sys, threading from string import split, join import air_modes import sqlite3 @@ -28,6 +28,9 @@ from air_modes.exceptions import * class output_sql(air_modes.parse): def __init__(self, mypos, filename): air_modes.parse.__init__(self, mypos) + + self._lock = threading.Lock() + #create the database self.filename = filename self.db = sqlite3.connect(filename) @@ -40,7 +43,7 @@ class output_sql(air_modes.parse): "lat" REAL, "lon" REAL );""" - c.execute(query) + self.locked_execute(c, query) query = """CREATE TABLE IF NOT EXISTS "vectors" ( "icao" INTEGER KEY NOT NULL, "seen" TEXT NOT NULL, @@ -48,12 +51,12 @@ class output_sql(air_modes.parse): "heading" REAL, "vertical" REAL );""" - c.execute(query) + self.locked_execute(c, query) query = """CREATE TABLE IF NOT EXISTS "ident" ( "icao" INTEGER PRIMARY KEY NOT NULL, "ident" TEXT NOT NULL );""" - c.execute(query) + self.locked_execute(c, query) c.close() self.db.commit() #we close the db conn now to reopen it in the output() thread context. @@ -63,6 +66,10 @@ class output_sql(air_modes.parse): def __del__(self): self.db = None + def locked_execute(self, c, query): + with self._lock: + c.execute(query) + def output(self, message): try: #we're checking to see if the db is empty, and creating the db object @@ -74,10 +81,12 @@ class output_sql(air_modes.parse): query = self.make_insert_query(message) if query is not None: - c = self.db.cursor() - c.execute(query) - c.close() - self.db.commit() #don't know if this is necessary + with self._lock: + c = self.db.cursor() + c.execute(query) + c.close() + self.db.commit() + except ADSBError: pass