138 Commits

Author SHA1 Message Date
Nick Foster
d6109d3c36 Add Doxygen stuff as it appears pybind macros require it. 2021-02-11 16:21:42 -08:00
Nick Foster
fd1771f161 Update to GR 3.9.0. WIP with plenty of untested code. 2021-02-10 20:55:39 -08:00
Jaroslav Škarvada
9c77b55c0b Switched to gnuradio-3.9
Signed-off-by: Jaroslav Škarvada <jskarvad@redhat.com>
2021-02-10 12:32:01 -08:00
Nick Foster
9e2515a566 Merge pull request #109 from jaredd/master
Fix python3 error w.r.t. modifying a list in place. Also fix SBS1 interface. Author: Jared Dulmage/github.com/jaredd
2020-05-06 09:39:40 -07:00
Jared Dulmage
6614f6ce43 Convert sbs message to bytes before sending 2020-05-05 22:56:50 -06:00
Jared Dulmage
5f7c6f57c8 Copy poslist items before deleting to avoid RuntimeError 2020-05-05 14:22:20 -06:00
Jared Dulmage
9dc3367aaf Replaced "is" with "==" to avoid SyntaxWarning 2020-05-05 14:21:40 -06:00
Nick Foster
a2f2627c54 Fix KML generation 2019-10-15 13:22:04 -07:00
Nick Foster
8d6b66f556 Default boxcar filter enabled. Reduces spurious replies. 2019-10-15 13:12:51 -07:00
Nick Foster
2b10c06e15 Fix ZMQ socket interface (used for everything) for strange queue.Queue issues. 2019-10-15 13:11:05 -07:00
Nick Foster
37f45f8a4d Fix some copypasta cruft in CMakeLists.txt 2019-10-07 04:06:47 -07:00
Nick Foster
9b17824c49 Big update to UHD 3.14, Gnuradio 3.8, Python 3.6. Not fully tested. 2019-09-17 14:13:51 -07:00
Nick Foster
0b6c383506 Merge branch 'master' of github.com:bistromath/gr-air-modes 2017-09-28 13:30:33 -07:00
Nick Foster
f6ba6da8ba Add Dockerfile. 2017-09-28 13:30:18 -07:00
Nick Foster
48c1afc3ba Merge pull request #100 from gnieboer/WinFixes
Win fixes
2017-03-14 12:32:35 -07:00
gnieboer
3b65986159 Updated GrBoost.cmake to include quoted string fix from GR 2017-03-12 15:08:18 -04:00
gnieboer
eb63b0034f Updated FindQwt to include add'l search paths 2017-03-12 15:07:44 -04:00
bistromath
719d52b6dd Merge pull request #96 from kpreid/patch-1
Remove unneeded parameter from rx_path.get_threshold.
2016-11-16 19:03:20 -08:00
Kevin Reid
f96b83cb48 Remove unneeded parameter from rx_path.get_threshold. 2016-11-16 16:32:40 -08:00
bistromath
fd277e7fb0 Merge pull request #93 from kevinluchsinger/master
fixes issue #76 (timestamps did "overflow" after 536 seconds)
2016-10-10 11:10:57 -06:00
Kevin Luchsinger
737c98bf8a fixes issue #76 (timestamps did "overflow" after 536 seconds) 2016-10-08 21:26:10 +02:00
bistromath
8cbcc676c4 Merge pull request #88 from Geoff160/master
Modified gr-air-modes/apps/modes_gui to add correct az_map problem.
2016-09-01 11:01:42 -07:00
Geoffrey Marr
d4d17bc4db Modified gr-air-modes/apps/modes_gui to add correct az_map problem. 2016-09-01 08:45:39 -06:00
Nick Foster
3bad1f5d35 Move az_map import into GUI as it doesn't belong in the module init 2016-08-05 16:19:05 -07:00
Nick Foster
c29eb6030a Fix SWIG on 16.04. Not sure how this didn't get propagated. 2016-06-05 11:38:45 -07:00
bistromath
e8c2a47278 Merge pull request #84 from devnulling/master
Add API Field, update markerwithlabel.js source
2016-05-17 15:59:24 -07:00
devnulling
0599c09198 Add API Key field 2016-05-16 22:18:52 -07:00
devnulling
49b7d87e7a Add API Key field 2016-05-16 22:18:15 -07:00
devnulling
2ffdcf6705 Add API Key field 2016-05-16 22:17:50 -07:00
bistromath
65e5bd1e2e Merge pull request #82 from devnulling/master
Add PyZMQ to required dependencies
2016-05-03 23:39:55 -07:00
devnulling
2b8451cbe2 Add PyZMQ to required dependencies 2016-05-03 21:26:43 -07:00
Nick Foster
bdfcc42b39 Fix for bug 81 (SWIG implicit module naming). Thanks to Maitland Bottoms
(bottoms@debian.org) for the patch.
2016-05-01 19:23:46 -07:00
Nick Foster
514414f6b3 Fix integer truncation issue in tag_to_timestamp(). Thanks to John Ilig
for finding it.
2015-09-03 17:39:53 -07:00
Nick Foster
e82cf9d4de Merge branch 'master' of github.com:bistromath/gr-air-modes 2015-07-10 09:29:15 -07:00
Nick Foster
2c9ef501b8 Correctly use zero offset for devices that don't issue proper timestamp
tags.
2015-07-09 13:08:00 -07:00
Nick Foster
953a7ddded Correctly return a zero timestamp tag instead of a null tag. 2015-07-09 13:03:10 -07:00
Nick Foster
c96dea7fa0 preamble: Check to see if PMT key is actually a symbol before converting
to string.
2015-07-09 12:23:52 -07:00
bistromath
d810ed75a8 Merge pull request #70 from KART35/master
Fix crash with missing gr.udp_source() in gnuradio-companion 3.7.7.1.
2015-05-26 13:31:15 -07:00
Ian Crawford
3d1b95832a Fix crash with missing gr.udp_source() in gnuradio-companion 3.7.7.1. Changed to blocks.udp_source(). 2015-05-26 13:01:58 -07:00
Nick Foster
93078a8cae I'm the worst 2015-05-08 09:08:38 -07:00
Nick Foster
d569e31a68 Fix parse error (shift on float) reported by 'engink1981'. 2015-05-07 20:47:09 -07:00
Nick Foster
f0323160a0 Fix SBS-1 ID list prune issue. Lousy fix, but it'll do for now. The
backend needs a rewrite.
2015-05-07 20:44:28 -07:00
Nick Foster
95ff2cade0 Remove spurious print. 2015-04-17 22:16:55 -07:00
Nick Foster
9bdac2a499 Use whole/fractional timestamps in the whole chain. This prevents loss
of precision when setting time to UTC.
2015-04-17 14:07:28 -07:00
Nick Foster
da15a2daf2 Check for PyZMQ at runtime instead of compile time to support
cross-compiling.
2015-01-21 12:32:39 -08:00
Nick Foster
9891907e97 Spruce up the Javascript map handler so it doesn't just wipe the map
and repopulate. No "flashing icons".
2014-09-28 15:09:07 -07:00
Nick Foster
1536dae56e Fix issue with stop/restart in modes_gui. 2014-09-28 10:43:54 -07:00
Nick Foster
48f9c2a29a Live sample rate changing for RTL devices in modes_gui 2014-09-21 19:28:44 -07:00
Nick Foster
78c5500c80 Fix GUI rate picking logic for <4Msps devices (RTL) 2014-09-21 19:18:21 -07:00
Nick Foster
6f11777724 Fix rate setting for devices with <4Msps rates (RTL). 2014-09-21 19:03:16 -07:00
Nick Foster
cc0fa1801b Fix enumeration in SBS1 list pruning. 2014-02-12 15:15:32 -08:00
Nick Foster
42bf16ffc4 Fix FlightGear and SBS1 outputs. 2014-02-07 16:59:34 -08:00
Tom Rondeau
27e0f87361 CMake fixes (force Python2, use GnuradioConfig.cmake). 2013-12-24 12:51:45 -08:00
Nick Foster
585ecf1ba6 Remove FindZeroMQ as it's causing more trouble than it's worth. Zero_Chaos, this
is my hat I'm eating.
2013-10-26 21:53:49 -07:00
Nick Foster
ca4edd8808 look for zmq.h instead of zmq.hpp, which is apparently missing in some installs. 2013-10-02 22:14:44 -04:00
Nick Foster
aa5fdd17ea Add pkgconfig searching for libzmq 2013-10-02 21:06:51 -04:00
Nick Foster
e47992d800 Actually looking for both ZMQ and PyZMQ now. 2013-09-18 08:57:44 -07:00
Nick Foster
9f522cf082 Add checking for ZMQ/PyZMQ to CMakeLists.txt 2013-09-18 08:53:00 -07:00
Nick Foster
3f86a74132 Fix FindGnuradioRuntime.cmake as per recent gr-modtool fixes. 2013-09-15 11:16:36 -07:00
Nick Foster
b3021720d6 GUI: catch ConfigParser.NoSectionError for platforms which don't throw
IOError
2013-08-21 17:57:01 -07:00
Nick Foster
b7627fa2d4 Rewrite CRC to use bytewise CRC instead of bitwise. ~8x faster. 2013-08-19 16:40:02 -07:00
Nick Foster
4d2587574a Use single gain setting for HackRF devices. This requires updated gr-osmosdr
with gain bin settings.
2013-08-09 21:00:40 -07:00
Nick Foster
ca58874861 HackRF Jawbreaker enhancements -- selectable DC blocking filter improves short
pkt decoding.
2013-08-09 15:41:26 -07:00
Nick Foster
55559086ac Small Osmosdr fixes. Still unsatisfactory w/HackRF Jawbreaker. DC block
unhelpful.
2013-08-08 22:15:24 -07:00
Nick Foster
c878d80e28 Change gain on the fly. 2013-08-05 17:06:22 -07:00
Nick Foster
a528f375f7 Huge messy reorg to move things into the GR3.7 paradigm. Mostly in effort to get
SWIG magic to work on member functions. Can change sample rate on the fly now.
2013-08-05 16:29:02 -07:00
Nick Foster
1630e67c3b Preamble subtracts average (LPF'ed) input from samples. This has the effect only
of improving the slicer threshold to exactly 0.5.
2013-08-04 22:20:15 -07:00
Nick Foster
8e1bdafc51 Re-tab air_modes_preamble.cc 2013-08-04 22:14:08 -07:00
Nick Foster
e52e4039c0 Re-tab air_modes_slicer.cc 2013-08-04 22:12:39 -07:00
Nick Foster
640b13e62d Switched to using complex_to_mag_squared vs. mag. Based on an observation that
the optimal slicer threshold is 0.5(V**2)t. Less CPU load, better performance.
Some configurations of threshold and --pmf result in slightly worse
performance. Most result in better performance.
2013-08-04 22:09:34 -07:00
Nick Foster
2c92b15a34 Broke the file source. Fixed. 2013-08-04 20:10:35 -07:00
Nick Foster
797bef13d1 Fix some parsing issues with the prefs file 2013-07-23 11:45:49 -07:00
Nick Foster
366e4d1736 Prefs file saves last GUI settings. 2013-07-23 11:37:52 -07:00
Nick Foster
96679fbd35 Fix stupid race condx the stupid way. 2013-07-22 22:14:45 -07:00
Nick Foster
fb6143596d Fix label -- is ICAO when ident not present. 2013-07-22 22:11:13 -07:00
Nick Foster
dfa62d0621 Fix label width, position, changed to blue, removed frame. 2013-07-22 21:54:37 -07:00
Nick Foster
8d3b4d4da1 Well, that was easy. Map markers have labels. Will probably change marker style. 2013-07-22 21:19:40 -07:00
Nick Foster
2869446a9a Relabel climb rate ft/s to ft/min in dashboard 2013-07-22 18:26:34 -07:00
Nick Foster
3540114c94 Merge branch 'az_map' into next 2013-07-22 18:23:35 -07:00
Nick Foster
041305fd49 Limit text box updates to 10/s 2013-07-22 18:23:05 -07:00
Nick Foster
48c55fa7f8 JSON generation for last 1 minute so it lines up w/ICAO list display. 2013-07-22 18:15:26 -07:00
Nick Foster
1eca043bac Saner scrolling behavior and range labeling. 2013-07-22 18:15:03 -07:00
Nick Foster
6e230a7d9e Azimuth map: range rings labeled, intelligent ring distance selection, scroll
wheel zooms
2013-07-22 15:51:43 -07:00
Nick Foster
a09c5add43 Set default UI tab to setup. 2013-07-18 18:43:29 -07:00
Nick Foster
498cea34b2 Better sample rate selection. 2013-07-18 18:31:10 -07:00
Nick Foster
4174658f0d Fixed GUI live print and reports/sec widget. 2013-07-18 17:46:50 -07:00
Nick Foster
b594fe2799 Mapview: added highlighting of selected aircraft 2013-07-18 09:45:09 -07:00
Nick Foster
7fef37d34d Merge branch 'gr3.7' into mapview 2013-07-17 21:46:55 -07:00
Nick Foster
2deefdf310 Remove unnecessary imports from modes_gui. 2013-07-17 21:46:25 -07:00
Nick Foster
37aa74fbe0 Merge branch 'gr3.7' into mapview
Conflicts:
	python/radio.py
2013-07-17 21:42:48 -07:00
Nick Foster
d71e6bc1e7 Interim commit. 2013-07-17 21:41:55 -07:00
Nick Foster
0ce6374656 Convert next branch to 3.7 API. Based on Johnathan Corgan's 3.7 conversion of
the master branch.
2013-07-17 18:03:45 -07:00
Nick Foster
2766107a76 Small changes to map view. WebKit won't render files w/o .htm[l] extension so using a named temp file is out. 2013-06-21 16:28:56 -07:00
Nick Foster
55cd17de67 Added support for integrated Google Maps interface via QWebView/JavaScript/JSONP. Broken due to something hairy wrt QWebView and /tmp. 2013-06-20 23:05:41 -07:00
Nick Foster
fbe3c464fb GUI working again w/new parser setup. Live print isn't working due to use of print instead of return. 2013-06-19 11:24:11 -07:00
Nick Foster
12c09ba1df Fix TCAS printing. 2013-06-18 22:03:37 -07:00
Nick Foster
a7af518653 Move parser factory decorator into parse.py. Fix multiple bugs in parse. 2013-06-18 21:49:07 -07:00
Nick Foster
302fa7203d Left a debug print in there 2013-06-18 19:09:49 -07:00
Nick Foster
e18a2e460c Fix SQL bug introduced with AC type addition 2013-06-18 19:09:09 -07:00
Nick Foster
f8f08ecd37 Flightgear plugin modified for new parser interface. 2013-06-18 19:06:30 -07:00
Nick Foster
9563972591 Moved SBS1, az_map, and SQL modules to new parser interface. Not tested. 2013-06-18 19:02:22 -07:00
Nick Foster
72ae3abf12 Forgot to add types.py (from the mlat_server branch). 2013-06-18 18:11:13 -07:00
Nick Foster
d84c0c3204 Parser works for the print case. Not quite sure this is the best way to do it, but it's better. 2013-06-18 17:47:13 -07:00
Nick Foster
a1e2297134 Progress toward rewriting the parser to be less insane. 2013-06-18 17:34:11 -07:00
Nick Foster
230356bcaa Derp bug in hacked Jawbreaker gain. 2013-06-18 15:50:06 -07:00
Nick Foster
29f8a2c1b4 Add try/catch around az_map's parsing. 2013-06-18 11:45:58 -07:00
Nick Foster
d508b39b31 Fix modes_gui. Only thing which should be nonfunc. is the reports/sec box (no thread to run it). 2013-06-10 13:37:50 -04:00
Nick Foster
bed2aa499e Re-remove the RTLSDR interpolating filter. Still need to distinguish HackRF vs. RTL-SDR sources. 2013-06-10 11:24:42 -04:00
Nick Foster
244c9105f2 Remove leftover arg from sql constructor 2013-06-10 08:52:28 -04:00
Nick Foster
1880126100 Move --tcp to radio. 2013-06-10 08:52:11 -04:00
Nick Foster
34939bba52 Whoops 2013-06-10 08:37:28 -04:00
Nick Foster
fd6ee2ce89 Change reference output so it now outputs SNR, which is much more useful. 2013-06-10 07:47:42 -04:00
Nick Foster
c0543923f6 Used wrong gain bin for HackRF source -- this is still a TODO 2013-06-08 16:53:06 -04:00
Nick Foster
1cb8c726ed Add default for --source option 2013-06-08 16:48:14 -04:00
Nick Foster
b5e3964d12 Add radio options to separate option group 2013-06-08 16:12:38 -04:00
Nick Foster
94af9fac48 Add back in the SBS1 interface 2013-06-08 16:06:42 -04:00
Nick Foster
d2a6f40bbd Clean up logic in zmq_socket and don't repeat code 2013-06-08 16:06:16 -04:00
Nick Foster
51cb2bdf46 Cleanup and remove dead/obsolete code. 2013-06-08 15:42:22 -04:00
Nick Foster
798d5e15c9 Rework options in radio.py for cleaner cmdline interface 2013-06-08 15:25:28 -04:00
Nick Foster
4bbe250f39 Publish some SQL notifications. Still TODO: issue list of new ICAOs. Might use a separate thread/publisher for that. 2013-06-05 18:30:45 -04:00
Nick Foster
cfab7123cc Non-functional cosmetic changes. 2013-06-05 18:18:18 -04:00
Nick Foster
a847f5f875 Fix introduced CPU consumption bug. 2013-06-05 17:50:25 -04:00
Nick Foster
79aee53a52 Subscribe to *all* the servers. 2013-06-05 16:05:14 -04:00
Nick Foster
f62813f039 Add getters/setters in preamble/slicer, bring them out to radio.py via pubsub. 2013-06-05 15:56:09 -04:00
Nick Foster
2ace332b89 Fix broken msgq assumption 2013-06-03 09:29:08 -04:00
Nick Foster
9dad60303a update socks in receive loop 2013-06-03 09:19:49 -04:00
Nick Foster
4fe2334b28 Don't need locks for queue inserts 2013-06-03 09:17:17 -04:00
Nick Foster
ba55d24e92 Don't use commit() on each SQL insert, it makes things terrislow. 2013-06-03 09:07:36 -04:00
Nick Foster
b71c978e27 New universal pubsub interface in zmq_socket.py. Needs more work. 2013-06-03 08:38:26 -04:00
Nick Foster
33874893b7 Better exception handling in sql.py 2013-05-30 17:17:15 -04:00
Nick Foster
4216b96262 Threading fixes for ZMQ work. Also moved radio optparse options into radio.py. 2013-05-30 00:58:03 -07:00
Nick Foster
841e2aaa04 Add checking for 0MQ library to CMake 2013-05-29 14:57:51 -07:00
Nick Foster
4d569f9112 In progress, temp commit. Have removed pubsub interface in favor of 0MQ sockets -- this gets us free message-passing across network or local host. 2013-05-29 14:18:15 -07:00
Nick Foster
db34eca30e Refactored modes_rx to use a more modular radio interface (radio.py) using a pubsub pattern to formalize the old "outputs" interface I was using. Should make it easier to reuse the radio interface. 2013-05-27 19:50:42 -07:00
Nick Foster
3e9854f337 Move Boost finding into module from Gnuradio 2013-05-07 13:05:52 -07:00
Nicholas Corgan
4e5fb40531 Some Windows fixes 2013-04-01 15:51:00 -07:00
Mattias Schäfer
f25d21f505 cpr.set_location lacked self arg 2013-01-22 08:07:46 -08:00
Stephan Ruloff
f4fbd25bb0 Add type 5 squawk ID decoding to parser, fix some SBS-1 outputs. 2013-01-08 17:25:28 -08:00
115 changed files with 5290 additions and 9433 deletions

View File

@@ -1,7 +0,0 @@
Mode S receiver:
Nick Foster <bistromath@gmail.com>
Parts of the ECC algorithm are from Eric Cottrell's gr-air program.
gr-howto-write-a-block:
Eric Blossom <eb@comsec.com>

View File

@@ -1,4 +1,4 @@
# Copyright 2011 Free Software Foundation, Inc.
# Copyright 2011,2013 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
@@ -21,12 +21,16 @@
########################################################################
# Project setup
########################################################################
cmake_minimum_required(VERSION 2.6)
project(gr-gr-air-modes CXX)
set(gr-gr-air-modes_VERSION_MAJOR 0)
set(gr-gr-air-modes_VERSION_MINOR 0)
cmake_minimum_required(VERSION 3.8)
project(gr-air_modes CXX C)
enable_testing()
#install to PyBOMBS target prefix if defined
if(DEFINED ENV{PYBOMBS_PREFIX})
set(CMAKE_INSTALL_PREFIX $ENV{PYBOMBS_PREFIX})
message(STATUS "PyBOMBS installed GNU Radio. Setting CMAKE_INSTALL_PREFIX to $ENV{PYBOMBS_PREFIX}")
endif()
#select the release build type by default to get optimization flags
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release")
@@ -34,87 +38,97 @@ if(NOT CMAKE_BUILD_TYPE)
endif(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "")
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules)
#make sure our local CMake Modules path comes first
list(INSERT CMAKE_MODULE_PATH 0 ${CMAKE_SOURCE_DIR}/cmake/Modules)
# Set the version information here
set(VERSION_MAJOR 1)
set(VERSION_API 0)
set(VERSION_ABI 0)
set(VERSION_PATCH git)
# Set cmake policies.
# This will suppress developer warnings during the cmake process that can occur
# if a newer cmake version than the minimum is used.
cmake_policy(SET CMP0011 NEW)
# Enable generation of compile_commands.json for code completion engines
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
########################################################################
# Compiler specific setup
########################################################################
if(CMAKE_COMPILER_IS_GNUCXX AND NOT WIN32)
if((CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR
CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
AND NOT WIN32)
#http://gcc.gnu.org/wiki/Visibility
add_definitions(-fvisibility=hidden)
endif()
########################################################################
# Find boost
########################################################################
if(UNIX AND EXISTS "/usr/lib64")
list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix
endif(UNIX AND EXISTS "/usr/lib64")
set(Boost_ADDITIONAL_VERSIONS
"1.35.0" "1.35" "1.36.0" "1.36" "1.37.0" "1.37" "1.38.0" "1.38" "1.39.0" "1.39"
"1.40.0" "1.40" "1.41.0" "1.41" "1.42.0" "1.42" "1.43.0" "1.43" "1.44.0" "1.44"
"1.45.0" "1.45" "1.46.0" "1.46" "1.47.0" "1.47" "1.48.0" "1.48" "1.49.0" "1.49"
"1.50.0" "1.50" "1.51.0" "1.51" "1.52.0" "1.52" "1.53.0" "1.53" "1.54.0" "1.54"
"1.55.0" "1.55" "1.56.0" "1.56" "1.57.0" "1.57" "1.58.0" "1.58" "1.59.0" "1.59"
"1.60.0" "1.60" "1.61.0" "1.61" "1.62.0" "1.62" "1.63.0" "1.63" "1.64.0" "1.64"
"1.65.0" "1.65" "1.66.0" "1.66" "1.67.0" "1.67" "1.68.0" "1.68" "1.69.0" "1.69"
)
find_package(Boost "1.35")
IF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
SET(CMAKE_CXX_STANDARD 11)
ELSEIF(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
SET(CMAKE_CXX_STANDARD 11)
ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
SET(CMAKE_CXX_STANDARD 11)
ELSE()
message(WARNING "C++ standard could not be set because compiler is not GNU, Clang or MSVC.")
ENDIF()
if(NOT Boost_FOUND)
message(FATAL_ERROR "Boost required to compile gr-air-modes")
endif()
IF(CMAKE_C_COMPILER_ID STREQUAL "GNU")
SET(CMAKE_C_STANDARD 11)
ELSEIF(CMAKE_C_COMPILER_ID MATCHES "Clang")
SET(CMAKE_C_STANDARD 11)
ELSEIF(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
SET(CMAKE_C_STANDARD 11)
ELSE()
message(WARNING "C standard could not be set because compiler is not GNU, Clang or MSVC.")
ENDIF()
########################################################################
# Install directories
########################################################################
include(GrPlatform) #define LIB_SUFFIX
set(GR_RUNTIME_DIR bin)
set(GR_LIBRARY_DIR lib${LIB_SUFFIX})
set(GR_INCLUDE_DIR include)
set(GR_DATA_DIR share)
set(GR_PKG_DATA_DIR ${GR_DATA_DIR}/${CMAKE_PROJECT_NAME})
set(GR_DOC_DIR ${GR_DATA_DIR}/doc)
set(GR_PKG_DOC_DIR ${GR_DOC_DIR}/${CMAKE_PROJECT_NAME})
set(GR_CONF_DIR etc)
set(GR_PKG_CONF_DIR ${GR_CONF_DIR}/${CMAKE_PROJECT_NAME}/conf.d)
set(GR_LIBEXEC_DIR libexec)
set(GR_PKG_LIBEXEC_DIR ${GR_LIBEXEC_DIR}/${CMAKE_PROJECT_NAME})
set(GRC_BLOCKS_DIR ${GR_PKG_DATA_DIR}/grc/blocks)
find_package(pybind11 REQUIRED)
execute_process(
COMMAND "${PYTHON_EXECUTABLE}" -c
"try:\n import numpy\n import os\n inc_path = numpy.get_include()\n if os.path.exists(os.path.join(inc_path, 'numpy', 'arrayobject.h')):\n print(inc_path, end='')\nexcept:\n pass"
OUTPUT_VARIABLE PYTHON_NUMPY_INCLUDE_DIR)
########################################################################
# Find gnuradio build dependencies
########################################################################
find_package(Gruel)
find_package(GnuradioCore)
find_package(Gnuradio "3.9" REQUIRED)
include(GrVersion)
if(NOT GRUEL_FOUND)
message(FATAL_ERROR "Gruel required to compile gr-air-modes")
endif()
include(GrPlatform) #define LIB_SUFFIX
if(NOT GNURADIO_CORE_FOUND)
message(FATAL_ERROR "GnuRadio Core required to compile gr-air-modes")
endif()
if(NOT CMAKE_MODULES_DIR)
set(CMAKE_MODULES_DIR lib${LIB_SUFFIX}/cmake)
endif(NOT CMAKE_MODULES_DIR)
set(GR_INCLUDE_DIR include/gr_air_modes)
set(GR_CMAKE_DIR ${CMAKE_MODULES_DIR}/${CMAKE_PROJECT_NAME})
set(GR_PKG_DATA_DIR ${GR_DATA_DIR}/${CMAKE_PROJECT_NAME})
set(GR_PKG_DOC_DIR ${GR_DOC_DIR}/${CMAKE_PROJECT_NAME})
set(GR_PKG_CONF_DIR ${GR_CONF_DIR}/${CMAKE_PROJECT_NAME}/conf.d)
set(GR_PKG_LIBEXEC_DIR ${GR_LIBEXEC_DIR}/${CMAKE_PROJECT_NAME})
########################################################################
# Setup the include and linker paths
# On Apple only, set install name and use rpath correctly, if not already set
########################################################################
include_directories(
${CMAKE_SOURCE_DIR}/include
${Boost_INCLUDE_DIRS}
${GRUEL_INCLUDE_DIRS}
${GNURADIO_CORE_INCLUDE_DIRS}
)
link_directories(
${Boost_LIBRARY_DIRS}
${GRUEL_LIBRARY_DIRS}
${GNURADIO_CORE_LIBRARY_DIRS}
)
# Set component parameters
set(GR_GR-AIR-MODES_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE INTERNAL "" FORCE)
set(GR_GR-AIR-MODES_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/swig CACHE INTERNAL "" FORCE)
if(APPLE)
if(NOT CMAKE_INSTALL_NAME_DIR)
set(CMAKE_INSTALL_NAME_DIR
${CMAKE_INSTALL_PREFIX}/${GR_LIBRARY_DIR} CACHE
PATH "Library Install Name Destination Directory" FORCE)
endif(NOT CMAKE_INSTALL_NAME_DIR)
if(NOT CMAKE_INSTALL_RPATH)
set(CMAKE_INSTALL_RPATH
${CMAKE_INSTALL_PREFIX}/${GR_LIBRARY_DIR} CACHE
PATH "Library Install RPath" FORCE)
endif(NOT CMAKE_INSTALL_RPATH)
if(NOT CMAKE_BUILD_WITH_INSTALL_RPATH)
set(CMAKE_BUILD_WITH_INSTALL_RPATH ON CACHE
BOOL "Do Build Using Library Install RPath" FORCE)
endif(NOT CMAKE_BUILD_WITH_INSTALL_RPATH)
endif(APPLE)
########################################################################
# Create uninstall target
@@ -128,14 +142,16 @@ add_custom_target(uninstall
${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake
)
install(FILES cmake/Modules/air_modesConfig.cmake
DESTINATION ${CMAKE_MODULES_DIR}/air_modes
)
########################################################################
# Add subdirectories
########################################################################
add_subdirectory(include)
add_subdirectory(include/air_modes)
add_subdirectory(lib)
add_subdirectory(swig)
add_subdirectory(python)
add_subdirectory(grc)
add_subdirectory(apps)
add_subdirectory(docs)
add_subdirectory(res)

13
Dockerfile Normal file
View File

@@ -0,0 +1,13 @@
FROM bistromath/gnuradio:v3.9
ENV num_threads 10
MAINTAINER bistromath@gmail.com version: 0.1
WORKDIR /opt
RUN apt install -y python3-zmq python3-scipy
RUN mkdir gr-air_modes
COPY . gr-air_modes/
WORKDIR /opt/gr-air_modes
RUN mkdir build && cd build && cmake ../ && make -j${num_threads} && make install && ldconfig

1
README
View File

@@ -97,6 +97,7 @@ gr-air-modes requires:
* Python >= 2.5 (written for Python 2.7, Python 3.0 might work)
** NumPy and SciPy are required for the FlightGear output plugin.
* PyZMQ
* Gnuradio >= 3.5.0
* Ettus UHD >= 3.4.0 for use with USRPs
* osmosdr (any version) for use with RTLSDR dongles

View File

@@ -21,7 +21,6 @@ include(GrPython)
GR_PYTHON_INSTALL(
PROGRAMS
mlat_server
modes_rx
modes_gui
uhd_modes.py

View File

@@ -1,265 +0,0 @@
#!/usr/bin/env python
#
# Copyright 2012 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.
#
#simple multilateration server app.
#looks at received messages and then attempts to multilaterate positions
#will not attempt clock synchronization yet; it's up to clients to present
#accurate timestamps. later on we can do clock sync based on ADS-B packets.
#SECURITY NOTE: THIS SERVER WAS WRITTEN WITH NO REGARD TOWARD SECURITY.
#NO REAL ATTEMPT AT DATA VALIDATION, MUCH LESS SECURITY, HAS BEEN MADE.
#USE THIS PROGRAM AT YOUR OWN RISK AND WITH THE UNDERSTANDING THAT THE
#INTERNET IS A SCARY PLACE FULL OF MEAN PEOPLE.
import time, os, sys, socket, struct
from string import split, join
from datetime import *
import air_modes
import pickle
import time
import bisect
#change this to 0 for ASCII format for debugging. use HIGHEST_PROTOCOL
#for actual use to keep the pickle size down.
pickle_prot = 0
#pickle_prot = pickle.HIGHEST_PROTOCOL
def ordered_insert(a, item):
a.insert(bisect.bisect_right(a, item), item)
class client_info:
def __init__(self):
self.name = ""
self.position = []
self.offset_secs = 0
self.offset_frac_secs = 0.0
self.time_source = None
class connection:
def __init__(self, addr, sock, clientinfo):
self.addr = addr
self.sock = sock
self.clientinfo = clientinfo
class mlat_server:
def __init__(self, mypos, port):
self._s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self._s.bind(('', port))
self._s.listen(1)
self._s.setblocking(0) #nonblocking
self._conns = [] #list of active connections
self._reports = {} #hashed by data
self._lastreport = 0 #used for pruning
self._parser = air_modes.parse(None)
self._remnant = None #for piecing together messages across packets
def __del__(self):
self._s.close()
def get_messages(self):
num=0
for conn in self._conns:
pkt = None
try:
pkt = conn.sock.recv(16384)
except socket.error:
print "%s disconnected" % conn.clientinfo.name
self._conns.remove(conn)
if not pkt: break
try:
for msg in pkt.splitlines(True):
if msg.endswith("\n"):
if self._remnant:
msg = self._remnant + msg
self._remnant = None
[data, ecc, reference, timestamp_int, timestamp_frac] = msg.split()
st = stamp(conn.clientinfo, int(timestamp_int), float(timestamp_frac))
if data not in self._reports:
self._reports[data] = []
#ordered insert
ordered_insert(self._reports[data], st)
if st.tofloat() > self._lastreport:
self._lastreport = st.tofloat()
num += 1
else:
if self._remnant is not None:
raise Exception("Malformed data: " + msg)
else:
self._remnant = msg
except Exception as e:
print "Invalid message from %s: %s..." % (conn.addr, pkt[:64])
print e
#self.prune()
return num
#prune should delete all reports in self._reports older than 5s.
#not really tested well
def prune(self):
for data, stamps in self._reports.iteritems():
#check the last stamp first so we don't iterate over
#the whole list if we don't have to
if self._lastreport - stamps[-1].tofloat() > 5:
del self._reports[data]
else:
for i,st in enumerate(stamps):
if self._lastreport - st.tofloat() > 5:
del self._reports[data][i]
if len(self._reports[data]) == 0:
del self._reports[data]
#return a list of eligible messages for multilateration
#eligible reports are:
#1. bit-identical
#2. from distinct stations (at least 3)
#3. within 0.003 seconds of each other
#traverse the reports for each data pkt (hash) looking for >3 reports
#within 0.003s, then check for unique IPs (this should pass 99% of the time)
#let's break a record for most nested loops. this one goes four deep.
#it's loop-ception!
def get_eligible_reports(self):
groups = []
for data,stamps in self._reports.iteritems():
if len(stamps) >= 4: #quick check before we do a set()
stations = set([st.clientinfo for st in stamps])
if len(stations) >= 4:
i=0
#it's O(n) since the list is already sorted
#can probably be cleaner and more concise
while(i < len(stamps)):
refstamp = stamps[i].tofloat()
reps = []
while (i<len(stamps)) and (stamps[i].tofloat() < (refstamp + 0.003)):
reps.append(stamps[i])
i+=1
deduped = []
for cinfo in stations:
for st in reps[::-1]:
if st.clientinfo == cinfo:
deduped.append(st)
break
if len(deduped) >= 4:
groups.append({"data": data, "stamps": deduped})
#TODO: pop the entries so you don't issue duplicate reports
if len(groups) > 0:
return groups
return None
#issue multilaterated positions
def output(self, msg):
if msg is not None:
try:
for conn in self._conns[:]: #iterate over a copy of the list
conn.sock.send(msg)
except socket.error:
print "%s disconnected" % conn.clientinfo.name
self._conns.remove(conn)
print "Connections: ", len(self._conns)
#add a new connection to the list
def add_pending_conns(self):
try:
conn, addr = self._s.accept()
conn.send("HELO") #yeah it's like that
msg = conn.recv(1024)
if not msg:
return
try:
clientinfo = pickle.loads(msg)
except:
print "Invalid pickle received from client"
return
if clientinfo.__class__.__name__ != "client_info":
print "Invalid datatype received from client"
return
self._conns.append(connection(addr[0], conn, clientinfo))
print "New connection from %s: %s" % (addr[0], clientinfo.name)
except socket.error:
pass
#retrieve altitude from a mode S packet or None if not available
#returns alt in meters
def get_modes_altitude(data):
df = data["df"] #reply type
f2m = 0.3048
if df == 0 or df == 4:
return air_modes.altitude.decode_alt(data["ac"], True)
elif df == 17:
bds = data["me"].get_type()
if bds == 0x05:
return f2m*air_modes.altitude.decode_alt(data["me"]["alt"], False)
else:
return None
position = [37.76, -117.443, 100]
if __name__=="__main__":
srv = mlat_server(position, 19005)
while 1:
nummsgs = srv.get_messages()
if len(srv._conns) > 0:
print "Received %i messages" % nummsgs
srv.add_pending_conns()
reps = srv.get_eligible_reports()
if reps:
for rep in reps:
print "Report with data %x" % rep["data"]
for st in rep["stamps"]:
print "Stamp from %s: %f" % (st.clientinfo.name, st.tofloat())
srv.prune()
#now format the reports to get them ready for multilateration
#it's expecting a list of tuples [(station[], timestamp)...]
#also have to parse the data to pull altitude out of the mix
if reps:
for rep in reps:
alt = get_modes_altitude(air_modes.modes_reply(rep["data"]))
if (alt is None and len(rep["stamps"]) > 3) or alt is not None:
mlat_list = [(st.clientinfo.position, st.tofloat()) for st in rep["stamps"]]
print mlat_list
#multilaterate!
try:
pos, senttime = air_modes.mlat.mlat(mlat_list, alt)
if pos is not None:
print "Resolved position: ", pos
#send a report!
msg = "%x %i %i %f %f %f %f %f %f\n" % \
(rep["data"], len(mlat_list), int(senttime), \
float(senttime) - int(senttime), \
pos[0], pos[1], pos[2], 0.0, 0.0)
srv.output(msg)
except air_modes.exceptions.MlatNonConvergeError as e:
print e
#DEBUG
# else:
# msg = "0921354 4 2 0.12345 36.671234 -112.342515 9028.1243 0.0 0.0\n"
# srv.output(msg)
time.sleep(0.3)

View File

@@ -1,67 +0,0 @@
#!/usr/bin/env python
#test stuff for mlat server
import socket, pickle, time, random, sys, math, numpy
import air_modes
class rx_data:
secs = 0
frac_secs = 0.0
data = None
class client_info:
def __init__(self):
self.name = ""
self.position = []
self.offset_secs = 0
self.offset_frac_secs = 0.0
info = client_info()
info.name = sys.argv[1]
info.position = [float(sys.argv[2]), float(sys.argv[3]), 100]
info.offset_secs = 0
info.offset_frac_secs = 0.0
data1 = rx_data()
data1.secs = 0
data1.data = int("0x8da81f875857f10eb65b10cb66f3", 16)
ac_starting_pos = [37.617175, -122.400843, 8000]
ac_hdg = 130.
ac_spd = 0.00008
def get_pos(time):
return [ac_starting_pos[0] + ac_spd * time * math.cos(ac_hdg*math.pi/180.), \
ac_starting_pos[1] + ac_spd * time * math.sin(ac_hdg*math.pi/180.), \
ac_starting_pos[2]]
def get_simulated_timestamp(time, position):
prange = numpy.linalg.norm(numpy.array(air_modes.mlat.llh2ecef(position))-numpy.array(air_modes.mlat.llh2geoid(info.position))) / air_modes.mlat.c
return time + prange + random.random()*60e-9 - 30e-9
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(1)
sock.connect(("localhost", 19005))
sock.send(pickle.dumps(info))
print sock.recv(1024)
sock.setblocking(0)
ts = 0.0
while 1:
pos = get_pos(ts)
stamp = get_simulated_timestamp(ts, pos)
print "Timestamp: %.10f" % (stamp)
print "Position: ", pos
data1.secs = int(stamp)
data1.frac_secs = float(stamp)
data1.frac_secs -= int(data1.frac_secs)
sock.send(pickle.dumps([data1]))
try:
positions = sock.recv(1024)
except:
pass
if positions: print positions
ts+=1
time.sleep(1)
sock.close()
sock = None

View File

@@ -19,16 +19,20 @@
# Boston, MA 02110-1301, USA.
#
import os, sys, time, threading, datetime, math, csv
from PyQt4 import QtCore,QtGui
import os, sys, time, threading, datetime, math, csv, tempfile, ConfigParser
from optparse import OptionParser
from PyQt4 import QtCore,QtGui,QtWebKit
from PyQt4.Qwt5 import Qwt
from gnuradio import gr, gru, optfir, eng_notation, blks2
import gnuradio.gr.gr_threading as _threading
from gnuradio import gr, eng_notation
from gnuradio.eng_option import eng_option
from gnuradio.gr.pubsub import pubsub
import air_modes
from air_modes.exceptions import *
from air_modes.modes_rx_ui import Ui_MainWindow
from air_modes.gui_model import *
from air_modes.az_map import *
import sqlite3
import zmq
class mainwindow(QtGui.QMainWindow):
live_data_changed_signal = QtCore.pyqtSignal(QtCore.QString, name='liveDataChanged')
@@ -39,53 +43,71 @@ class mainwindow(QtGui.QMainWindow):
#set defaults
#add file, RTL, UHD sources
self.ui.combo_source.addItems(["UHD device", "RTL-SDR", "File"])
self.ui.combo_source.addItems(["UHD", "Osmocom", "File/UDP"])
self.ui.combo_source.setCurrentIndex(0)
#populate antenna, rate combo boxes based on source
self.populate_source_options()
defaults = self.get_defaults()
#should round to actual achieved gain
self.ui.line_gain.insert("30")
self.ui.line_gain.insert(defaults["gain"])
#default to 5dB
self.ui.line_threshold.insert("5")
self.ui.line_threshold.insert(defaults["threshold"])
self.ui.prog_rssi.setMinimum(-40)
if defaults["pmf"] is not None:
self.ui.check_pmf.setChecked(bool(defaults["pmf"]))
if defaults["dcblock"] is not None:
self.ui.check_dcblock.setChecked(bool(defaults["dcblock"]))
if defaults["samplerate"] is not None:
if defaults["samplerate"] in self.rates:
self.ui.combo_rate.setCurrentIndex(self.rates.index(int(defaults["samplerate"])))
self.ui.prog_rssi.setMinimum(-60)
self.ui.prog_rssi.setMaximum(0)
self.ui.combo_ant.setCurrentIndex(self.ui.combo_ant.findText("RX2"))
if defaults["antenna"] is None:
self.ui.combo_ant.setCurrentIndex(self.ui.combo_ant.findText("RX2"))
else:
self.ui.combo_ant.setCurrentIndex(self.ui.combo_ant.findText(defaults["antenna"]))
#check KML by default, leave the rest unchecked.
self.ui.check_sbs1.setCheckState(QtCore.Qt.Unchecked)
self.ui.check_raw.setCheckState(QtCore.Qt.Unchecked)
self.ui.check_fgfs.setCheckState(QtCore.Qt.Unchecked)
self.ui.check_kml.setCheckState(QtCore.Qt.Checked)
self.ui.check_sbs1.setChecked(bool(defaults["sbs1"] == "1"))
self.ui.check_raw.setChecked(bool(defaults["raw"] == "1"))
self.ui.check_fgfs.setChecked(bool(defaults["fgfs"] == "1"))
self.ui.check_kml.setChecked(bool(defaults["kml"] == "1"))
self.ui.line_sbs1port.insert("30003")
self.ui.line_rawport.insert("9988")
self.ui.line_fgfsport.insert("5500")
self.ui.line_kmlfilename.insert("modes.kml")
self.ui.line_sbs1port.insert(defaults["sbs1port"])#"30003")
self.ui.line_rawport.insert(defaults["rawport"])#"9988")
self.ui.line_fgfsport.insert(defaults["fgfsport"])#"5500")
self.ui.line_kmlfilename.insert(defaults["kmlfile"])#"modes.kml")
if defaults["latitude"] is not None:
self.ui.line_my_lat.insert(defaults["latitude"])
if defaults["longitude"] is not None:
self.ui.line_my_lon.insert(defaults["longitude"])
if defaults["apikey"] is not None:
self.ui.line_my_api_key.insert(defaults["apikey"])
#disable by default
self.ui.check_adsbonly.setCheckState(QtCore.Qt.Unchecked)
#set up the radio stuff
self.queue = gr.msg_queue(10)
self.runner = None
self.fg = None
self.outputs = []
self.updates = []
self.output_handler = None
self.running = False
self.kmlgen = None #necessary bc we stop its thread in shutdown
self.dbname = "air_modes.db"
self.num_reports = 0
self.last_report = 0
self.context = zmq.Context(1)
self.datamodel = dashboard_data_model(None)
self.ui.list_aircraft.setModel(self.datamodel)
self.ui.list_aircraft.setModelColumn(0)
self.az_model = air_modes.az_map_model(None)
self.az_model = air_modes.az_map.az_map_model(None)
self.ui.azimuth_map.setModel(self.az_model)
#set up dashboard views
@@ -117,12 +139,31 @@ class mainwindow(QtGui.QMainWindow):
self.ui.list_aircraft.selectionModel().currentRowChanged.connect(self.dashboard_mapper.setCurrentModelIndex)
self.ui.list_aircraft.selectionModel().currentRowChanged.connect(self.update_heading_widget)
self.ui.list_aircraft.selectionModel().currentRowChanged.connect(self.update_bearing_widget)
self.datamodel.dataChanged.connect(self.unmapped_widgets_dataChanged)
self.ui.list_aircraft.selectionModel().currentRowChanged.connect(self.update_rssi_widget)
self.ui.list_aircraft.selectionModel().currentRowChanged.connect(self.update_map_highlight)
self.datamodel.dataChanged.connect(self.unmapped_widgets_dataChanged)
#hook up parameter-changed signals so we can change gain, rate, etc. while running
self.ui.combo_rate.currentIndexChanged['QString'].connect(self.update_sample_rate)
self.ui.line_gain.editingFinished.connect(self.update_gain)
self.ui.combo_source.currentIndexChanged['QString'].connect(self.populate_source_options)
#hook up live data text box update signal
self.live_data_changed_signal.connect(self.on_append_live_data)
self._last_live_data_update = time.time()
self._pending_msgstr = ""
self.prefs = None
def update_sample_rate(self, rate):
if self.running:
self._radio.set_rate(int(float(rate)*1e6))
def update_gain(self):
if self.running:
self._radio.set_gain(float(self.ui.line_gain.text()))
############ widget update functions for non-mapped widgets ############
def update_heading_widget(self, index):
if index.model() is not None:
@@ -148,7 +189,7 @@ class mainwindow(QtGui.QMainWindow):
if index.model() is not None:
rssi = index.model().data(index.model().index(index.row(), 2)).toDouble()[0]
self.ui.prog_rssi.setValue(rssi)
def increment_reportspersec(self, msg):
self.num_reports += 1
@@ -159,6 +200,12 @@ class mainwindow(QtGui.QMainWindow):
self.ui.line_reports.setText("%i" % self.num_reports)
self.num_reports = 0
def update_map_highlight(self, index):
if index.model() is not None:
icaostr = index.model().data(index.model().index(index.row(), self.datamodel._colnames.index("icao"))).toString()
icao = int(str(icaostr), 16)
self.jsonpgen.set_highlight(icao)
##################### dynamic option population ########################
#goes and gets valid antenna, sample rate options from the device and grays out appropriate things
def populate_source_options(self):
@@ -166,12 +213,13 @@ class mainwindow(QtGui.QMainWindow):
self.rates = []
self.ratetext = []
self.antennas = []
if sourceid == "UHD device":
if sourceid == "UHD":
try:
from gnuradio import uhd
self.src = uhd.single_usrp_source("", uhd.io_type_t.COMPLEX_FLOAT32, 1)
self.rates = [rate.start() for rate in self.src.get_samp_rates()]
self.rates = [rate.start() for rate in self.src.get_samp_rates()
if (rate.start() % 2.e6) == 0 and rate >= 4e6]
self.antennas = self.src.get_antennas()
self.src = None #deconstruct UHD source for now
self.ui.combo_ant.setEnabled(True)
@@ -183,16 +231,28 @@ class mainwindow(QtGui.QMainWindow):
self.ui.combo_ant.setEnabled(False)
self.ui.combo_rate.setEnabled(False)
self.ui.stack_source.setCurrentIndex(0)
elif sourceid == "RTL-SDR":
self.rates = [3.2e6]
self.antennas = ["RX"]
self.ui.combo_ant.setEnabled(False)
self.ui.combo_rate.setEnabled(False)
self.ui.stack_source.setCurrentIndex(0)
elif sourceid == "File":
self.rates = [2e6, 4e6, 6e6, 8e6, 10e6]
elif sourceid == "Osmocom":
try:
import osmosdr
self.src = osmosdr.source("")
self.rates = [rate.start() for rate in self.src.get_sample_rates()
if ((rate.start() % 2.e6) == 0)
or (rate.start() < 4.e6 and ((rate.start()%0.2e6) == 0))]
self.antennas = ["RX"]
self.src = None
self.ui.combo_ant.setEnabled(False)
self.ui.combo_rate.setEnabled(True)
self.ui.stack_source.setCurrentIndex(0)
except:
self.rates = []
self.antennas = []
self.ui.combo_ant.setEnabled(False)
self.ui.combo_rate.setEnabled(False)
self.ui.stack_source.setCurrentIndex(0)
elif sourceid == "File/UDP":
self.rates = [2e6*i for i in range(2,13)]
self.antennas = ["None"]
self.ui.combo_ant.setEnabled(False)
self.ui.combo_rate.setEnabled(True)
@@ -206,8 +266,18 @@ class mainwindow(QtGui.QMainWindow):
self.ui.combo_ant.clear()
self.ui.combo_ant.addItems(self.antennas)
if 4e6 in self.rates:
self.ui.combo_rate.setCurrentIndex(self.rates.index(4e6))
#set up recommended sample rate
if len(self.rates) > 1:
if max(self.rates) > 4.e6:
recommended_rate = min(x for x in self.rates if x >= 4e6 and
max(self.rates) % x == 0)
else:
recommended_rate = max(self.rates)
if recommended_rate >= 8.e6:
self.ui.check_pmf.setChecked(True)
else:
self.ui.check_pmf.setChecked(False)
self.ui.combo_rate.setCurrentIndex(self.rates.index(recommended_rate))
################ action handlers ####################
def on_combo_source_currentIndexChanged(self, index):
@@ -215,47 +285,54 @@ class mainwindow(QtGui.QMainWindow):
def on_button_start_released(self):
#if we're already running, kill it!
if self.runner is not None:
self.output_handler.done = True
self.output_handler = None
self.outputs = []
self.updates = []
self.fg.stop()
self.runner = None
self.fg = None
if self.kmlgen is not None:
self.kmlgen.done = True
#TODO FIXME need a way to kill kmlgen safely without delay
#self.kmlgen.join()
#self.kmlgen = None
if self.running is True:
self.on_quit()
self.num_reports = 0
self.ui.line_reports.setText("0")
self.ui.button_start.setText("Start")
self.running = False
else: #we aren't already running, let's get this party started
options = {}
options["source"] = str(self.ui.combo_source.currentText())
options["rate"] = float(self.ui.combo_rate.currentText()) * 1e6
options["antenna"] = str(self.ui.combo_ant.currentText())
options["gain"] = float(self.ui.line_gain.text())
options["threshold"] = float(self.ui.line_threshold.text())
options["filename"] = str(self.ui.line_inputfile.text())
options["pmf"] = self.ui.check_pmf.checkState()
parser = OptionParser(option_class=eng_option)
air_modes.modes_radio.add_radio_options(parser)
(options, args) = parser.parse_args() #sets defaults nicely
if str(self.ui.combo_source.currentText()) != "File/UDP":
options.source = str(self.ui.combo_source.currentText()).lower()
else:
options.source = str(self.ui.line_inputfile.text())
options.rate = float(self.ui.combo_rate.currentText()) * 1e6
options.antenna = str(self.ui.combo_ant.currentText())
options.gain = float(self.ui.line_gain.text())
options.threshold = float(self.ui.line_threshold.text())
options.pmf = self.ui.check_pmf.isChecked()
options.dcblock = self.ui.check_dcblock.isChecked()
self.fg = adsb_rx_block(options, self.queue) #create top RX block
self.runner = top_block_runner(self.fg) #spawn new thread to do RX
self._servers = ["inproc://modes-radio-pub"] #TODO ADD REMOTES
self._relay = air_modes.zmq_pubsub_iface(self.context, subaddr=self._servers, pubaddr=None)
if self.ui.check_raw.checkState():
options.tcp = int(self.ui.line_rawport.text())
self._radio = air_modes.modes_radio(options, self.context)
self._publisher = pubsub()
self._relay.subscribe("dl_data", air_modes.make_parser(self._publisher))
try:
my_position = [float(self.ui.line_my_lat.text()), float(self.ui.line_my_lon.text())]
except:
my_position = None
self.datamodelout = dashboard_output(my_position, self.datamodel)
try:
my_apikey = str(self.ui.line_my_api_key.text())
except:
my_apikey = None
self._cpr_dec = air_modes.cpr_decoder(my_position)
self.datamodelout = dashboard_output(self._cpr_dec, self.datamodel, self._publisher)
self.outputs = [self.datamodelout.output]
self.updates = []
self.lock = threading.Lock() #grab a lock to ensure sql and kml don't step on each other
#output options to populate outputs, updates
@@ -265,55 +342,88 @@ class mainwindow(QtGui.QMainWindow):
if self.ui.check_sbs1.checkState():
sbs1port = int(self.ui.line_sbs1port.text())
sbs1out = air_modes.output_sbs1(my_position, sbs1port)
self.outputs.append(sbs1out.output)
self.updates.append(sbs1out.add_pending_conns)
sbs1out = air_modes.output_sbs1(self._cpr_dec, sbs1port, self._publisher)
if self.ui.check_fgfs.checkState():
fghost = "127.0.0.1" #TODO FIXME
fgport = self.ui.line_fgfsport.text()
fgout = air_modes.output_flightgear(my_position, fghost, int(fgport))
self.outputs.append(fgout.output)
if self.ui.check_raw.checkState():
rawport = air_modes.raw_server(int(self.ui.line_rawport.text()))
self.outputs.append(rawport.output)
self.updates.append(rawport.add_pending_conns)
fgout = air_modes.output_flightgear(self._cpr_dec, fghost, int(fgport), self._publisher)
#add azimuth map output and hook it up
if my_position is not None:
self.az_map_output = air_modes.az_map_output(my_position, self.az_model)
self.outputs.append(self.az_map_output.output)
self.az_map_output = air_modes.az_map.az_map_output(self._cpr_dec, self.az_model, self._publisher)
#self._relay.subscribe("dl_data", self.az_map_output.output)
self.livedata = air_modes.output_print(my_position)
#add output for live data box
self.outputs.append(self.output_live_data)
#set up map
#NOTE this is busted on windows. WebKit requires .htm[l] extensions to render,
#so using a temp file doesn't work.
self._htmlfile = open("/tmp/mode_s.html", 'wb+')#tempfile.NamedTemporaryFile()
self._jsonfile = tempfile.NamedTemporaryFile()
self.livedata = air_modes.output_print(self._cpr_dec,
self._publisher,
self.live_data_changed_signal.emit)
#create SQL database for KML and dashboard displays
self.dbwriter = air_modes.output_sql(my_position, self.dbname, self.lock)
self.outputs.append(self.dbwriter.output) #now the db will update itself
self.dbwriter = air_modes.output_sql(self._cpr_dec, self.dbname, self.lock, self._publisher)
self.jsonpgen = air_modes.output_jsonp(self._jsonfile.name, self.dbname, my_position, self.lock, timeout=1)
htmlstring = air_modes.html_template(my_apikey, my_position, self._jsonfile.name)
self._htmlfile.write(htmlstring)
self._htmlfile.flush()
class WebPage(QtWebKit.QWebPage):
def javaScriptConsoleMessage(self, msg, line, source):
print('%s line %d: %s' % (source, line, msg))
page = WebPage()
self.ui.mapView.setPage(page)
self.ui.mapView.load( QtCore.QUrl( QtCore.QUrl.fromLocalFile("/tmp/mode_s.html") ) )
self.ui.mapView.show()
#output to update reports/sec widget
self.outputs.append(self.increment_reportspersec)
self.updates.append(self.update_reportspersec)
self._relay.subscribe("dl_data", self.increment_reportspersec)
self._rps_timer = QtCore.QTimer()
self._rps_timer.timeout.connect(self.update_reportspersec)
self._rps_timer.start(1000)
#create output handler thread
self.output_handler = output_handler(self.outputs, self.updates, self.queue)
#start the flowgraph
self._radio.start()
self.ui.button_start.setText("Stop")
self.running = True
def on_quit(self):
if self.runner is not None:
#grab prefs and save them
self.prefs = {}
self.prefs["samplerate"] = options.rate
self.prefs["antenna"] = options.antenna
self.prefs["gain"] = options.gain
self.prefs["pmf"] = "1" if options.pmf else "0"
self.prefs["dcblock"] = "1" if options.dcblock else "0"
self.prefs["source"] = self.ui.combo_source.currentText()
self.prefs["threshold"] = options.threshold
self.prefs["sbs1"] = "1" if self.ui.check_sbs1.isChecked() else "0"
self.prefs["sbs1port"] = int(self.ui.line_sbs1port.text())
self.prefs["fgfs"] = "1" if self.ui.check_fgfs.isChecked() else "0"
self.prefs["fgfsport"] = int(self.ui.line_fgfsport.text())
self.prefs["raw"] = "1" if self.ui.check_raw.isChecked() else "0"
self.prefs["rawport"] = int(self.ui.line_rawport.text())
self.prefs["kml"] = "1" if self.ui.check_kml.isChecked() else "0"
self.prefs["kmlfile"] = self.ui.line_kmlfilename.text()
try:
self.output_handler.done = True
self.prefs["latitude"] = float(self.ui.line_my_lat.text())
self.prefs["longitude"] = float(self.ui.line_my_lon.text())
except:
pass
self.output_handler = None
self.outputs = []
self.updates = []
self.fg.stop()
self.runner = None
self.fg = None
try:
self.prefs["apikey"] = self.ui.line_my_api_key.text()
except:
pass
def on_quit(self):
if self.running is True:
self._radio.close()
self._radio = None
self._relay.close()
self._relay = None
self._rps_timer = None
try:
self.kmlgen.done = True
#TODO FIXME need a way to kill kmlgen safely without delay
@@ -322,116 +432,78 @@ class mainwindow(QtGui.QMainWindow):
except:
pass
if self.prefs is not None:
self.write_defaults(self.prefs)
#slot to catch signal emitted by output_live_data (necessary for
#thread safety since output_live_data is called by another thread)
def on_append_live_data(self, msgstr):
self._pending_msgstr += msgstr + "\n"
if time.time() - self._last_live_data_update >= 0.1:
self._last_live_data_update = time.time()
self.update_live_data(self._pending_msgstr)
self._pending_msgstr = ""
def update_live_data(self, msgstr):
#limit scrollback buffer size -- is there a faster way?
if(self.ui.text_livedata.document().lineCount() > 500):
cursor = self.ui.text_livedata.textCursor()
cursor.movePosition(QtGui.QTextCursor.Start)
cursor.select(QtGui.QTextCursor.LineUnderCursor)
cursor.removeSelectedText()
self.ui.text_livedata.append(msgstr)
self.ui.text_livedata.verticalScrollBar().setSliderPosition(self.ui.text_livedata.verticalScrollBar().maximum())
def output_live_data(self, msg):
msgstr = self.livedata.parse(msg)
if msgstr is not None:
self.live_data_changed_signal.emit(msgstr)
opt_file = "~/.gr-air-modes/prefs"
def get_defaults(self):
defaults = {}
defaults["samplerate"] = None #let app pick it
defaults["pmf"] = None
defaults["dcblock"] = None
defaults["antenna"] = None
defaults["gain"] = "25"
defaults["kml"] = "1"
defaults["kmlfile"] = "modes.kml"
defaults["sbs1"] = "0"
defaults["sbs1port"] = "30003"
defaults["raw"] = "0"
defaults["rawport"] = "9988"
defaults["fgfs"] = "0"
defaults["fgfsport"] = "5500"
defaults["source"] = "UHD"
defaults["threshold"] = "5"
defaults["latitude"] = None
defaults["longitude"] = None
defaults["apikey"] = None
prefs = ConfigParser.ConfigParser(defaults)
prefs.optionxform = str
try:
prefs.read(os.path.expanduser(self.opt_file))
for item in prefs.items("GUI"):
defaults[item[0]] = item[1]
except (IOError, ConfigParser.NoSectionError):
print("No preferences file %s found, creating..." % os.path.expanduser(self.opt_file))
self.write_defaults(defaults)
return defaults
def write_defaults(self, defaults):
config = ConfigParser.RawConfigParser()
config.add_section('GUI')
#the output handler is a thread which runs the various registered output functions when there's a received message.
#it also executes registered updates at the sleep rate -- currently 10Hz.
class output_handler(threading.Thread):
def __init__(self, outputs, updates, queue):
threading.Thread.__init__(self)
self.setDaemon(1)
self.outputs = outputs
self.updates = updates
self.queue = queue
self.done = False
self.start()
for item in defaults:
config.set('GUI', item, str(defaults[item]))
def run(self):
while self.done is False:
for update in self.updates:
update()
while not self.queue.empty_p():
msg = self.queue.delete_head()
for output in self.outputs:
try:
output(msg.to_string())
except ADSBError:
pass
dirname = os.path.dirname(os.path.expanduser(self.opt_file))
if not os.path.exists(dirname):
os.makedirs(dirname)
time.sleep(0.1)
self.done = True
self.outputs = None
self.updates = None
self.queue = None
class top_block_runner(_threading.Thread):
def __init__(self, tb):
_threading.Thread.__init__(self)
self.setDaemon(1)
self.tb = tb
self.done = False
self.start()
def run(self):
self.tb.run()
self.done = True
#Top block for ADSB receiver. If you define a standard interface you
#can make this common code between the GUI app and the cmdline app
class adsb_rx_block (gr.top_block):
def __init__(self, options, queue):
gr.top_block.__init__(self)
self.options = options
rate = options["rate"]
print "Rate: %f" % rate
use_resampler = False
freq = 1090e6
if options["source"] == "UHD device":
from gnuradio import uhd
self.u = uhd.single_usrp_source("", uhd.io_type_t.COMPLEX_FLOAT32, 1)
time_spec = uhd.time_spec(0.0)
self.u.set_time_now(time_spec)
self.u.set_antenna(options["antenna"])
self.u.set_samp_rate(rate)
rate = self.u.get_samp_rate()
self.u.set_gain(int(options["gain"]))
self.u.set_center_freq(freq, 0)
elif options["source"] == "RTL-SDR":
import osmosdr
self.u = osmosdr.source_c()
self.u.set_sample_rate(3.2e6) #fixed for RTL dongles
rate = int(4e6)
self.u.set_gain_mode(0) #manual gain mode
self.u.set_gain(int(options["gain"]))
self.u.set_center_freq(freq, 0)
use_resampler = True
elif options["source"] == "File":
self.u = gr.file_source(gr.sizeof_gr_complex, options["filename"])
else:
raise NotImplementedError
self.rx_path = air_modes.rx_path(rate, options["threshold"], queue, options["pmf"])
if use_resampler:
self.lpfiltcoeffs = gr.firdes.low_pass(1, 5*3.2e6, 1.6e6, 300e3)
self.resample = blks2.rational_resampler_ccf(interpolation=5, decimation=4, taps=self.lpfiltcoeffs)
self.connect(self.u, self.resample, self.rx_path)
else:
self.connect(self.u, self.rx_path)
with open(os.path.expanduser(self.opt_file), 'wb') as prefsfile:
config.write(prefsfile)
if __name__ == '__main__':

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env python
# Copyright 2010 Nick Foster
#!/usr/bin/env python3
# Copyright 2010, 2013 Nick Foster
#
# This file is part of gr-air-modes
#
@@ -19,256 +19,80 @@
# Boston, MA 02110-1301, USA.
#
my_position = None
from gnuradio import gr, gru, optfir, eng_notation, blks2
from gnuradio.eng_option import eng_option
from gnuradio.gr.pubsub import pubsub
from optparse import OptionParser
import time, os, sys, threading
from string import split, join
import time, os, sys, threading, math
import air_modes
from air_modes.types import modes_report, stamp
from air_modes.parse import modes_reply
import gnuradio.gr.gr_threading as _threading
import csv
from air_modes.modes_types import *
from air_modes.exceptions import *
import pickle
import zmq
class top_block_runner(_threading.Thread):
def __init__(self, tb):
_threading.Thread.__init__(self)
self.setDaemon(1)
self.tb = tb
self.done = False
self.start()
#todo: maybe move plugins to separate programs (flightgear, SBS1, etc.)
def main():
my_position = None
usage = "%prog: [options]"
optparser = OptionParser(option_class=eng_option, usage=usage)
air_modes.modes_radio.add_radio_options(optparser)
def run(self):
self.tb.run()
self.done = True
optparser.add_option("-l","--location", type="string", default=None,
help="GPS coordinates of receiving station in format xx.xxxxx,xx.xxxxx")
#data source options
optparser.add_option("-a","--remote", type="string", default=None,
help="specify additional servers from which to take data in format tcp://x.x.x.x:y,tcp://....")
optparser.add_option("-n","--no-print", action="store_true", default=False,
help="disable printing decoded packets to stdout")
#output plugins
optparser.add_option("-K","--kml", type="string", default=None,
help="filename for Google Earth KML output")
optparser.add_option("-P","--sbs1", action="store_true", default=False,
help="open an SBS-1-compatible server on port 30003")
optparser.add_option("-m","--multiplayer", type="string", default=None,
help="FlightGear server to send aircraft data, in format host:port")
class adsb_rx_block (gr.top_block):
def __init__(self, options, args, queue):
gr.top_block.__init__(self)
(options, args) = optparser.parse_args()
self.options = options
self.args = args
rate = int(options.rate)
use_resampler = False
self.time_source = None
if options.filename is None and options.udp is None and not options.rtlsdr:
#UHD source by default
from gnuradio import uhd
self.u = uhd.single_usrp_source(options.args, uhd.io_type_t.COMPLEX_FLOAT32, 1)
#check for GPSDO
#if you have a GPSDO, UHD will automatically set the timestamp to UTC time
#as well as automatically set the clock to lock to GPSDO.
if self.u.get_time_source(0) == 'gpsdo':
self.time_source = 'gpsdo'
else:
self.time_source = None
self.u.set_time_now(uhd.time_spec(0.0))
if not options.antenna is None:
self.u.set_antenna(options.antenna)
self.u.set_samp_rate(rate)
rate = int(self.u.get_samp_rate()) #retrieve actual
if options.gain is None: #set to halfway
g = self.u.get_gain_range()
options.gain = (g.start()+g.stop()) / 2.0
if not(self.tune(options.freq)):
print "Failed to set initial frequency"
print "Setting gain to %i" % options.gain
self.u.set_gain(options.gain)
print "Gain is %i" % self.u.get_gain()
elif options.rtlsdr: #RTLSDR dongle
import osmosdr
self.u = osmosdr.source_c(options.args)
self.u.set_sample_rate(3.2e6) #fixed for RTL dongles
if not self.u.set_center_freq(options.freq):
print "Failed to set initial frequency"
self.u.set_gain_mode(0) #manual gain mode
if options.gain is None:
options.gain = 34
self.u.set_gain(options.gain)
print "Gain is %i" % self.u.get_gain()
use_resampler = True
else:
if options.filename is not None:
self.u = gr.file_source(gr.sizeof_gr_complex, options.filename)
elif options.udp is not None:
self.u = gr.udp_source(gr.sizeof_gr_complex, "localhost", options.udp)
else:
raise Exception("No valid source selected")
print "Rate is %i" % (rate,)
pass_all = 0
if options.output_all :
pass_all = 1
self.rx_path = air_modes.rx_path(rate, options.threshold, queue, options.pmf)
if use_resampler:
self.lpfiltcoeffs = gr.firdes.low_pass(1, 5*3.2e6, 1.6e6, 300e3)
self.resample = blks2.rational_resampler_ccf(interpolation=5, decimation=4, taps=self.lpfiltcoeffs)
self.connect(self.u, self.resample, self.rx_path)
else:
self.connect(self.u, self.rx_path)
def tune(self, freq):
result = self.u.set_center_freq(freq, 0)
return result
def printraw(msg):
print msg
def printmlat(msg):
print "Mlat report: %s" % msg
if __name__ == '__main__':
usage = "%prog: [options] output filename"
parser = OptionParser(option_class=eng_option, usage=usage)
parser.add_option("-R", "--rx-subdev-spec", type="string",
help="select USRP Rx side A or B", metavar="SUBDEV")
parser.add_option("-A", "--antenna", type="string",
help="select which antenna to use on daughterboard")
parser.add_option("-D", "--args", type="string",
help="arguments to pass to UHD/RTL constructor", default="")
parser.add_option("-f", "--freq", type="eng_float", default=1090e6,
help="set receive frequency in Hz [default=%default]", metavar="FREQ")
parser.add_option("-g", "--gain", type="int", default=None,
help="set RF gain", metavar="dB")
parser.add_option("-r", "--rate", type="eng_float", default=4000000,
help="set ADC sample rate [default=%default]")
parser.add_option("-T", "--threshold", type="eng_float", default=5.0,
help="set pulse detection threshold above noise in dB [default=%default]")
parser.add_option("-a","--output-all", action="store_true", default=False,
help="output all frames")
parser.add_option("-F","--filename", type="string", default=None,
help="read data from file instead of USRP")
parser.add_option("-K","--kml", type="string", default=None,
help="filename for Google Earth KML output")
parser.add_option("-P","--sbs1", action="store_true", default=False,
help="open an SBS-1-compatible server on port 30003")
parser.add_option("-w","--raw", action="store_true", default=False,
help="open a server outputting raw timestamped data on port 9988")
parser.add_option("-n","--no-print", action="store_true", default=False,
help="disable printing decoded packets to stdout")
parser.add_option("-l","--location", type="string", default=None,
help="GPS coordinates of receiving station in format xx.xxxxx,xx.xxxxx")
parser.add_option("-u","--udp", type="int", default=None,
help="Use UDP source on specified port")
parser.add_option("-m","--multiplayer", type="string", default=None,
help="FlightGear server to send aircraft data, in format host:port")
parser.add_option("-d","--rtlsdr", action="store_true", default=False,
help="Use RTLSDR dongle instead of UHD source")
parser.add_option("-p","--pmf", action="store_true", default=False,
help="Use pulse matched filtering")
parser.add_option("-s","--mlat-server", type="string", default=None,
help="Use a multilateration server to track non-ADS-B aircraft")
(options, args) = parser.parse_args()
#construct the radio
context = zmq.Context(1)
tb = air_modes.modes_radio(options, context)
servers = ["inproc://modes-radio-pub"]
if options.remote is not None:
servers += options.remote.split(",")
relay = air_modes.zmq_pubsub_iface(context, subaddr=servers, pubaddr=None)
publisher = pubsub()
relay.subscribe("dl_data", air_modes.make_parser(publisher))
if options.location is not None:
reader = csv.reader([options.location], quoting=csv.QUOTE_NONNUMERIC)
my_position = reader.next()
my_position = [float(n) for n in options.location.split(",")]
queue = gr.msg_queue()
mlat_queue = None
outputs = [] #registry of plugin output functions
mlat_outputs = [] #registry of plugin mlat handling functions
updates = [] #registry of plugin update functions
if options.raw is True:
rawport = air_modes.raw_server(9988) #port
outputs.append(rawport.output)
outputs.append(printraw)
updates.append(rawport.add_pending_conns)
#CPR decoder obj to handle getting position from BDS0,5 and BDS0,6 pkts
cpr_dec = air_modes.cpr_decoder(my_position)
if options.kml is not None:
#we spawn a thread to run every 30 seconds (or whatever) to generate KML
dbname = 'adsb.db'
lock = threading.Lock()
sqldb = air_modes.output_sql(my_position, dbname, lock) #input into the db
sqldb = air_modes.output_sql(cpr_dec, dbname, lock, publisher) #input into the db
kmlgen = air_modes.output_kml(options.kml, dbname, my_position, lock) #create a KML generating thread to read from the db
outputs.append(sqldb.output)
if options.sbs1 is True:
sbs1port = air_modes.output_sbs1(my_position, 30003)
outputs.append(sbs1port.output)
updates.append(sbs1port.add_pending_conns)
if options.no_print is not True:
outputs.append(air_modes.output_print(my_position).output)
printer = air_modes.output_print(cpr_dec, publisher)
if options.multiplayer is not None:
[fghost, fgport] = options.multiplayer.split(':')
fgout = air_modes.output_flightgear(my_position, fghost, int(fgport))
outputs.append(fgout.output)
fgout = air_modes.output_flightgear(cpr_dec, fghost, int(fgport), publisher)
fg = adsb_rx_block(options, args, queue)
if options.sbs1 is True:
sbs1port = air_modes.output_sbs1(cpr_dec, 30003, publisher)
if options.mlat_server is not None:
mlat_queue = gr.msg_queue()
mlat_client = air_modes.mlat_client(mlat_queue, my_position, options.mlat_server, fg.time_source)
outputs.append(mlat_client.output) #output to server when we get a report
updates.append(mlat_client.get_mlat_positions) #check for replies from the server
#note: this means that get_mlat_positions and printmlat execute in the same thread.
#you could just have mlat_client spawn a thread to check its socket. might make more sense to do this.
mlat_outputs.append(printmlat)
runner = top_block_runner(fg)
tb.run()
time.sleep(0.2)
tb.close()
time.sleep(0.2)
relay.close()
while 1:
try:
#handle the once-per-loop updates (check for mlat responses, add TCP conns to SBS-1 plugin, etc.)
for update in updates:
update()
#main message handler
if not queue.empty_p() :
while not queue.empty_p() :
msg = queue.delete_head() #blocking read
[data, ecc, reference, timestamp_int, timestamp_frac] = msg.to_string().split()
#error handling? creating a modes_reply can throw NoHandlerError, but you really want that to be handled by the output plugin in question.
msg_tuple = modes_report(modes_reply(long(data, 16)), long(ecc, 16), float(reference), stamp(int(timestamp_int), float(timestamp_frac)))
for out in outputs:
try:
out(msg_tuple)
except air_modes.ADSBError:
pass
if options.kml is not None:
kmlgen.close()
if mlat_queue:
if not mlat_queue.empty_p():
while not mlat_queue.empty_p():
msg = mlat_queue.delete_head()
for out in mlat_outputs:
try:
out(msg.to_string())
except air_modes.ADSBError:
pass
if runner.done:
raise KeyboardInterrupt
else:
time.sleep(0.1)
except KeyboardInterrupt:
fg.stop()
runner = None
if options.kml is not None:
kmlgen.done = True
break
if __name__ == '__main__':
main()

View File

@@ -58,7 +58,7 @@
# the new option.
# E.g. my_install(TARGETS foo DESTINATION OPTIONAL) would result in
# MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION would
# be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor.
# be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefore.
#=============================================================================
# Copyright 2010 Alexander Neundorf <neundorf@kde.org>

View File

@@ -1,26 +0,0 @@
INCLUDE(FindPkgConfig)
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_INCLUDEDIR}
PATHS /usr/local/include/gnuradio
/usr/include/gnuradio
)
FIND_LIBRARY(
GNURADIO_CORE_LIBRARIES
NAMES gnuradio-core
HINTS $ENV{GNURADIO_CORE_DIR}/lib
${PC_GNURADIO_CORE_LIBDIR}
PATHS /usr/local/lib
/usr/local/lib64
/usr/lib
/usr/lib64
)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_CORE DEFAULT_MSG GNURADIO_CORE_LIBRARIES GNURADIO_CORE_INCLUDE_DIRS)
MARK_AS_ADVANCED(GNURADIO_CORE_LIBRARIES GNURADIO_CORE_INCLUDE_DIRS)

View File

@@ -1,26 +0,0 @@
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_GRUEL gnuradio-core)
FIND_PATH(
GRUEL_INCLUDE_DIRS
NAMES gruel/attributes.h
HINTS $ENV{GRUEL_DIR}/include
${PC_GRUEL_INCLUDEDIR}
PATHS /usr/local/include
/usr/include
)
FIND_LIBRARY(
GRUEL_LIBRARIES
NAMES gruel
HINTS $ENV{GRUEL_DIR}/lib
${PC_GRUEL_LIBDIR}
PATHS /usr/local/lib
/usr/local/lib64
/usr/lib
/usr/lib64
)
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GRUEL DEFAULT_MSG GRUEL_LIBRARIES GRUEL_INCLUDE_DIRS)
MARK_AS_ADVANCED(GRUEL_LIBRARIES GRUEL_INCLUDE_DIRS)

View File

@@ -1,24 +0,0 @@
# Copyright (c) 2007, Simon Edwards <simon@simonzone.com>
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
import PyQt4.pyqtconfig
pyqtcfg = PyQt4.pyqtconfig.Configuration()
print("pyqt_version:%06.0x" % pyqtcfg.pyqt_version)
print("pyqt_version_str:%s" % pyqtcfg.pyqt_version_str)
pyqt_version_tag = ""
in_t = False
for item in pyqtcfg.pyqt_sip_flags.split(' '):
if item=="-t":
in_t = True
elif in_t:
if item.startswith("Qt_4"):
pyqt_version_tag = item
else:
in_t = False
print("pyqt_version_tag:%s" % pyqt_version_tag)
print("pyqt_sip_dir:%s" % pyqtcfg.pyqt_sip_dir)
print("pyqt_sip_flags:%s" % pyqtcfg.pyqt_sip_flags)

View File

@@ -1,53 +0,0 @@
# Find PyQt4
# ~~~~~~~~~~
# Copyright (c) 2007-2008, Simon Edwards <simon@simonzone.com>
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
#
# PyQt4 website: http://www.riverbankcomputing.co.uk/pyqt/index.php
#
# Find the installed version of PyQt4. FindPyQt4 should only be called after
# Python has been found.
#
# This file defines the following variables:
#
# PYQT4_VERSION - The version of PyQt4 found expressed as a 6 digit hex number
# suitable for comparision as a string
#
# PYQT4_VERSION_STR - The version of PyQt4 as a human readable string.
#
# PYQT4_VERSION_TAG - The PyQt version tag using by PyQt's sip files.
#
# PYQT4_SIP_DIR - The directory holding the PyQt4 .sip files.
#
# PYQT4_SIP_FLAGS - The SIP flags used to build PyQt.
IF(EXISTS PYQT4_VERSION)
# Already in cache, be silent
SET(PYQT4_FOUND TRUE)
ELSE(EXISTS PYQT4_VERSION)
FIND_FILE(_find_pyqt_py FindPyQt.py PATHS ${CMAKE_MODULE_PATH})
EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} ${_find_pyqt_py} OUTPUT_VARIABLE pyqt_config)
IF(pyqt_config)
STRING(REGEX REPLACE "^pyqt_version:([^\n]+).*$" "\\1" PYQT4_VERSION ${pyqt_config})
STRING(REGEX REPLACE ".*\npyqt_version_str:([^\n]+).*$" "\\1" PYQT4_VERSION_STR ${pyqt_config})
STRING(REGEX REPLACE ".*\npyqt_version_tag:([^\n]+).*$" "\\1" PYQT4_VERSION_TAG ${pyqt_config})
STRING(REGEX REPLACE ".*\npyqt_sip_dir:([^\n]+).*$" "\\1" PYQT4_SIP_DIR ${pyqt_config})
STRING(REGEX REPLACE ".*\npyqt_sip_flags:([^\n]+).*$" "\\1" PYQT4_SIP_FLAGS ${pyqt_config})
SET(PYQT4_FOUND TRUE)
ENDIF(pyqt_config)
IF(PYQT4_FOUND)
IF(NOT PYQT4_FIND_QUIETLY)
MESSAGE(STATUS "Found PyQt4 version: ${PYQT4_VERSION_STR}")
ENDIF(NOT PYQT4_FIND_QUIETLY)
ELSE(PYQT4_FOUND)
IF(PYQT4_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find Python")
ENDIF(PYQT4_FIND_REQUIRED)
ENDIF(PYQT4_FOUND)
ENDIF(EXISTS PYQT4_VERSION)

View File

@@ -1,30 +0,0 @@
# - try to find Qwt libraries and include files
# QWT_INCLUDE_DIR where to find qwt_plot.h, etc.
# QWT_LIBRARIES libraries to link against
# QWT_FOUND If false, do not try to use Qwt
find_path (QWT_INCLUDE_DIRS
NAMES qwt_plot.h
PATHS
/usr/local/include/qwt-qt4
/usr/local/include/qwt
/usr/include/qwt-qt4
/usr/include/qwt
/opt/local/include/qwt
/sw/include/qwt
)
find_library (QWT_LIBRARIES
NAMES qwt-qt4 qwt
PATHS
/usr/local/lib
/usr/lib
/opt/local/lib
/sw/lib
)
# handle the QUIETLY and REQUIRED arguments and set QWT_FOUND to TRUE if
# all listed variables are TRUE
include ( FindPackageHandleStandardArgs )
find_package_handle_standard_args( Qwt DEFAULT_MSG QWT_LIBRARIES QWT_INCLUDE_DIRS )
MARK_AS_ADVANCED(QWT_LIBRARIES QWT_INCLUDE_DIRS)

View File

@@ -1,210 +0,0 @@
# Copyright 2010-2011 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
if(DEFINED __INCLUDED_GR_MISC_UTILS_CMAKE)
return()
endif()
set(__INCLUDED_GR_MISC_UTILS_CMAKE TRUE)
########################################################################
# Set global variable macro.
# Used for subdirectories to export settings.
# Example: include and library paths.
########################################################################
function(GR_SET_GLOBAL var)
set(${var} ${ARGN} CACHE INTERNAL "" FORCE)
endfunction(GR_SET_GLOBAL)
########################################################################
# Set the pre-processor definition if the condition is true.
# - def the pre-processor definition to set and condition name
########################################################################
function(GR_ADD_COND_DEF def)
if(${def})
add_definitions(-D${def})
endif(${def})
endfunction(GR_ADD_COND_DEF)
########################################################################
# Check for a header and conditionally set a compile define.
# - hdr the relative path to the header file
# - def the pre-processor definition to set
########################################################################
function(GR_CHECK_HDR_N_DEF hdr def)
include(CheckIncludeFileCXX)
CHECK_INCLUDE_FILE_CXX(${hdr} ${def})
GR_ADD_COND_DEF(${def})
endfunction(GR_CHECK_HDR_N_DEF)
########################################################################
# Include subdirectory macro.
# Sets the CMake directory variables,
# includes the subdirectory CMakeLists.txt,
# resets the CMake directory variables.
#
# This macro includes subdirectories rather than adding them
# so that the subdirectory can affect variables in the level above.
# This provides a work-around for the lack of convenience libraries.
# This way a subdirectory can append to the list of library sources.
########################################################################
macro(GR_INCLUDE_SUBDIRECTORY subdir)
#insert the current directories on the front of the list
list(INSERT _cmake_source_dirs 0 ${CMAKE_CURRENT_SOURCE_DIR})
list(INSERT _cmake_binary_dirs 0 ${CMAKE_CURRENT_BINARY_DIR})
#set the current directories to the names of the subdirs
set(CMAKE_CURRENT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${subdir})
set(CMAKE_CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${subdir})
#include the subdirectory CMakeLists to run it
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt)
#reset the value of the current directories
list(GET _cmake_source_dirs 0 CMAKE_CURRENT_SOURCE_DIR)
list(GET _cmake_binary_dirs 0 CMAKE_CURRENT_BINARY_DIR)
#pop the subdir names of the front of the list
list(REMOVE_AT _cmake_source_dirs 0)
list(REMOVE_AT _cmake_binary_dirs 0)
endmacro(GR_INCLUDE_SUBDIRECTORY)
########################################################################
# Check if a compiler flag works and conditionally set a compile define.
# - flag the compiler flag to check for
# - have the variable to set with result
########################################################################
macro(GR_ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have)
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG(${flag} ${have})
if(${have})
add_definitions(${flag})
endif(${have})
endmacro(GR_ADD_CXX_COMPILER_FLAG_IF_AVAILABLE)
########################################################################
# Generates the .la libtool file
# This appears to generate libtool files that cannot be used by auto*.
# Usage GR_LIBTOOL(TARGET [target] DESTINATION [dest])
# Notice: there is not COMPONENT option, these will not get distributed.
########################################################################
function(GR_LIBTOOL)
if(NOT DEFINED GENERATE_LIBTOOL)
set(GENERATE_LIBTOOL OFF) #disabled by default
endif()
if(GENERATE_LIBTOOL)
include(CMakeParseArgumentsCopy)
CMAKE_PARSE_ARGUMENTS(GR_LIBTOOL "" "TARGET;DESTINATION" "" ${ARGN})
find_program(LIBTOOL libtool)
if(LIBTOOL)
include(CMakeMacroLibtoolFile)
CREATE_LIBTOOL_FILE(${GR_LIBTOOL_TARGET} /${GR_LIBTOOL_DESTINATION})
endif(LIBTOOL)
endif(GENERATE_LIBTOOL)
endfunction(GR_LIBTOOL)
########################################################################
# Do standard things to the library target
# - set target properties
# - make install rules
# Also handle gnuradio custom naming conventions w/ extras mode.
########################################################################
function(GR_LIBRARY_FOO target)
#parse the arguments for component names
include(CMakeParseArgumentsCopy)
CMAKE_PARSE_ARGUMENTS(GR_LIBRARY "" "RUNTIME_COMPONENT;DEVEL_COMPONENT" "" ${ARGN})
#set additional target properties
set_target_properties(${target} PROPERTIES SOVERSION ${LIBVER})
#install the generated files like so...
install(TARGETS ${target}
LIBRARY DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} # .so/.dylib file
ARCHIVE DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_DEVEL_COMPONENT} # .lib file
RUNTIME DESTINATION ${GR_RUNTIME_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} # .dll file
)
#extras mode enabled automatically on linux
if(NOT DEFINED LIBRARY_EXTRAS)
set(LIBRARY_EXTRAS ${LINUX})
endif()
#special extras mode to enable alternative naming conventions
if(LIBRARY_EXTRAS)
#create .la file before changing props
GR_LIBTOOL(TARGET ${target} DESTINATION ${GR_LIBRARY_DIR})
#give the library a special name with ultra-zero soversion
set_target_properties(${target} PROPERTIES OUTPUT_NAME ${target}-${LIBVER} SOVERSION "0.0.0")
set(target_name lib${target}-${LIBVER}.so.0.0.0)
#custom command to generate symlinks
add_custom_command(
TARGET ${target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink ${target_name} ${CMAKE_CURRENT_BINARY_DIR}/lib${target}.so
COMMAND ${CMAKE_COMMAND} -E create_symlink ${target_name} ${CMAKE_CURRENT_BINARY_DIR}/lib${target}-${LIBVER}.so.0
COMMAND ${CMAKE_COMMAND} -E touch ${target_name} #so the symlinks point to something valid so cmake 2.6 will install
)
#and install the extra symlinks
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/lib${target}.so
${CMAKE_CURRENT_BINARY_DIR}/lib${target}-${LIBVER}.so.0
DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT}
)
endif(LIBRARY_EXTRAS)
endfunction(GR_LIBRARY_FOO)
########################################################################
# Create a dummy custom command that depends on other targets.
# Usage:
# GR_GEN_TARGET_DEPS(unique_name target_deps <target1> <target2> ...)
# ADD_CUSTOM_COMMAND(<the usual args> ${target_deps})
#
# Custom command cant depend on targets, but can depend on executables,
# and executables can depend on targets. So this is the process:
########################################################################
function(GR_GEN_TARGET_DEPS name var)
file(
WRITE ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp.in
"int main(void){return 0;}\n"
)
execute_process(
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp.in
${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp
)
add_executable(${name} ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp)
if(ARGN)
add_dependencies(${name} ${ARGN})
endif(ARGN)
if(CMAKE_CROSSCOMPILING)
set(${var} "DEPENDS;${name}" PARENT_SCOPE) #cant call command when cross
else()
set(${var} "DEPENDS;${name};COMMAND;${name}" PARENT_SCOPE)
endif()
endfunction(GR_GEN_TARGET_DEPS)

View File

@@ -1,54 +0,0 @@
# Copyright 2011 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
if(DEFINED __INCLUDED_GR_PLATFORM_CMAKE)
return()
endif()
set(__INCLUDED_GR_PLATFORM_CMAKE TRUE)
########################################################################
# Setup additional defines for OS types
########################################################################
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(LINUX TRUE)
endif()
if(LINUX AND EXISTS "/etc/debian_version")
set(DEBIAN TRUE)
endif()
if(LINUX AND EXISTS "/etc/redhat-release")
set(REDHAT TRUE)
endif()
if(LINUX AND EXISTS "/etc/slackware-version")
set(SLACKWARE TRUE)
endif()
########################################################################
# when the library suffix should be 64 (applies to redhat linux family)
########################################################################
if (REDHAT OR SLACKWARE)
set(LIB64_CONVENTION TRUE)
endif()
if(NOT DEFINED LIB_SUFFIX AND LIB64_CONVENTION AND CMAKE_SYSTEM_PROCESSOR MATCHES "64$")
set(LIB_SUFFIX 64)
endif()
set(LIB_SUFFIX ${LIB_SUFFIX} CACHE STRING "lib directory suffix")

View File

@@ -1,232 +0,0 @@
# Copyright 2010-2011 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
if(DEFINED __INCLUDED_GR_PYTHON_CMAKE)
return()
endif()
set(__INCLUDED_GR_PYTHON_CMAKE TRUE)
########################################################################
# Setup the python interpreter:
# This allows the user to specify a specific interpreter,
# or finds the interpreter via the built-in cmake module.
########################################################################
#this allows the user to override PYTHON_EXECUTABLE
if(PYTHON_EXECUTABLE)
set(PYTHONINTERP_FOUND TRUE)
#otherwise if not set, try to automatically find it
else(PYTHON_EXECUTABLE)
#use the built-in find script
find_package(PythonInterp)
#and if that fails use the find program routine
if(NOT PYTHONINTERP_FOUND)
find_program(PYTHON_EXECUTABLE NAMES python python2.7 python2.6 python2.5)
if(PYTHON_EXECUTABLE)
set(PYTHONINTERP_FOUND TRUE)
endif(PYTHON_EXECUTABLE)
endif(NOT PYTHONINTERP_FOUND)
endif(PYTHON_EXECUTABLE)
#make the path to the executable appear in the cmake gui
set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} CACHE FILEPATH "python interpreter")
#make sure we can use -B with python (introduced in 2.6)
if(PYTHON_EXECUTABLE)
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -B -c ""
OUTPUT_QUIET ERROR_QUIET
RESULT_VARIABLE PYTHON_HAS_DASH_B_RESULT
)
if(PYTHON_HAS_DASH_B_RESULT EQUAL 0)
set(PYTHON_DASH_B "-B")
endif()
endif(PYTHON_EXECUTABLE)
########################################################################
# Check for the existence of a python module:
# - desc a string description of the check
# - mod the name of the module to import
# - cmd an additional command to run
# - have the result variable to set
########################################################################
macro(GR_PYTHON_CHECK_MODULE desc mod cmd have)
message(STATUS "")
message(STATUS "Python checking for ${desc}")
execute_process(
COMMAND ${PYTHON_EXECUTABLE} -c "
#########################################
try:
import ${mod}
assert ${cmd}
except ImportError, AssertionError: exit(-1)
except: pass
#########################################"
RESULT_VARIABLE ${have}
)
if(${have} EQUAL 0)
message(STATUS "Python checking for ${desc} - found")
set(${have} TRUE)
else(${have} EQUAL 0)
message(STATUS "Python checking for ${desc} - not found")
set(${have} FALSE)
endif(${have} EQUAL 0)
endmacro(GR_PYTHON_CHECK_MODULE)
########################################################################
# Sets the python installation directory GR_PYTHON_DIR
########################################################################
execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "
from distutils import sysconfig
print sysconfig.get_python_lib(plat_specific=True, prefix='')
" OUTPUT_VARIABLE GR_PYTHON_DIR OUTPUT_STRIP_TRAILING_WHITESPACE
)
file(TO_CMAKE_PATH ${GR_PYTHON_DIR} GR_PYTHON_DIR)
########################################################################
# Create an always-built target with a unique name
# Usage: GR_UNIQUE_TARGET(<description> <dependencies list>)
########################################################################
function(GR_UNIQUE_TARGET desc)
file(RELATIVE_PATH reldir ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR})
execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import re, hashlib
unique = hashlib.md5('${reldir}${ARGN}').hexdigest()[:5]
print(re.sub('\\W', '_', '${desc} ${reldir} ' + unique))"
OUTPUT_VARIABLE _target OUTPUT_STRIP_TRAILING_WHITESPACE)
add_custom_target(${_target} ALL DEPENDS ${ARGN})
endfunction(GR_UNIQUE_TARGET)
########################################################################
# Install python sources (also builds and installs byte-compiled python)
########################################################################
function(GR_PYTHON_INSTALL)
include(CMakeParseArgumentsCopy)
CMAKE_PARSE_ARGUMENTS(GR_PYTHON_INSTALL "" "DESTINATION;COMPONENT" "FILES;PROGRAMS" ${ARGN})
####################################################################
if(GR_PYTHON_INSTALL_FILES)
####################################################################
install(${ARGN}) #installs regular python files
#create a list of all generated files
unset(pysrcfiles)
unset(pycfiles)
unset(pyofiles)
foreach(pyfile ${GR_PYTHON_INSTALL_FILES})
get_filename_component(pyfile ${pyfile} ABSOLUTE)
list(APPEND pysrcfiles ${pyfile})
#determine if this file is in the source or binary directory
file(RELATIVE_PATH source_rel_path ${CMAKE_CURRENT_SOURCE_DIR} ${pyfile})
string(LENGTH "${source_rel_path}" source_rel_path_len)
file(RELATIVE_PATH binary_rel_path ${CMAKE_CURRENT_BINARY_DIR} ${pyfile})
string(LENGTH "${binary_rel_path}" binary_rel_path_len)
#and set the generated path appropriately
if(${source_rel_path_len} GREATER ${binary_rel_path_len})
set(pygenfile ${CMAKE_CURRENT_BINARY_DIR}/${binary_rel_path})
else()
set(pygenfile ${CMAKE_CURRENT_BINARY_DIR}/${source_rel_path})
endif()
list(APPEND pycfiles ${pygenfile}c)
list(APPEND pyofiles ${pygenfile}o)
#ensure generation path exists
get_filename_component(pygen_path ${pygenfile} PATH)
file(MAKE_DIRECTORY ${pygen_path})
endforeach(pyfile)
#the command to generate the pyc files
add_custom_command(
DEPENDS ${pysrcfiles} OUTPUT ${pycfiles}
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_BINARY_DIR}/python_compile_helper.py ${pysrcfiles} ${pycfiles}
)
#the command to generate the pyo files
add_custom_command(
DEPENDS ${pysrcfiles} OUTPUT ${pyofiles}
COMMAND ${PYTHON_EXECUTABLE} -O ${CMAKE_BINARY_DIR}/python_compile_helper.py ${pysrcfiles} ${pyofiles}
)
#create install rule and add generated files to target list
set(python_install_gen_targets ${pycfiles} ${pyofiles})
install(FILES ${python_install_gen_targets}
DESTINATION ${GR_PYTHON_INSTALL_DESTINATION}
COMPONENT ${GR_PYTHON_INSTALL_COMPONENT}
)
####################################################################
elseif(GR_PYTHON_INSTALL_PROGRAMS)
####################################################################
file(TO_NATIVE_PATH ${PYTHON_EXECUTABLE} pyexe_native)
if (CMAKE_CROSSCOMPILING)
set(pyexe_native /usr/bin/env python)
endif()
foreach(pyfile ${GR_PYTHON_INSTALL_PROGRAMS})
get_filename_component(pyfile_name ${pyfile} NAME)
get_filename_component(pyfile ${pyfile} ABSOLUTE)
string(REPLACE "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" pyexefile "${pyfile}.exe")
list(APPEND python_install_gen_targets ${pyexefile})
get_filename_component(pyexefile_path ${pyexefile} PATH)
file(MAKE_DIRECTORY ${pyexefile_path})
add_custom_command(
OUTPUT ${pyexefile} DEPENDS ${pyfile}
COMMAND ${PYTHON_EXECUTABLE} -c
\"open('${pyexefile}', 'w').write('\#!${pyexe_native}\\n'+open('${pyfile}').read())\"
COMMENT "Shebangin ${pyfile_name}"
)
#on windows, python files need an extension to execute
get_filename_component(pyfile_ext ${pyfile} EXT)
if(WIN32 AND NOT pyfile_ext)
set(pyfile_name "${pyfile_name}.py")
endif()
install(PROGRAMS ${pyexefile} RENAME ${pyfile_name}
DESTINATION ${GR_PYTHON_INSTALL_DESTINATION}
COMPONENT ${GR_PYTHON_INSTALL_COMPONENT}
)
endforeach(pyfile)
endif()
GR_UNIQUE_TARGET("pygen" ${python_install_gen_targets})
endfunction(GR_PYTHON_INSTALL)
########################################################################
# Write the python helper script that generates byte code files
########################################################################
file(WRITE ${CMAKE_BINARY_DIR}/python_compile_helper.py "
import sys, py_compile
files = sys.argv[1:]
srcs, gens = files[:len(files)/2], files[len(files)/2:]
for src, gen in zip(srcs, gens):
py_compile.compile(file=src, cfile=gen, doraise=True)
")

View File

@@ -1,232 +0,0 @@
# Copyright 2010-2011 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
if(DEFINED __INCLUDED_GR_SWIG_CMAKE)
return()
endif()
set(__INCLUDED_GR_SWIG_CMAKE TRUE)
include(GrPython)
########################################################################
# Builds a swig documentation file to be generated into python docstrings
# Usage: GR_SWIG_MAKE_DOCS(output_file input_path input_path....)
#
# Set the following variable to specify extra dependent targets:
# - GR_SWIG_DOCS_SOURCE_DEPS
# - GR_SWIG_DOCS_TARGET_DEPS
########################################################################
function(GR_SWIG_MAKE_DOCS output_file)
if(ENABLE_DOXYGEN)
#setup the input files variable list, quote formated
set(input_files)
unset(INPUT_PATHS)
foreach(input_path ${ARGN})
if (IS_DIRECTORY ${input_path}) #when input path is a directory
file(GLOB input_path_h_files ${input_path}/*.h)
else() #otherwise its just a file, no glob
set(input_path_h_files ${input_path})
endif()
list(APPEND input_files ${input_path_h_files})
set(INPUT_PATHS "${INPUT_PATHS} \"${input_path}\"")
endforeach(input_path)
#determine the output directory
get_filename_component(name ${output_file} NAME_WE)
get_filename_component(OUTPUT_DIRECTORY ${output_file} PATH)
set(OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}/${name}_swig_docs)
make_directory(${OUTPUT_DIRECTORY})
#generate the Doxyfile used by doxygen
configure_file(
${CMAKE_SOURCE_DIR}/docs/doxygen/Doxyfile.swig_doc.in
${OUTPUT_DIRECTORY}/Doxyfile
@ONLY)
#Create a dummy custom command that depends on other targets
include(GrMiscUtils)
GR_GEN_TARGET_DEPS(_${name}_tag tag_deps ${GR_SWIG_DOCS_TARGET_DEPS})
#call doxygen on the Doxyfile + input headers
add_custom_command(
OUTPUT ${OUTPUT_DIRECTORY}/xml/index.xml
DEPENDS ${input_files} ${GR_SWIG_DOCS_SOURCE_DEPS} ${tag_deps}
COMMAND ${DOXYGEN_EXECUTABLE} ${OUTPUT_DIRECTORY}/Doxyfile
COMMENT "Generating doxygen xml for ${name} docs"
)
#call the swig_doc script on the xml files
add_custom_command(
OUTPUT ${output_file}
DEPENDS ${input_files} ${stamp-file} ${OUTPUT_DIRECTORY}/xml/index.xml
COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B}
${CMAKE_SOURCE_DIR}/docs/doxygen/swig_doc.py
${OUTPUT_DIRECTORY}/xml
${output_file}
COMMENT "Generating python docstrings for ${name}"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs/doxygen
)
else(ENABLE_DOXYGEN)
file(WRITE ${output_file} "\n") #no doxygen -> empty file
endif(ENABLE_DOXYGEN)
endfunction(GR_SWIG_MAKE_DOCS)
########################################################################
# Build a swig target for the common gnuradio use case. Usage:
# GR_SWIG_MAKE(target ifile ifile ifile...)
#
# Set the following variables before calling:
# - GR_SWIG_FLAGS
# - GR_SWIG_INCLUDE_DIRS
# - GR_SWIG_LIBRARIES
# - GR_SWIG_SOURCE_DEPS
# - GR_SWIG_TARGET_DEPS
# - GR_SWIG_DOC_FILE
# - GR_SWIG_DOC_DIRS
########################################################################
macro(GR_SWIG_MAKE name)
set(ifiles ${ARGN})
list(APPEND GR_SWIG_TARGET_DEPS ${GR_SWIG_LIBRARIES})
#do swig doc generation if specified
if (GR_SWIG_DOC_FILE)
set(GR_SWIG_DOCS_SOURCE_DEPS ${GR_SWIG_SOURCE_DEPS})
set(GR_SWIG_DOCS_TAREGT_DEPS ${GR_SWIG_TARGET_DEPS})
GR_SWIG_MAKE_DOCS(${GR_SWIG_DOC_FILE} ${GR_SWIG_DOC_DIRS})
add_custom_target(${name}_swig_doc DEPENDS ${GR_SWIG_DOC_FILE})
list(APPEND GR_SWIG_TARGET_DEPS ${name}_swig_doc)
endif()
#append additional include directories
find_package(PythonLibs)
list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_PATH}) #deprecated name (now dirs)
list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS})
list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR})
#determine include dependencies for swig file
execute_process(
COMMAND ${PYTHON_EXECUTABLE}
${CMAKE_BINARY_DIR}/get_swig_deps.py
"${ifiles}" "${GR_SWIG_INCLUDE_DIRS}"
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE SWIG_MODULE_${name}_EXTRA_DEPS
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
#Create a dummy custom command that depends on other targets
include(GrMiscUtils)
GR_GEN_TARGET_DEPS(_${name}_swig_tag tag_deps ${GR_SWIG_TARGET_DEPS})
set(tag_file ${CMAKE_CURRENT_BINARY_DIR}/${name}.tag)
add_custom_command(
OUTPUT ${tag_file}
DEPENDS ${GR_SWIG_SOURCE_DEPS} ${tag_deps}
COMMAND ${CMAKE_COMMAND} -E touch ${tag_file}
)
#append the specified include directories
include_directories(${GR_SWIG_INCLUDE_DIRS})
list(APPEND SWIG_MODULE_${name}_EXTRA_DEPS ${tag_file})
#setup the swig flags with flags and include directories
set(CMAKE_SWIG_FLAGS -fvirtual -modern -keyword -w511 -module ${name} ${GR_SWIG_FLAGS})
foreach(dir ${GR_SWIG_INCLUDE_DIRS})
list(APPEND CMAKE_SWIG_FLAGS "-I${dir}")
endforeach(dir)
#set the C++ property on the swig .i file so it builds
set_source_files_properties(${ifiles} PROPERTIES CPLUSPLUS ON)
#setup the actual swig library target to be built
include(UseSWIG)
SWIG_ADD_MODULE(${name} python ${ifiles})
SWIG_LINK_LIBRARIES(${name} ${PYTHON_LIBRARIES} ${GR_SWIG_LIBRARIES})
endmacro(GR_SWIG_MAKE)
########################################################################
# Install swig targets generated by GR_SWIG_MAKE. Usage:
# GR_SWIG_INSTALL(
# TARGETS target target target...
# [DESTINATION destination]
# [COMPONENT component]
# )
########################################################################
macro(GR_SWIG_INSTALL)
include(CMakeParseArgumentsCopy)
CMAKE_PARSE_ARGUMENTS(GR_SWIG_INSTALL "" "DESTINATION;COMPONENT" "TARGETS" ${ARGN})
foreach(name ${GR_SWIG_INSTALL_TARGETS})
install(TARGETS ${SWIG_MODULE_${name}_REAL_NAME}
DESTINATION ${GR_SWIG_INSTALL_DESTINATION}
COMPONENT ${GR_SWIG_INSTALL_COMPONENT}
)
include(GrPython)
GR_PYTHON_INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${name}.py
DESTINATION ${GR_SWIG_INSTALL_DESTINATION}
COMPONENT ${GR_SWIG_INSTALL_COMPONENT}
)
GR_LIBTOOL(
TARGET ${SWIG_MODULE_${name}_REAL_NAME}
DESTINATION ${GR_SWIG_INSTALL_DESTINATION}
)
endforeach(name)
endmacro(GR_SWIG_INSTALL)
########################################################################
# Generate a python file that can determine swig dependencies.
# Used by the make macro above to determine extra dependencies.
# When you build C++, CMake figures out the header dependencies.
# This code essentially performs that logic for swig includes.
########################################################################
file(WRITE ${CMAKE_BINARY_DIR}/get_swig_deps.py "
import os, sys, re
include_matcher = re.compile('[#|%]include\\s*[<|\"](.*)[>|\"]')
include_dirs = sys.argv[2].split(';')
def get_swig_incs(file_path):
file_contents = open(file_path, 'r').read()
return include_matcher.findall(file_contents, re.MULTILINE)
def get_swig_deps(file_path, level):
deps = [file_path]
if level == 0: return deps
for inc_file in get_swig_incs(file_path):
for inc_dir in include_dirs:
inc_path = os.path.join(inc_dir, inc_file)
if not os.path.exists(inc_path): continue
deps.extend(get_swig_deps(inc_path, level-1))
return deps
if __name__ == '__main__':
ifiles = sys.argv[1].split(';')
deps = sum([get_swig_deps(ifile, 3) for ifile in ifiles], [])
#sys.stderr.write(';'.join(set(deps)) + '\\n\\n')
print(';'.join(set(deps)))
")

View File

@@ -1,137 +0,0 @@
# Copyright 2010-2011 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
if(DEFINED __INCLUDED_GR_TEST_CMAKE)
return()
endif()
set(__INCLUDED_GR_TEST_CMAKE TRUE)
########################################################################
# Add a unit test and setup the environment for a unit test.
# Takes the same arguments as the ADD_TEST function.
#
# Before calling set the following variables:
# GR_TEST_TARGET_DEPS - built targets for the library path
# GR_TEST_LIBRARY_DIRS - directories for the library path
# GR_TEST_PYTHON_DIRS - directories for the python path
########################################################################
function(GR_ADD_TEST test_name)
#Ensure that the build exe also appears in the PATH.
list(APPEND GR_TEST_TARGET_DEPS ${ARGN})
#In the land of windows, all libraries must be in the PATH.
#Since the dependent libraries are not yet installed,
#we must manually set them in the PATH to run tests.
#The following appends the path of a target dependency.
foreach(target ${GR_TEST_TARGET_DEPS})
get_target_property(location ${target} LOCATION)
if(location)
get_filename_component(path ${location} PATH)
string(REGEX REPLACE "\\$\\(.*\\)" ${CMAKE_BUILD_TYPE} path ${path})
list(APPEND GR_TEST_LIBRARY_DIRS ${path})
endif(location)
endforeach(target)
if(WIN32)
#SWIG generates the python library files into a subdirectory.
#Therefore, we must append this subdirectory into PYTHONPATH.
#Only do this for the python directories matching the following:
foreach(pydir ${GR_TEST_PYTHON_DIRS})
get_filename_component(name ${pydir} NAME)
if(name MATCHES "^(swig|lib|src)$")
list(APPEND GR_TEST_PYTHON_DIRS ${pydir}/${CMAKE_BUILD_TYPE})
endif()
endforeach(pydir)
endif(WIN32)
file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR} srcdir)
file(TO_NATIVE_PATH "${GR_TEST_LIBRARY_DIRS}" libpath) #ok to use on dir list?
file(TO_NATIVE_PATH "${GR_TEST_PYTHON_DIRS}" pypath) #ok to use on dir list?
set(environs "GR_DONT_LOAD_PREFS=1" "srcdir=${srcdir}")
#http://www.cmake.org/pipermail/cmake/2009-May/029464.html
#Replaced this add test + set environs code with the shell script generation.
#Its nicer to be able to manually run the shell script to diagnose problems.
#ADD_TEST(${ARGV})
#SET_TESTS_PROPERTIES(${test_name} PROPERTIES ENVIRONMENT "${environs}")
if(UNIX)
set(LD_PATH_VAR "LD_LIBRARY_PATH")
if(APPLE)
set(LD_PATH_VAR "DYLD_LIBRARY_PATH")
endif()
set(binpath "${CMAKE_CURRENT_BINARY_DIR}:$PATH")
list(APPEND libpath "$${LD_PATH_VAR}")
list(APPEND pypath "$PYTHONPATH")
#replace list separator with the path separator
string(REPLACE ";" ":" libpath "${libpath}")
string(REPLACE ";" ":" pypath "${pypath}")
list(APPEND environs "PATH=${binpath}" "${LD_PATH_VAR}=${libpath}" "PYTHONPATH=${pypath}")
#generate a bat file that sets the environment and runs the test
find_program(SHELL sh)
set(sh_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.sh)
file(WRITE ${sh_file} "#!${SHELL}\n")
#each line sets an environment variable
foreach(environ ${environs})
file(APPEND ${sh_file} "export ${environ}\n")
endforeach(environ)
#load the command to run with its arguments
foreach(arg ${ARGN})
file(APPEND ${sh_file} "${arg} ")
endforeach(arg)
file(APPEND ${sh_file} "\n")
#make the shell file executable
execute_process(COMMAND chmod +x ${sh_file})
add_test(${test_name} ${SHELL} ${sh_file})
endif(UNIX)
if(WIN32)
list(APPEND libpath ${DLL_PATHS} "%PATH%")
list(APPEND pypath "%PYTHONPATH%")
#replace list separator with the path separator (escaped)
string(REPLACE ";" "\\;" libpath "${libpath}")
string(REPLACE ";" "\\;" pypath "${pypath}")
list(APPEND environs "PATH=${libpath}" "PYTHONPATH=${pypath}")
#generate a bat file that sets the environment and runs the test
set(bat_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.bat)
file(WRITE ${bat_file} "@echo off\n")
#each line sets an environment variable
foreach(environ ${environs})
file(APPEND ${bat_file} "SET ${environ}\n")
endforeach(environ)
#load the command to run with its arguments
foreach(arg ${ARGN})
file(APPEND ${bat_file} "${arg} ")
endforeach(arg)
file(APPEND ${bat_file} "\n")
add_test(${test_name} ${bat_file})
endif(WIN32)
endfunction(GR_ADD_TEST)

View File

@@ -0,0 +1,31 @@
INCLUDE(FindPkgConfig)
PKG_CHECK_MODULES(PC_AIR_MODES air_modes)
FIND_PATH(
AIR_MODES_INCLUDE_DIRS
NAMES air_modes/api.h
HINTS $ENV{AIR_MODES_DIR}/include
${PC_AIR_MODES_INCLUDEDIR}
PATHS ${CMAKE_INSTALL_PREFIX}/include
/usr/local/include
/usr/include
)
FIND_LIBRARY(
AIR_MODES_LIBRARIES
NAMES gnuradio-air-modes
HINTS $ENV{AIR_MODES_DIR}/lib
${PC_AIR_MODES_LIBDIR}
PATHS ${CMAKE_INSTALL_PREFIX}/lib
${CMAKE_INSTALL_PREFIX}/lib64
/usr/local/lib
/usr/local/lib64
/usr/lib
/usr/lib64
)
include("${CMAKE_CURRENT_LIST_DIR}/air_modesTarget.cmake")
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(AIR_MODES DEFAULT_MSG AIR_MODES_LIBRARIES AIR_MODES_INCLUDE_DIRS)
MARK_AS_ADVANCED(AIR_MODES_LIBRARIES AIR_MODES_INCLUDE_DIRS)

View File

@@ -1,52 +1,26 @@
# Copyright 2018 Free Software Foundation, Inc.
#
# Copyright 2007,2009,2011 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
#
# GNU Radio 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.
#
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
include $(top_srcdir)/Makefile.common
include(CMakeFindDependencyMacro)
EXTRA_DIST = \
example/aadvark.cc \
example/aadvark.h \
example/Doxyfile \
example/xml/aadvark_8cc.xml \
example/xml/aadvark_8h.xml \
example/xml/classAadvark.xml \
example/xml/combine.xslt \
example/xml/compound.xsd \
example/xml/index.xml \
example/xml/index.xsd
if PYTHON
utilspythondir = $(grpythondir)/doxyxml
TESTS = \
run_tests
nobase_utilspython_PYTHON = \
__init__.py \
base.py \
doxyindex.py \
text.py \
generated/__init__.py \
generated/index.py \
generated/indexsuper.py \
generated/compound.py \
generated/compoundsuper.py
endif
set(target_deps "@TARGET_DEPENDENCIES@")
foreach(dep IN LISTS target_deps)
find_dependency(${dep})
endforeach()
include("${CMAKE_CURRENT_LIST_DIR}/@TARGET@Targets.cmake")

View File

@@ -1,21 +1,10 @@
# Copyright 2011 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
# This file was generated by gr_modtool, a tool from the GNU Radio framework
# This file is a part of gr-air-modes
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
########################################################################
# Setup dependencies

View File

@@ -1,21 +1,10 @@
# Copyright 2011 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
# This file was generated by gr_modtool, a tool from the GNU Radio framework
# This file is a part of gr-air-modes
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
########################################################################
# Create the doxygen configuration file
@@ -28,6 +17,7 @@ file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR} abs_top_builddir)
set(HAVE_DOT ${DOXYGEN_DOT_FOUND})
set(enable_html_docs YES)
set(enable_latex_docs NO)
set(enable_mathjax NO)
set(enable_xml_docs YES)
configure_file(

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +0,0 @@
/Makefile
/Makefile.in

View File

@@ -1,23 +1,12 @@
#
# Copyright 2010 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
#
# This file was generated by gr_modtool, a tool from the GNU Radio framework
# This file is a part of gr-air-modes
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
#
"""
Python interface to contents of doxygen xml documentation.
@@ -27,7 +16,7 @@ doxygen-generated xml used in this example.
>>> # Parse the doxygen docs.
>>> import os
>>> this_dir = os.path.dirname(globals()['__file__'])
>>> this_dir = os.path.dirname(globals()['__file__'])
>>> xml_path = this_dir + "/example/xml/"
>>> di = DoxyIndex(xml_path)
@@ -64,11 +53,11 @@ u'Outputs the vital aadvark statistics.'
"""
from doxyindex import DoxyIndex, DoxyFunction, DoxyParam, DoxyClass, DoxyFile, DoxyNamespace, DoxyGroup, DoxyFriend, DoxyOther
from .doxyindex import DoxyIndex, DoxyFunction, DoxyParam, DoxyClass, DoxyFile, DoxyNamespace, DoxyGroup, DoxyFriend, DoxyOther
def _test():
import os
this_dir = os.path.dirname(globals()['__file__'])
this_dir = os.path.dirname(globals()['__file__'])
xml_path = this_dir + "/example/xml/"
di = DoxyIndex(xml_path)
# Get the Aadvark class

Binary file not shown.

Binary file not shown.

View File

@@ -1,23 +1,12 @@
#
# Copyright 2010 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
#
# This file was generated by gr_modtool, a tool from the GNU Radio framework
# This file is a part of gr-air-modes
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
#
"""
A base class is created.
@@ -30,18 +19,18 @@ import pdb
from xml.parsers.expat import ExpatError
from generated import compound
from .generated import compound
class Base(object):
class Duplicate(StandardError):
class Duplicate(Exception):
pass
class NoSuchMember(StandardError):
class NoSuchMember(Exception):
pass
class ParsingError(StandardError):
class ParsingError(Exception):
pass
def __init__(self, parse_data, top=None):
@@ -94,7 +83,7 @@ class Base(object):
for cls in self.mem_classes:
if cls.can_parse(mem):
return cls
raise StandardError(("Did not find a class for object '%s'." \
raise Exception(("Did not find a class for object '%s'." \
% (mem.get_name())))
def convert_mem(self, mem):
@@ -102,11 +91,11 @@ class Base(object):
cls = self.get_cls(mem)
converted = cls.from_parse_data(mem, self.top)
if converted is None:
raise StandardError('No class matched this object.')
raise Exception('No class matched this object.')
self.add_ref(converted)
return converted
except StandardError, e:
print e
except Exception as e:
print(e)
@classmethod
def includes(cls, inst):
@@ -144,7 +133,7 @@ class Base(object):
self._in_category[cat] = [mem for mem in self._members
if cat.includes(mem)]
return self._in_category[cat]
def get_member(self, name, cat=None):
self.confirm_no_error()
# Check if it's in a namespace or class.
@@ -173,7 +162,7 @@ class Base(object):
def members(self):
self.confirm_no_error()
return self._members
def process_memberdefs(self):
mdtss = []
for sec in self._retrieved_data.compounddef.sectiondef:
@@ -188,7 +177,7 @@ class Base(object):
if pair not in uniques:
uniques.add(pair)
self._members.append(converted)
def retrieve_data(self):
filename = os.path.join(self._xml_path, self.refid + '.xml')
try:
@@ -197,7 +186,7 @@ class Base(object):
print('Error in xml in file %s' % filename)
self._error = True
self._retrieved_data = None
def check_parsed(self):
if not self._parsed:
self._parse()

View File

@@ -1,23 +1,12 @@
#
# Copyright 2010 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
#
# This file was generated by gr_modtool, a tool from the GNU Radio framework
# This file is a part of gr-air-modes
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
#
"""
Classes providing more user-friendly interfaces to the doxygen xml
docs than the generated classes provide.
@@ -25,9 +14,9 @@ docs than the generated classes provide.
import os
from generated import index
from base import Base
from text import description
from .generated import index
from .base import Base
from .text import description
class DoxyIndex(Base):
"""
@@ -40,28 +29,23 @@ class DoxyIndex(Base):
if self._parsed:
return
super(DoxyIndex, self)._parse()
self._root = index.parse(os.path.join(self._xml_path, 'index.xml'))
self._root = index.parse(os.path.join(self._xml_path, 'index.xml'))
for mem in self._root.compound:
converted = self.convert_mem(mem)
# For files we want the contents to be accessible directly
# from the parent rather than having to go through the file
# object.
# For files and namespaces we want the contents to be
# accessible directly from the parent rather than having
# to go through the file object.
if self.get_cls(mem) == DoxyFile:
if mem.name.endswith('.h'):
self._members += converted.members()
self._members.append(converted)
elif self.get_cls(mem) == DoxyNamespace:
self._members += converted.members()
self._members.append(converted)
else:
self._members.append(converted)
def generate_swig_doc_i(self):
"""
%feature("docstring") gr_make_align_on_samplenumbers_ss::align_state "
Wraps the C++: gr_align_on_samplenumbers_ss::align_state";
"""
pass
class DoxyCompMem(Base):
@@ -78,14 +62,30 @@ class DoxyCompMem(Base):
bd = description(getattr(parse_data, 'briefdescription', None))
dd = description(getattr(parse_data, 'detaileddescription', None))
self._data['brief_description'] = bd
self._data['detailed_description'] = dd
self._data['detailed_description'] = dd
def set_parameters(self, data):
vs = [ddc.value for ddc in data.detaileddescription.content_]
pls = []
for v in vs:
if hasattr(v, 'parameterlist'):
pls += v.parameterlist
pis = []
for pl in pls:
pis += pl.parameteritem
dpis = []
for pi in pis:
dpi = DoxyParameterItem(pi)
dpi._parse()
dpis.append(dpi)
self._data['params'] = dpis
class DoxyCompound(DoxyCompMem):
pass
class DoxyMember(DoxyCompMem):
pass
class DoxyFunction(DoxyMember):
@@ -98,10 +98,13 @@ class DoxyFunction(DoxyMember):
return
super(DoxyFunction, self)._parse()
self.set_descriptions(self._parse_data)
self._data['params'] = []
prms = self._parse_data.param
for prm in prms:
self._data['params'].append(DoxyParam(prm))
self.set_parameters(self._parse_data)
if not self._data['params']:
# If the params weren't set by a comment then just grab the names.
self._data['params'] = []
prms = self._parse_data.param
for prm in prms:
self._data['params'].append(DoxyParam(prm))
brief_description = property(lambda self: self.data()['brief_description'])
detailed_description = property(lambda self: self.data()['detailed_description'])
@@ -111,7 +114,7 @@ Base.mem_classes.append(DoxyFunction)
class DoxyParam(DoxyMember):
__module__ = "gnuradio.utils.doxyxml"
def _parse(self):
@@ -121,16 +124,46 @@ class DoxyParam(DoxyMember):
self.set_descriptions(self._parse_data)
self._data['declname'] = self._parse_data.declname
@property
def description(self):
descriptions = []
if self.brief_description:
descriptions.append(self.brief_description)
if self.detailed_description:
descriptions.append(self.detailed_description)
return '\n\n'.join(descriptions)
brief_description = property(lambda self: self.data()['brief_description'])
detailed_description = property(lambda self: self.data()['detailed_description'])
declname = property(lambda self: self.data()['declname'])
name = property(lambda self: self.data()['declname'])
class DoxyParameterItem(DoxyMember):
"""A different representation of a parameter in Doxygen."""
def _parse(self):
if self._parsed:
return
super(DoxyParameterItem, self)._parse()
names = []
for nl in self._parse_data.parameternamelist:
for pn in nl.parametername:
names.append(description(pn))
# Just take first name
self._data['name'] = names[0]
# Get description
pd = description(self._parse_data.get_parameterdescription())
self._data['description'] = pd
description = property(lambda self: self.data()['description'])
name = property(lambda self: self.data()['name'])
class DoxyClass(DoxyCompound):
__module__ = "gnuradio.utils.doxyxml"
kind = 'class'
def _parse(self):
if self._parsed:
return
@@ -139,22 +172,24 @@ class DoxyClass(DoxyCompound):
if self._error:
return
self.set_descriptions(self._retrieved_data.compounddef)
self.set_parameters(self._retrieved_data.compounddef)
# Sectiondef.kind tells about whether private or public.
# We just ignore this for now.
self.process_memberdefs()
brief_description = property(lambda self: self.data()['brief_description'])
detailed_description = property(lambda self: self.data()['detailed_description'])
params = property(lambda self: self.data()['params'])
Base.mem_classes.append(DoxyClass)
class DoxyFile(DoxyCompound):
__module__ = "gnuradio.utils.doxyxml"
kind = 'file'
def _parse(self):
if self._parsed:
return
@@ -164,7 +199,7 @@ class DoxyFile(DoxyCompound):
if self._error:
return
self.process_memberdefs()
brief_description = property(lambda self: self.data()['brief_description'])
detailed_description = property(lambda self: self.data()['detailed_description'])
@@ -172,16 +207,26 @@ Base.mem_classes.append(DoxyFile)
class DoxyNamespace(DoxyCompound):
__module__ = "gnuradio.utils.doxyxml"
kind = 'namespace'
def _parse(self):
if self._parsed:
return
super(DoxyNamespace, self)._parse()
self.retrieve_data()
self.set_descriptions(self._retrieved_data.compounddef)
if self._error:
return
self.process_memberdefs()
Base.mem_classes.append(DoxyNamespace)
class DoxyGroup(DoxyCompound):
__module__ = "gnuradio.utils.doxyxml"
kind = 'group'
@@ -209,7 +254,7 @@ class DoxyGroup(DoxyCompound):
self.process_memberdefs()
title = property(lambda self: self.data()['title'])
Base.mem_classes.append(DoxyGroup)
@@ -224,14 +269,14 @@ Base.mem_classes.append(DoxyFriend)
class DoxyOther(Base):
__module__ = "gnuradio.utils.doxyxml"
kinds = set(['variable', 'struct', 'union', 'define', 'typedef', 'enum', 'dir', 'page'])
kinds = set(['variable', 'struct', 'union', 'define', 'typedef', 'enum',
'dir', 'page', 'signal', 'slot', 'property'])
@classmethod
def can_parse(cls, obj):
return obj.kind in cls.kinds
Base.mem_classes.append(DoxyOther)
Base.mem_classes.append(DoxyOther)

File diff suppressed because it is too large Load Diff

View File

@@ -1,50 +0,0 @@
/* -*- c++ -*- */
/*
* Copyright 2010 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* GNU Radio 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.
*
* GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include <iostream>
#include "aadvark.h"
void Aadvark::print() {
std::cout << "aadvark is " << aadvarkness << "/10 aadvarky" << std::endl;
}
Aadvark::Aadvark(int aaness): aadvarkness(aaness) {}
bool aadvarky_enough(Aadvark aad) {
if (aad.get_aadvarkness() > 6)
return true;
else
return false;
}
int Aadvark::get_aadvarkness() {
return aadvarkness;
}
int main() {
Aadvark arold = Aadvark(6);
arold.print();
if (aadvarky_enough(arold))
std::cout << "He is aadvarky enough" << std::endl;
else
std::cout << "He is not aadvarky enough" << std::endl;
}

View File

@@ -1,44 +0,0 @@
/* -*- c++ -*- */
/*
* Copyright 2010 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* GNU Radio 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.
*
* GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include <iostream>
/*!
* \brief Models the mammal Aadvark.
*
* Sadly the model is incomplete and cannot capture all aspects of an aadvark yet.
*
* This line is uninformative and is only to test line breaks in the comments.
*/
class Aadvark {
public:
//! \brief Outputs the vital aadvark statistics.
void print();
//! \param aaness The aadvarkness of an aadvark is a measure of how aadvarky it is.
Aadvark(int aaness);
int get_aadvarkness();
private:
int aadvarkness;
};
bool aadvarky_enough(Aadvark aad);
int main();

View File

@@ -1,88 +0,0 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<doxygen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="compound.xsd" version="1.6.3">
<compounddef id="aadvark_8cc" kind="file">
<compoundname>aadvark.cc</compoundname>
<includes local="no">iostream</includes>
<includes refid="aadvark_8cc" local="yes">aadvark.h</includes>
<includedby refid="aadvark_8cc" local="yes">aadvark.cc</includedby>
<incdepgraph>
<node id="0">
<label>aadvark.cc</label>
<link refid="aadvark.cc"/>
<childnode refid="1" relation="include">
</childnode>
</node>
<node id="1">
<label>iostream</label>
</node>
</incdepgraph>
<sectiondef kind="func">
<memberdef kind="function" id="aadvark_8cc_1acb52858524210ec6dddc3e16d1e52946" prot="public" static="no" const="no" explicit="no" inline="no" virt="non-virtual">
<type>bool</type>
<definition>bool aadvarky_enough</definition>
<argsstring>(Aadvark aad)</argsstring>
<name>aadvarky_enough</name>
<param>
<type><ref refid="classAadvark" kindref="compound">Aadvark</ref></type>
<declname>aad</declname>
</param>
<briefdescription>
</briefdescription>
<detaileddescription>
</detaileddescription>
<inbodydescription>
</inbodydescription>
<location file="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.cc" line="10" bodyfile="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.cc" bodystart="10" bodyend="15"/>
</memberdef>
<memberdef kind="function" id="aadvark_8cc_1ae66f6b31b5ad750f1fe042a706a4e3d4" prot="public" static="no" const="no" explicit="no" inline="no" virt="non-virtual">
<type>int</type>
<definition>int main</definition>
<argsstring>()</argsstring>
<name>main</name>
<briefdescription>
</briefdescription>
<detaileddescription>
</detaileddescription>
<inbodydescription>
</inbodydescription>
<location file="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.cc" line="21" bodyfile="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.cc" bodystart="21" bodyend="28"/>
</memberdef>
</sectiondef>
<briefdescription>
</briefdescription>
<detaileddescription>
</detaileddescription>
<programlisting>
<codeline lineno="1"><highlight class="preprocessor">#include<sp/>&lt;iostream&gt;</highlight><highlight class="normal"></highlight></codeline>
<codeline lineno="2"><highlight class="normal"></highlight><highlight class="preprocessor">#include<sp/>&quot;aadvark.h&quot;</highlight><highlight class="normal"></highlight></codeline>
<codeline lineno="3"><highlight class="normal"></highlight></codeline>
<codeline lineno="4"><highlight class="normal"></highlight><highlight class="keywordtype">void</highlight><highlight class="normal"><sp/><ref refid="classAadvark_1abd061aa5f998002e72080a34f512a059" kindref="member" tooltip="Outputs the vital aadvark statistics.">Aadvark::print</ref>()<sp/>{</highlight></codeline>
<codeline lineno="5"><highlight class="normal"><sp/><sp/>std::cout<sp/>&lt;&lt;<sp/></highlight><highlight class="stringliteral">&quot;aadvark<sp/>is<sp/>&quot;</highlight><highlight class="normal"><sp/>&lt;&lt;<sp/>aadvarkness<sp/>&lt;&lt;<sp/></highlight><highlight class="stringliteral">&quot;/10<sp/>aadvarky&quot;</highlight><highlight class="normal"><sp/>&lt;&lt;<sp/>std::endl;</highlight></codeline>
<codeline lineno="6"><highlight class="normal">}</highlight></codeline>
<codeline lineno="7"><highlight class="normal"></highlight></codeline>
<codeline lineno="8"><highlight class="normal"><ref refid="classAadvark_1adf1a4b97a641411a74a04ab312484462" kindref="member">Aadvark::Aadvark</ref>(</highlight><highlight class="keywordtype">int</highlight><highlight class="normal"><sp/>aaness):<sp/>aadvarkness(aaness)<sp/>{}</highlight></codeline>
<codeline lineno="9"><highlight class="normal"></highlight></codeline>
<codeline lineno="10"><highlight class="normal"></highlight><highlight class="keywordtype">bool</highlight><highlight class="normal"><sp/>aadvarky_enough(<ref refid="classAadvark" kindref="compound" tooltip="Models the mammal Aadvark.">Aadvark</ref><sp/>aad)<sp/>{</highlight></codeline>
<codeline lineno="11"><highlight class="normal"><sp/><sp/></highlight><highlight class="keywordflow">if</highlight><highlight class="normal"><sp/>(aad.get_aadvarkness()<sp/>&gt;<sp/>6)</highlight></codeline>
<codeline lineno="12"><highlight class="normal"><sp/><sp/><sp/><sp/></highlight><highlight class="keywordflow">return</highlight><highlight class="normal"><sp/></highlight><highlight class="keyword">true</highlight><highlight class="normal">;</highlight></codeline>
<codeline lineno="13"><highlight class="normal"><sp/><sp/></highlight><highlight class="keywordflow">else</highlight><highlight class="normal"></highlight></codeline>
<codeline lineno="14"><highlight class="normal"><sp/><sp/><sp/><sp/></highlight><highlight class="keywordflow">return</highlight><highlight class="normal"><sp/></highlight><highlight class="keyword">false</highlight><highlight class="normal">;</highlight></codeline>
<codeline lineno="15"><highlight class="normal">}</highlight></codeline>
<codeline lineno="16"><highlight class="normal"></highlight></codeline>
<codeline lineno="17"><highlight class="normal"></highlight><highlight class="keywordtype">int</highlight><highlight class="normal"><sp/>Aadvark::get_aadvarkness()<sp/>{</highlight></codeline>
<codeline lineno="18"><highlight class="normal"><sp/><sp/></highlight><highlight class="keywordflow">return</highlight><highlight class="normal"><sp/>aadvarkness;</highlight></codeline>
<codeline lineno="19"><highlight class="normal">}</highlight></codeline>
<codeline lineno="20"><highlight class="normal"></highlight></codeline>
<codeline lineno="21"><highlight class="normal"></highlight><highlight class="keywordtype">int</highlight><highlight class="normal"><sp/>main()<sp/>{</highlight></codeline>
<codeline lineno="22"><highlight class="normal"><sp/><sp/><ref refid="classAadvark" kindref="compound" tooltip="Models the mammal Aadvark.">Aadvark</ref><sp/>arold<sp/>=<sp/><ref refid="classAadvark" kindref="compound" tooltip="Models the mammal Aadvark.">Aadvark</ref>(6);</highlight></codeline>
<codeline lineno="23"><highlight class="normal"><sp/><sp/>arold.<ref refid="classAadvark_1abd061aa5f998002e72080a34f512a059" kindref="member" tooltip="Outputs the vital aadvark statistics.">print</ref>();</highlight></codeline>
<codeline lineno="24"><highlight class="normal"><sp/><sp/></highlight><highlight class="keywordflow">if</highlight><highlight class="normal"><sp/>(aadvarky_enough(arold))</highlight></codeline>
<codeline lineno="25"><highlight class="normal"><sp/><sp/><sp/><sp/>std::cout<sp/>&lt;&lt;<sp/></highlight><highlight class="stringliteral">&quot;He<sp/>is<sp/>aadvarky<sp/>enough&quot;</highlight><highlight class="normal"><sp/>&lt;&lt;<sp/>std::endl;</highlight></codeline>
<codeline lineno="26"><highlight class="normal"><sp/><sp/></highlight><highlight class="keywordflow">else</highlight><highlight class="normal"></highlight></codeline>
<codeline lineno="27"><highlight class="normal"><sp/><sp/><sp/><sp/>std::cout<sp/>&lt;&lt;<sp/></highlight><highlight class="stringliteral">&quot;He<sp/>is<sp/>not<sp/>aadvarky<sp/>enough&quot;</highlight><highlight class="normal"><sp/>&lt;&lt;<sp/>std::endl;</highlight></codeline>
<codeline lineno="28"><highlight class="normal">}</highlight></codeline>
<codeline lineno="29"><highlight class="normal"></highlight></codeline>
</programlisting>
<location file="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.cc"/>
</compounddef>
</doxygen>

View File

@@ -1,72 +0,0 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<doxygen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="compound.xsd" version="1.6.3">
<compounddef id="aadvark_8h" kind="file">
<compoundname>aadvark.h</compoundname>
<includes local="no">iostream</includes>
<incdepgraph>
<node id="3">
<label>aadvark.h</label>
<link refid="aadvark.h"/>
<childnode refid="4" relation="include">
</childnode>
</node>
<node id="4">
<label>iostream</label>
</node>
</incdepgraph>
<innerclass refid="classAadvark" prot="public">Aadvark</innerclass>
<sectiondef kind="func">
<memberdef kind="function" id="aadvark_8h_1acb52858524210ec6dddc3e16d1e52946" prot="public" static="no" const="no" explicit="no" inline="no" virt="non-virtual">
<type>bool</type>
<definition>bool aadvarky_enough</definition>
<argsstring>(Aadvark aad)</argsstring>
<name>aadvarky_enough</name>
<param>
<type><ref refid="classAadvark" kindref="compound">Aadvark</ref></type>
<declname>aad</declname>
</param>
<briefdescription>
</briefdescription>
<detaileddescription>
</detaileddescription>
<inbodydescription>
</inbodydescription>
<location file="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.h" line="21" bodyfile="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.cc" bodystart="10" bodyend="15"/>
</memberdef>
<memberdef kind="function" id="aadvark_8h_1ae66f6b31b5ad750f1fe042a706a4e3d4" prot="public" static="no" const="no" explicit="no" inline="no" virt="non-virtual">
<type>int</type>
<definition>int main</definition>
<argsstring>()</argsstring>
<name>main</name>
<briefdescription>
</briefdescription>
<detaileddescription>
</detaileddescription>
<inbodydescription>
</inbodydescription>
<location file="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.h" line="23" bodyfile="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.cc" bodystart="21" bodyend="28"/>
</memberdef>
</sectiondef>
<briefdescription>
</briefdescription>
<detaileddescription>
</detaileddescription>
<programlisting>
<codeline lineno="1"><highlight class="preprocessor">#include<sp/>&lt;iostream&gt;</highlight><highlight class="normal"></highlight></codeline>
<codeline lineno="2"><highlight class="normal"></highlight></codeline>
<codeline lineno="10" refid="classAadvark" refkind="compound"><highlight class="keyword">class<sp/></highlight><highlight class="normal"><ref refid="classAadvark" kindref="compound" tooltip="Models the mammal Aadvark.">Aadvark</ref><sp/>{</highlight></codeline>
<codeline lineno="11"><highlight class="normal"></highlight><highlight class="keyword">public</highlight><highlight class="normal">:</highlight></codeline>
<codeline lineno="13"><highlight class="normal"><sp/><sp/></highlight><highlight class="keywordtype">void</highlight><highlight class="normal"><sp/><ref refid="classAadvark_1abd061aa5f998002e72080a34f512a059" kindref="member" tooltip="Outputs the vital aadvark statistics.">print</ref>();</highlight></codeline>
<codeline lineno="15"><highlight class="normal"><sp/><sp/><ref refid="classAadvark_1adf1a4b97a641411a74a04ab312484462" kindref="member">Aadvark</ref>(</highlight><highlight class="keywordtype">int</highlight><highlight class="normal"><sp/>aaness);</highlight></codeline>
<codeline lineno="16"><highlight class="normal"><sp/><sp/></highlight><highlight class="keywordtype">int</highlight><highlight class="normal"><sp/>get_aadvarkness();</highlight></codeline>
<codeline lineno="17"><highlight class="normal"></highlight><highlight class="keyword">private</highlight><highlight class="normal">:</highlight></codeline>
<codeline lineno="18"><highlight class="normal"><sp/><sp/></highlight><highlight class="keywordtype">int</highlight><highlight class="normal"><sp/>aadvarkness;</highlight></codeline>
<codeline lineno="19"><highlight class="normal">};</highlight></codeline>
<codeline lineno="20"><highlight class="normal"></highlight></codeline>
<codeline lineno="21"><highlight class="normal"></highlight><highlight class="keywordtype">bool</highlight><highlight class="normal"><sp/>aadvarky_enough(<ref refid="classAadvark" kindref="compound" tooltip="Models the mammal Aadvark.">Aadvark</ref><sp/>aad);</highlight></codeline>
<codeline lineno="22"><highlight class="normal"></highlight></codeline>
<codeline lineno="23"><highlight class="normal"></highlight><highlight class="keywordtype">int</highlight><highlight class="normal"><sp/>main();</highlight></codeline>
</programlisting>
<location file="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.h"/>
</compounddef>
</doxygen>

View File

@@ -1,86 +0,0 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<doxygen xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="compound.xsd" version="1.6.3">
<compounddef id="classAadvark" kind="class" prot="public">
<compoundname>Aadvark</compoundname>
<includes refid="aadvark_8h" local="no">aadvark.h</includes>
<sectiondef kind="private-attrib">
<memberdef kind="variable" id="classAadvark_1ab79eb58d7bb9d5ddfa5d6f783836cab9" prot="private" static="no" mutable="no">
<type>int</type>
<definition>int Aadvark::aadvarkness</definition>
<argsstring></argsstring>
<name>aadvarkness</name>
<briefdescription>
</briefdescription>
<detaileddescription>
</detaileddescription>
<inbodydescription>
</inbodydescription>
<location file="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.h" line="18" bodyfile="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.h" bodystart="18" bodyend="-1"/>
</memberdef>
</sectiondef>
<sectiondef kind="public-func">
<memberdef kind="function" id="classAadvark_1abd061aa5f998002e72080a34f512a059" prot="public" static="no" const="no" explicit="no" inline="no" virt="non-virtual">
<type>void</type>
<definition>void Aadvark::print</definition>
<argsstring>()</argsstring>
<name>print</name>
<briefdescription>
<para>Outputs the vital aadvark statistics. </para> </briefdescription>
<detaileddescription>
</detaileddescription>
<inbodydescription>
</inbodydescription>
<location file="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.h" line="13" bodyfile="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.cc" bodystart="4" bodyend="6"/>
</memberdef>
<memberdef kind="function" id="classAadvark_1adf1a4b97a641411a74a04ab312484462" prot="public" static="no" const="no" explicit="no" inline="no" virt="non-virtual">
<type></type>
<definition>Aadvark::Aadvark</definition>
<argsstring>(int aaness)</argsstring>
<name>Aadvark</name>
<param>
<type>int</type>
<declname>aaness</declname>
</param>
<briefdescription>
</briefdescription>
<detaileddescription>
<para><parameterlist kind="param"><parameteritem>
<parameternamelist>
<parametername>aaness</parametername>
</parameternamelist>
<parameterdescription>
<para>The aadvarkness of an aadvark is a measure of how aadvarky it is. </para></parameterdescription>
</parameteritem>
</parameterlist>
</para> </detaileddescription>
<inbodydescription>
</inbodydescription>
<location file="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.h" line="15" bodyfile="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.cc" bodystart="8" bodyend="8"/>
</memberdef>
<memberdef kind="function" id="classAadvark_1affd2ada0a85807efcbe26615a848f53e" prot="public" static="no" const="no" explicit="no" inline="no" virt="non-virtual">
<type>int</type>
<definition>int Aadvark::get_aadvarkness</definition>
<argsstring>()</argsstring>
<name>get_aadvarkness</name>
<briefdescription>
</briefdescription>
<detaileddescription>
</detaileddescription>
<inbodydescription>
</inbodydescription>
<location file="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.h" line="16" bodyfile="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.cc" bodystart="17" bodyend="19"/>
</memberdef>
</sectiondef>
<briefdescription>
<para>Models the mammal <ref refid="classAadvark" kindref="compound">Aadvark</ref>. </para> </briefdescription>
<detaileddescription>
<para>Sadly the model is incomplete and cannot capture all aspects of an aadvark yet.</para><para>This line is uninformative and is only to test line breaks in the comments. </para> </detaileddescription>
<location file="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.h" line="10" bodyfile="/home/ben/gnuradio/gnuradio-core/src/python/gnuradio/utils/doxyxml/example/aadvark.h" bodystart="10" bodyend="19"/>
<listofallmembers>
<member refid="classAadvark_1adf1a4b97a641411a74a04ab312484462" prot="public" virt="non-virtual"><scope>Aadvark</scope><name>Aadvark</name></member>
<member refid="classAadvark_1ab79eb58d7bb9d5ddfa5d6f783836cab9" prot="private" virt="non-virtual"><scope>Aadvark</scope><name>aadvarkness</name></member>
<member refid="classAadvark_1affd2ada0a85807efcbe26615a848f53e" prot="public" virt="non-virtual"><scope>Aadvark</scope><name>get_aadvarkness</name></member>
<member refid="classAadvark_1abd061aa5f998002e72080a34f512a059" prot="public" virt="non-virtual"><scope>Aadvark</scope><name>print</name></member>
</listofallmembers>
</compounddef>
</doxygen>

View File

@@ -1,15 +0,0 @@
<!-- XSLT script to combine the generated output into a single file.
If you have xsltproc you could use:
xsltproc combine.xslt index.xml >all.xml
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" version="1.0" indent="yes" standalone="yes" />
<xsl:template match="/">
<doxygen version="{doxygenindex/@version}">
<!-- Load all doxgen generated xml files -->
<xsl:for-each select="doxygenindex/compound">
<xsl:copy-of select="document( concat( @refid, '.xml' ) )/doxygen/*" />
</xsl:for-each>
</doxygen>
</xsl:template>
</xsl:stylesheet>

View File

@@ -1,814 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="doxygen" type="DoxygenType"/>
<!-- Complex types -->
<xsd:complexType name="DoxygenType">
<xsd:sequence maxOccurs="unbounded">
<xsd:element name="compounddef" type="compounddefType" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="version" type="DoxVersionNumber" use="required" />
</xsd:complexType>
<xsd:complexType name="compounddefType">
<xsd:sequence>
<xsd:element name="compoundname" type="xsd:string"/>
<xsd:element name="title" type="xsd:string" minOccurs="0" />
<xsd:element name="basecompoundref" type="compoundRefType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="derivedcompoundref" type="compoundRefType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="includes" type="incType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="includedby" type="incType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="incdepgraph" type="graphType" minOccurs="0" />
<xsd:element name="invincdepgraph" type="graphType" minOccurs="0" />
<xsd:element name="innerdir" type="refType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="innerfile" type="refType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="innerclass" type="refType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="innernamespace" type="refType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="innerpage" type="refType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="innergroup" type="refType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="templateparamlist" type="templateparamlistType" minOccurs="0" />
<xsd:element name="sectiondef" type="sectiondefType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="briefdescription" type="descriptionType" minOccurs="0" />
<xsd:element name="detaileddescription" type="descriptionType" minOccurs="0" />
<xsd:element name="inheritancegraph" type="graphType" minOccurs="0" />
<xsd:element name="collaborationgraph" type="graphType" minOccurs="0" />
<xsd:element name="programlisting" type="listingType" minOccurs="0" />
<xsd:element name="location" type="locationType" minOccurs="0" />
<xsd:element name="listofallmembers" type="listofallmembersType" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="kind" type="DoxCompoundKind" />
<xsd:attribute name="prot" type="DoxProtectionKind" />
</xsd:complexType>
<xsd:complexType name="listofallmembersType">
<xsd:sequence>
<xsd:element name="member" type="memberRefType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="memberRefType">
<xsd:sequence>
<xsd:element name="scope" />
<xsd:element name="name" />
</xsd:sequence>
<xsd:attribute name="refid" type="xsd:string" />
<xsd:attribute name="prot" type="DoxProtectionKind" />
<xsd:attribute name="virt" type="DoxVirtualKind" />
<xsd:attribute name="ambiguityscope" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="compoundRefType" mixed="true">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="refid" type="xsd:string" use="optional" />
<xsd:attribute name="prot" type="DoxProtectionKind" />
<xsd:attribute name="virt" type="DoxVirtualKind" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="reimplementType" mixed="true">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="refid" type="xsd:string" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="incType" mixed="true">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="refid" type="xsd:string" />
<xsd:attribute name="local" type="DoxBool" />
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="refType" mixed="true">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="refid" type="xsd:string" />
<xsd:attribute name="prot" type="DoxProtectionKind" use="optional"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="refTextType" mixed="true">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="refid" type="xsd:string" />
<xsd:attribute name="kindref" type="DoxRefKind" />
<xsd:attribute name="external" type="xsd:string" use="optional"/>
<xsd:attribute name="tooltip" type="xsd:string" use="optional"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="sectiondefType">
<xsd:sequence>
<xsd:element name="header" type="xsd:string" minOccurs="0" />
<xsd:element name="description" type="descriptionType" minOccurs="0" />
<xsd:element name="memberdef" type="memberdefType" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="kind" type="DoxSectionKind" />
</xsd:complexType>
<xsd:complexType name="memberdefType">
<xsd:sequence>
<xsd:element name="templateparamlist" type="templateparamlistType" minOccurs="0" />
<xsd:element name="type" type="linkedTextType" minOccurs="0" />
<xsd:element name="definition" minOccurs="0" />
<xsd:element name="argsstring" minOccurs="0" />
<xsd:element name="name" />
<xsd:element name="read" minOccurs="0" />
<xsd:element name="write" minOccurs="0" />
<xsd:element name="bitfield" minOccurs="0" />
<xsd:element name="reimplements" type="reimplementType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="reimplementedby" type="reimplementType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="param" type="paramType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="enumvalue" type="enumvalueType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="initializer" type="linkedTextType" minOccurs="0" />
<xsd:element name="exceptions" type="linkedTextType" minOccurs="0" />
<xsd:element name="briefdescription" type="descriptionType" minOccurs="0" />
<xsd:element name="detaileddescription" type="descriptionType" minOccurs="0" />
<xsd:element name="inbodydescription" type="descriptionType" minOccurs="0" />
<xsd:element name="location" type="locationType" />
<xsd:element name="references" type="referenceType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="referencedby" type="referenceType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="kind" type="DoxMemberKind" />
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="prot" type="DoxProtectionKind" />
<xsd:attribute name="static" type="DoxBool" />
<xsd:attribute name="const" type="DoxBool" />
<xsd:attribute name="explicit" type="DoxBool" />
<xsd:attribute name="inline" type="DoxBool" />
<xsd:attribute name="virt" type="DoxVirtualKind" />
<xsd:attribute name="volatile" type="DoxBool" />
<xsd:attribute name="mutable" type="DoxBool" />
<!-- Qt property -->
<xsd:attribute name="readable" type="DoxBool" use="optional"/>
<xsd:attribute name="writable" type="DoxBool" use="optional"/>
<!-- C++/CLI variable -->
<xsd:attribute name="initonly" type="DoxBool" use="optional"/>
<!-- C++/CLI and C# property -->
<xsd:attribute name="settable" type="DoxBool" use="optional"/>
<xsd:attribute name="gettable" type="DoxBool" use="optional"/>
<!-- C++/CLI function -->
<xsd:attribute name="final" type="DoxBool" use="optional"/>
<xsd:attribute name="sealed" type="DoxBool" use="optional"/>
<xsd:attribute name="new" type="DoxBool" use="optional"/>
<!-- C++/CLI event -->
<xsd:attribute name="add" type="DoxBool" use="optional"/>
<xsd:attribute name="remove" type="DoxBool" use="optional"/>
<xsd:attribute name="raise" type="DoxBool" use="optional"/>
<!-- Objective-C 2.0 protocol method -->
<xsd:attribute name="optional" type="DoxBool" use="optional"/>
<xsd:attribute name="required" type="DoxBool" use="optional"/>
<!-- Objective-C 2.0 property accessor -->
<xsd:attribute name="accessor" type="DoxAccessor" use="optional"/>
</xsd:complexType>
<xsd:complexType name="descriptionType" mixed="true">
<xsd:sequence>
<xsd:element name="title" type="xsd:string" minOccurs="0"/>
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="sect1" type="docSect1Type" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="internal" type="docInternalType" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="enumvalueType" mixed="true">
<xsd:sequence>
<xsd:element name="name" />
<xsd:element name="initializer" type="linkedTextType" minOccurs="0" />
<xsd:element name="briefdescription" type="descriptionType" minOccurs="0" />
<xsd:element name="detaileddescription" type="descriptionType" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" />
<xsd:attribute name="prot" type="DoxProtectionKind" />
</xsd:complexType>
<xsd:complexType name="templateparamlistType">
<xsd:sequence>
<xsd:element name="param" type="paramType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="paramType">
<xsd:sequence>
<xsd:element name="type" type="linkedTextType" minOccurs="0" />
<xsd:element name="declname" minOccurs="0" />
<xsd:element name="defname" minOccurs="0" />
<xsd:element name="array" minOccurs="0" />
<xsd:element name="defval" type="linkedTextType" minOccurs="0" />
<xsd:element name="briefdescription" type="descriptionType" minOccurs="0" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="linkedTextType" mixed="true">
<xsd:sequence>
<xsd:element name="ref" type="refTextType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="graphType">
<xsd:sequence>
<xsd:element name="node" type="nodeType" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="nodeType">
<xsd:sequence>
<xsd:element name="label" />
<xsd:element name="link" type="linkType" minOccurs="0" />
<xsd:element name="childnode" type="childnodeType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="childnodeType">
<xsd:sequence>
<xsd:element name="edgelabel" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="refid" type="xsd:string" />
<xsd:attribute name="relation" type="DoxGraphRelation" />
</xsd:complexType>
<xsd:complexType name="linkType">
<xsd:attribute name="refid" type="xsd:string" />
<xsd:attribute name="external" type="xsd:string" use="optional"/>
</xsd:complexType>
<xsd:complexType name="listingType">
<xsd:sequence>
<xsd:element name="codeline" type="codelineType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="codelineType">
<xsd:sequence>
<xsd:element name="highlight" type="highlightType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="lineno" type="xsd:integer" />
<xsd:attribute name="refid" type="xsd:string" />
<xsd:attribute name="refkind" type="DoxRefKind" />
<xsd:attribute name="external" type="DoxBool" />
</xsd:complexType>
<xsd:complexType name="highlightType" mixed="true">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="sp" />
<xsd:element name="ref" type="refTextType" />
</xsd:choice>
<xsd:attribute name="class" type="DoxHighlightClass" />
</xsd:complexType>
<xsd:complexType name="referenceType" mixed="true">
<xsd:attribute name="refid" type="xsd:string" />
<xsd:attribute name="compoundref" type="xsd:string" use="optional" />
<xsd:attribute name="startline" type="xsd:integer" />
<xsd:attribute name="endline" type="xsd:integer" />
</xsd:complexType>
<xsd:complexType name="locationType">
<xsd:attribute name="file" type="xsd:string" />
<xsd:attribute name="line" type="xsd:integer" />
<xsd:attribute name="bodyfile" type="xsd:string" />
<xsd:attribute name="bodystart" type="xsd:integer" />
<xsd:attribute name="bodyend" type="xsd:integer" />
</xsd:complexType>
<xsd:complexType name="docSect1Type" mixed="true">
<xsd:sequence>
<xsd:element name="title" type="xsd:string" />
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="sect2" type="docSect2Type" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="internal" type="docInternalS1Type" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docSect2Type" mixed="true">
<xsd:sequence>
<xsd:element name="title" type="xsd:string" />
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="sect3" type="docSect3Type" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="internal" type="docInternalS2Type" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docSect3Type" mixed="true">
<xsd:sequence>
<xsd:element name="title" type="xsd:string" />
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="sect4" type="docSect4Type" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="internal" type="docInternalS3Type" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docSect4Type" mixed="true">
<xsd:sequence>
<xsd:element name="title" type="xsd:string" />
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="internal" type="docInternalS4Type" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docInternalType" mixed="true">
<xsd:sequence>
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="sect1" type="docSect1Type" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="docInternalS1Type" mixed="true">
<xsd:sequence>
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="sect2" type="docSect2Type" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="docInternalS2Type" mixed="true">
<xsd:sequence>
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="sect3" type="docSect3Type" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="docInternalS3Type" mixed="true">
<xsd:sequence>
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="sect3" type="docSect4Type" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="docInternalS4Type" mixed="true">
<xsd:sequence>
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:group name="docTitleCmdGroup">
<xsd:choice>
<xsd:element name="ulink" type="docURLLink" />
<xsd:element name="bold" type="docMarkupType" />
<xsd:element name="emphasis" type="docMarkupType" />
<xsd:element name="computeroutput" type="docMarkupType" />
<xsd:element name="subscript" type="docMarkupType" />
<xsd:element name="superscript" type="docMarkupType" />
<xsd:element name="center" type="docMarkupType" />
<xsd:element name="small" type="docMarkupType" />
<xsd:element name="htmlonly" type="xsd:string" />
<xsd:element name="latexonly" type="xsd:string" />
<xsd:element name="dot" type="xsd:string" />
<xsd:element name="anchor" type="docAnchorType" />
<xsd:element name="formula" type="docFormulaType" />
<xsd:element name="ref" type="docRefTextType" />
<xsd:element name="copy" type="docEmptyType" />
<xsd:element name="trademark" type="docEmptyType" />
<xsd:element name="registered" type="docEmptyType" />
<xsd:element name="lsquo" type="docEmptyType" />
<xsd:element name="rsquo" type="docEmptyType" />
<xsd:element name="ldquo" type="docEmptyType" />
<xsd:element name="rdquo" type="docEmptyType" />
<xsd:element name="ndash" type="docEmptyType" />
<xsd:element name="mdash" type="docEmptyType" />
<xsd:element name="umlaut" type="docCharType" />
<xsd:element name="acute" type="docCharType" />
<xsd:element name="grave" type="docCharType" />
<xsd:element name="circ" type="docCharType" />
<xsd:element name="slash" type="docCharType" />
<xsd:element name="tilde" type="docCharType" />
<xsd:element name="cedil" type="docCharType" />
<xsd:element name="ring" type="docCharType" />
<xsd:element name="szlig" type="docEmptyType" />
<xsd:element name="nonbreakablespace" type="docEmptyType" />
</xsd:choice>
</xsd:group>
<xsd:complexType name="docTitleType" mixed="true">
<xsd:group ref="docTitleCmdGroup" minOccurs="0" maxOccurs="unbounded" />
</xsd:complexType>
<xsd:group name="docCmdGroup">
<xsd:choice>
<xsd:group ref="docTitleCmdGroup"/>
<xsd:element name="linebreak" type="docEmptyType" />
<xsd:element name="hruler" type="docEmptyType" />
<xsd:element name="preformatted" type="docMarkupType" />
<xsd:element name="programlisting" type="listingType" />
<xsd:element name="verbatim" type="xsd:string" />
<xsd:element name="indexentry" type="docIndexEntryType" />
<xsd:element name="orderedlist" type="docListType" />
<xsd:element name="itemizedlist" type="docListType" />
<xsd:element name="simplesect" type="docSimpleSectType" />
<xsd:element name="title" type="docTitleType" />
<xsd:element name="variablelist" type="docVariableListType" />
<xsd:element name="table" type="docTableType" />
<xsd:element name="heading" type="docHeadingType" />
<xsd:element name="image" type="docImageType" />
<xsd:element name="dotfile" type="docDotFileType" />
<xsd:element name="toclist" type="docTocListType" />
<xsd:element name="language" type="docLanguageType" />
<xsd:element name="parameterlist" type="docParamListType" />
<xsd:element name="xrefsect" type="docXRefSectType" />
<xsd:element name="copydoc" type="docCopyType" />
</xsd:choice>
</xsd:group>
<xsd:complexType name="docParaType" mixed="true">
<xsd:group ref="docCmdGroup" minOccurs="0" maxOccurs="unbounded" />
</xsd:complexType>
<xsd:complexType name="docMarkupType" mixed="true">
<xsd:group ref="docCmdGroup" minOccurs="0" maxOccurs="unbounded" />
</xsd:complexType>
<xsd:complexType name="docURLLink" mixed="true">
<xsd:group ref="docTitleCmdGroup" minOccurs="0" maxOccurs="unbounded" />
<xsd:attribute name="url" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docAnchorType" mixed="true">
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docFormulaType" mixed="true">
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docIndexEntryType">
<xsd:sequence>
<xsd:element name="primaryie" type="xsd:string" />
<xsd:element name="secondaryie" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="docListType">
<xsd:sequence>
<xsd:element name="listitem" type="docListItemType" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="docListItemType">
<xsd:sequence>
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="docSimpleSectType">
<xsd:sequence>
<xsd:element name="title" type="docTitleType" minOccurs="0" />
<xsd:sequence minOccurs="0" maxOccurs="unbounded">
<xsd:element name="para" type="docParaType" minOccurs="1" maxOccurs="unbounded" />
<xsd:element name="simplesectsep" type="docEmptyType" minOccurs="0"/>
</xsd:sequence>
</xsd:sequence>
<xsd:attribute name="kind" type="DoxSimpleSectKind" />
</xsd:complexType>
<xsd:complexType name="docVarListEntryType">
<xsd:sequence>
<xsd:element name="term" type="docTitleType" />
</xsd:sequence>
</xsd:complexType>
<xsd:group name="docVariableListGroup">
<xsd:sequence>
<xsd:element name="varlistentry" type="docVarListEntryType" />
<xsd:element name="listitem" type="docListItemType" />
</xsd:sequence>
</xsd:group>
<xsd:complexType name="docVariableListType">
<xsd:sequence>
<xsd:group ref="docVariableListGroup" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="docRefTextType" mixed="true">
<xsd:group ref="docTitleCmdGroup" minOccurs="0" maxOccurs="unbounded" />
<xsd:attribute name="refid" type="xsd:string" />
<xsd:attribute name="kindref" type="DoxRefKind" />
<xsd:attribute name="external" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docTableType">
<xsd:sequence>
<xsd:element name="row" type="docRowType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="caption" type="docCaptionType" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="rows" type="xsd:integer" />
<xsd:attribute name="cols" type="xsd:integer" />
</xsd:complexType>
<xsd:complexType name="docRowType">
<xsd:sequence>
<xsd:element name="entry" type="docEntryType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="docEntryType">
<xsd:sequence>
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="thead" type="DoxBool" />
</xsd:complexType>
<xsd:complexType name="docCaptionType" mixed="true">
<xsd:group ref="docTitleCmdGroup" minOccurs="0" maxOccurs="unbounded" />
</xsd:complexType>
<xsd:complexType name="docHeadingType" mixed="true">
<xsd:group ref="docTitleCmdGroup" minOccurs="0" maxOccurs="unbounded" />
<xsd:attribute name="level" type="xsd:integer" /> <!-- todo: range 1-6 -->
</xsd:complexType>
<xsd:complexType name="docImageType" mixed="true">
<xsd:group ref="docTitleCmdGroup" minOccurs="0" maxOccurs="unbounded" />
<xsd:attribute name="type" type="DoxImageKind" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="width" type="xsd:string" />
<xsd:attribute name="height" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docDotFileType" mixed="true">
<xsd:group ref="docTitleCmdGroup" minOccurs="0" maxOccurs="unbounded" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docTocItemType" mixed="true">
<xsd:group ref="docTitleCmdGroup" minOccurs="0" maxOccurs="unbounded" />
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docTocListType">
<xsd:sequence>
<xsd:element name="tocitem" type="docTocItemType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="docLanguageType">
<xsd:sequence>
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="langid" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docParamListType">
<xsd:sequence>
<xsd:element name="parameteritem" type="docParamListItem" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
<xsd:attribute name="kind" type="DoxParamListKind" />
</xsd:complexType>
<xsd:complexType name="docParamListItem">
<xsd:sequence>
<xsd:element name="parameternamelist" type="docParamNameList" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="parameterdescription" type="descriptionType" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="docParamNameList">
<xsd:sequence>
<xsd:element name="parametername" type="docParamName" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="docParamName" mixed="true">
<xsd:sequence>
<xsd:element name="ref" type="refTextType" minOccurs="0" maxOccurs="1" />
</xsd:sequence>
<xsd:attribute name="direction" type="DoxParamDir" use="optional" />
</xsd:complexType>
<xsd:complexType name="docXRefSectType">
<xsd:sequence>
<xsd:element name="xreftitle" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="xrefdescription" type="descriptionType" />
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docCopyType">
<xsd:sequence>
<xsd:element name="para" type="docParaType" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="sect1" type="docSect1Type" minOccurs="0" maxOccurs="unbounded" />
<xsd:element name="internal" type="docInternalType" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="link" type="xsd:string" />
</xsd:complexType>
<xsd:complexType name="docCharType">
<xsd:attribute name="char" type="DoxCharRange"/>
</xsd:complexType>
<xsd:complexType name="docEmptyType"/>
<!-- Simple types -->
<xsd:simpleType name="DoxBool">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="yes" />
<xsd:enumeration value="no" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxGraphRelation">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="include" />
<xsd:enumeration value="usage" />
<xsd:enumeration value="template-instance" />
<xsd:enumeration value="public-inheritance" />
<xsd:enumeration value="protected-inheritance" />
<xsd:enumeration value="private-inheritance" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxRefKind">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="compound" />
<xsd:enumeration value="member" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxMemberKind">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="define" />
<xsd:enumeration value="property" />
<xsd:enumeration value="event" />
<xsd:enumeration value="variable" />
<xsd:enumeration value="typedef" />
<xsd:enumeration value="enum" />
<xsd:enumeration value="function" />
<xsd:enumeration value="signal" />
<xsd:enumeration value="prototype" />
<xsd:enumeration value="friend" />
<xsd:enumeration value="dcop" />
<xsd:enumeration value="slot" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxProtectionKind">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="public" />
<xsd:enumeration value="protected" />
<xsd:enumeration value="private" />
<xsd:enumeration value="package" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxVirtualKind">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="non-virtual" />
<xsd:enumeration value="virtual" />
<xsd:enumeration value="pure-virtual" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxCompoundKind">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="class" />
<xsd:enumeration value="struct" />
<xsd:enumeration value="union" />
<xsd:enumeration value="interface" />
<xsd:enumeration value="protocol" />
<xsd:enumeration value="category" />
<xsd:enumeration value="exception" />
<xsd:enumeration value="file" />
<xsd:enumeration value="namespace" />
<xsd:enumeration value="group" />
<xsd:enumeration value="page" />
<xsd:enumeration value="example" />
<xsd:enumeration value="dir" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxSectionKind">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="user-defined" />
<xsd:enumeration value="public-type" />
<xsd:enumeration value="public-func" />
<xsd:enumeration value="public-attrib" />
<xsd:enumeration value="public-slot" />
<xsd:enumeration value="signal" />
<xsd:enumeration value="dcop-func" />
<xsd:enumeration value="property" />
<xsd:enumeration value="event" />
<xsd:enumeration value="public-static-func" />
<xsd:enumeration value="public-static-attrib" />
<xsd:enumeration value="protected-type" />
<xsd:enumeration value="protected-func" />
<xsd:enumeration value="protected-attrib" />
<xsd:enumeration value="protected-slot" />
<xsd:enumeration value="protected-static-func" />
<xsd:enumeration value="protected-static-attrib" />
<xsd:enumeration value="package-type" />
<xsd:enumeration value="package-func" />
<xsd:enumeration value="package-attrib" />
<xsd:enumeration value="package-static-func" />
<xsd:enumeration value="package-static-attrib" />
<xsd:enumeration value="private-type" />
<xsd:enumeration value="private-func" />
<xsd:enumeration value="private-attrib" />
<xsd:enumeration value="private-slot" />
<xsd:enumeration value="private-static-func" />
<xsd:enumeration value="private-static-attrib" />
<xsd:enumeration value="friend" />
<xsd:enumeration value="related" />
<xsd:enumeration value="define" />
<xsd:enumeration value="prototype" />
<xsd:enumeration value="typedef" />
<xsd:enumeration value="enum" />
<xsd:enumeration value="func" />
<xsd:enumeration value="var" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxHighlightClass">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="comment" />
<xsd:enumeration value="normal" />
<xsd:enumeration value="preprocessor" />
<xsd:enumeration value="keyword" />
<xsd:enumeration value="keywordtype" />
<xsd:enumeration value="keywordflow" />
<xsd:enumeration value="stringliteral" />
<xsd:enumeration value="charliteral" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxSimpleSectKind">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="see" />
<xsd:enumeration value="return" />
<xsd:enumeration value="author" />
<xsd:enumeration value="authors" />
<xsd:enumeration value="version" />
<xsd:enumeration value="since" />
<xsd:enumeration value="date" />
<xsd:enumeration value="note" />
<xsd:enumeration value="warning" />
<xsd:enumeration value="pre" />
<xsd:enumeration value="post" />
<xsd:enumeration value="invariant" />
<xsd:enumeration value="remark" />
<xsd:enumeration value="attention" />
<xsd:enumeration value="par" />
<xsd:enumeration value="rcs" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxVersionNumber">
<xsd:restriction base="xsd:string">
<xsd:pattern value="\d+\.\d+.*" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxImageKind">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="html" />
<xsd:enumeration value="latex" />
<xsd:enumeration value="rtf" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxParamListKind">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="param" />
<xsd:enumeration value="retval" />
<xsd:enumeration value="exception" />
<xsd:enumeration value="templateparam" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxCharRange">
<xsd:restriction base="xsd:string">
<xsd:pattern value="[aeiouncAEIOUNC]" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxParamDir">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="in"/>
<xsd:enumeration value="out"/>
<xsd:enumeration value="inout"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="DoxAccessor">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="retain"/>
<xsd:enumeration value="copy"/>
<xsd:enumeration value="assign"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>

View File

@@ -1,17 +0,0 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<doxygenindex xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="index.xsd" version="1.6.3">
<compound refid="classAadvark" kind="class"><name>Aadvark</name>
<member refid="classAadvark_1ab79eb58d7bb9d5ddfa5d6f783836cab9" kind="variable"><name>aadvarkness</name></member>
<member refid="classAadvark_1abd061aa5f998002e72080a34f512a059" kind="function"><name>print</name></member>
<member refid="classAadvark_1adf1a4b97a641411a74a04ab312484462" kind="function"><name>Aadvark</name></member>
<member refid="classAadvark_1affd2ada0a85807efcbe26615a848f53e" kind="function"><name>get_aadvarkness</name></member>
</compound>
<compound refid="aadvark_8cc" kind="file"><name>aadvark.cc</name>
<member refid="aadvark_8cc_1acb52858524210ec6dddc3e16d1e52946" kind="function"><name>aadvarky_enough</name></member>
<member refid="aadvark_8cc_1ae66f6b31b5ad750f1fe042a706a4e3d4" kind="function"><name>main</name></member>
</compound>
<compound refid="aadvark_8h" kind="file"><name>aadvark.h</name>
<member refid="aadvark_8h_1acb52858524210ec6dddc3e16d1e52946" kind="function"><name>aadvarky_enough</name></member>
<member refid="aadvark_8h_1ae66f6b31b5ad750f1fe042a706a4e3d4" kind="function"><name>main</name></member>
</compound>
</doxygenindex>

View File

@@ -1,66 +0,0 @@
<?xml version='1.0' encoding='utf-8' ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="doxygenindex" type="DoxygenType"/>
<xsd:complexType name="DoxygenType">
<xsd:sequence>
<xsd:element name="compound" type="CompoundType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="version" type="xsd:string" use="required"/>
</xsd:complexType>
<xsd:complexType name="CompoundType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
<xsd:element name="member" type="MemberType" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
<xsd:attribute name="refid" type="xsd:string" use="required"/>
<xsd:attribute name="kind" type="CompoundKind" use="required"/>
</xsd:complexType>
<xsd:complexType name="MemberType">
<xsd:sequence>
<xsd:element name="name" type="xsd:string"/>
</xsd:sequence>
<xsd:attribute name="refid" type="xsd:string" use="required"/>
<xsd:attribute name="kind" type="MemberKind" use="required"/>
</xsd:complexType>
<xsd:simpleType name="CompoundKind">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="class"/>
<xsd:enumeration value="struct"/>
<xsd:enumeration value="union"/>
<xsd:enumeration value="interface"/>
<xsd:enumeration value="protocol"/>
<xsd:enumeration value="category"/>
<xsd:enumeration value="exception"/>
<xsd:enumeration value="file"/>
<xsd:enumeration value="namespace"/>
<xsd:enumeration value="group"/>
<xsd:enumeration value="page"/>
<xsd:enumeration value="example"/>
<xsd:enumeration value="dir"/>
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="MemberKind">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="define"/>
<xsd:enumeration value="property"/>
<xsd:enumeration value="event"/>
<xsd:enumeration value="variable"/>
<xsd:enumeration value="typedef"/>
<xsd:enumeration value="enum"/>
<xsd:enumeration value="enumvalue"/>
<xsd:enumeration value="function"/>
<xsd:enumeration value="signal"/>
<xsd:enumeration value="prototype"/>
<xsd:enumeration value="friend"/>
<xsd:enumeration value="dcop"/>
<xsd:enumeration value="slot"/>
</xsd:restriction>
</xsd:simpleType>
</xsd:schema>

View File

@@ -4,14 +4,14 @@
Generated Mon Feb 9 19:08:05 2009 by generateDS.py.
"""
from string import lower as str_lower
from xml.dom import minidom
from xml.dom import Node
import sys
import compoundsuper as supermod
from compoundsuper import MixedContainer
from . import compoundsuper as supermod
from .compoundsuper import MixedContainer
class DoxygenTypeSub(supermod.DoxygenType):

View File

@@ -4,9 +4,9 @@
# Generated Thu Jun 11 18:44:25 2009 by generateDS.py.
#
import sys
import getopt
from string import lower as str_lower
from xml.dom import minidom
from xml.dom import Node
@@ -19,9 +19,9 @@ from xml.dom import Node
try:
from generatedssuper import GeneratedsSuper
except ImportError, exp:
except ImportError as exp:
class GeneratedsSuper:
class GeneratedsSuper(object):
def format_string(self, input_data, input_name=''):
return input_data
def format_integer(self, input_data, input_name=''):
@@ -64,7 +64,7 @@ def showIndent(outfile, level):
outfile.write(' ')
def quote_xml(inStr):
s1 = (isinstance(inStr, basestring) and inStr or
s1 = (isinstance(inStr, str) and inStr or
'%s' % inStr)
s1 = s1.replace('&', '&amp;')
s1 = s1.replace('<', '&lt;')
@@ -72,7 +72,7 @@ def quote_xml(inStr):
return s1
def quote_attrib(inStr):
s1 = (isinstance(inStr, basestring) and inStr or
s1 = (isinstance(inStr, str) and inStr or
'%s' % inStr)
s1 = s1.replace('&', '&amp;')
s1 = s1.replace('<', '&lt;')
@@ -102,7 +102,7 @@ def quote_python(inStr):
return '"""%s"""' % s1
class MixedContainer:
class MixedContainer(object):
# Constants for category:
CategoryNone = 0
CategoryText = 1
@@ -4221,7 +4221,7 @@ class codelineType(GeneratedsSuper):
if attrs.get('lineno'):
try:
self.lineno = int(attrs.get('lineno').value)
except ValueError, exp:
except ValueError as exp:
raise ValueError('Bad integer attribute (lineno): %s' % exp)
if attrs.get('refkind'):
self.refkind = attrs.get('refkind').value
@@ -4504,12 +4504,12 @@ class referenceType(GeneratedsSuper):
if attrs.get('endline'):
try:
self.endline = int(attrs.get('endline').value)
except ValueError, exp:
except ValueError as exp:
raise ValueError('Bad integer attribute (endline): %s' % exp)
if attrs.get('startline'):
try:
self.startline = int(attrs.get('startline').value)
except ValueError, exp:
except ValueError as exp:
raise ValueError('Bad integer attribute (startline): %s' % exp)
if attrs.get('refid'):
self.refid = attrs.get('refid').value
@@ -4627,17 +4627,17 @@ class locationType(GeneratedsSuper):
if attrs.get('bodystart'):
try:
self.bodystart = int(attrs.get('bodystart').value)
except ValueError, exp:
except ValueError as exp:
raise ValueError('Bad integer attribute (bodystart): %s' % exp)
if attrs.get('line'):
try:
self.line = int(attrs.get('line').value)
except ValueError, exp:
except ValueError as exp:
raise ValueError('Bad integer attribute (line): %s' % exp)
if attrs.get('bodyend'):
try:
self.bodyend = int(attrs.get('bodyend').value)
except ValueError, exp:
except ValueError as exp:
raise ValueError('Bad integer attribute (bodyend): %s' % exp)
if attrs.get('bodyfile'):
self.bodyfile = attrs.get('bodyfile').value
@@ -6778,12 +6778,12 @@ class docTableType(GeneratedsSuper):
if attrs.get('rows'):
try:
self.rows = int(attrs.get('rows').value)
except ValueError, exp:
except ValueError as exp:
raise ValueError('Bad integer attribute (rows): %s' % exp)
if attrs.get('cols'):
try:
self.cols = int(attrs.get('cols').value)
except ValueError, exp:
except ValueError as exp:
raise ValueError('Bad integer attribute (cols): %s' % exp)
def buildChildren(self, child_, nodeName_):
if child_.nodeType == Node.ELEMENT_NODE and \
@@ -7108,7 +7108,7 @@ class docHeadingType(GeneratedsSuper):
if attrs.get('level'):
try:
self.level = int(attrs.get('level').value)
except ValueError, exp:
except ValueError as exp:
raise ValueError('Bad integer attribute (level): %s' % exp)
def buildChildren(self, child_, nodeName_):
if child_.nodeType == Node.TEXT_NODE:
@@ -8283,7 +8283,7 @@ Options:
"""
def usage():
print USAGE_TEXT
print(USAGE_TEXT)
sys.exit(1)
@@ -8339,4 +8339,3 @@ if __name__ == '__main__':
main()
#import pdb
#pdb.run('main()')

View File

@@ -8,9 +8,9 @@ from xml.dom import minidom
import os
import sys
import compound
from . import compound
import indexsuper as supermod
from . import indexsuper as supermod
class DoxygenTypeSub(supermod.DoxygenType):
def __init__(self, version=None, compound=None):

View File

@@ -4,9 +4,9 @@
# Generated Thu Jun 11 18:43:54 2009 by generateDS.py.
#
import sys
import getopt
from string import lower as str_lower
from xml.dom import minidom
from xml.dom import Node
@@ -19,9 +19,9 @@ from xml.dom import Node
try:
from generatedssuper import GeneratedsSuper
except ImportError, exp:
except ImportError as exp:
class GeneratedsSuper:
class GeneratedsSuper(object):
def format_string(self, input_data, input_name=''):
return input_data
def format_integer(self, input_data, input_name=''):
@@ -64,7 +64,7 @@ def showIndent(outfile, level):
outfile.write(' ')
def quote_xml(inStr):
s1 = (isinstance(inStr, basestring) and inStr or
s1 = (isinstance(inStr, str) and inStr or
'%s' % inStr)
s1 = s1.replace('&', '&amp;')
s1 = s1.replace('<', '&lt;')
@@ -72,7 +72,7 @@ def quote_xml(inStr):
return s1
def quote_attrib(inStr):
s1 = (isinstance(inStr, basestring) and inStr or
s1 = (isinstance(inStr, str) and inStr or
'%s' % inStr)
s1 = s1.replace('&', '&amp;')
s1 = s1.replace('<', '&lt;')
@@ -102,7 +102,7 @@ def quote_python(inStr):
return '"""%s"""' % s1
class MixedContainer:
class MixedContainer(object):
# Constants for category:
CategoryNone = 0
CategoryText = 1
@@ -462,7 +462,7 @@ Options:
"""
def usage():
print USAGE_TEXT
print(USAGE_TEXT)
sys.exit(1)
@@ -520,4 +520,3 @@ if __name__ == '__main__':
main()
#import pdb
#pdb.run('main()')

View File

@@ -1,16 +0,0 @@
#!/bin/sh
# 1st parameter is absolute path to component source directory
# 2nd parameter is absolute path to component build directory
# 3rd parameter is path to Python QA directory
# Note: calling master run_tests.sh in gnuradio core is not strictly
# correct, as it will result in a partially bogus PYTHONPATH, but it
# does make the correct paths in the second half so all is well.
@PYTHON@ @srcdir@/__init__.py
# @top_builddir@/run_tests.sh \
# @abs_top_srcdir@/gnuradio-core \
# @abs_top_builddir@/gnuradio-core \
# @srcdir@

View File

@@ -1,23 +1,12 @@
#
# Copyright 2010 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
#
# This file was generated by gr_modtool, a tool from the GNU Radio framework
# This file is a part of gr-air-modes
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
#
"""
Utilities for extracting text from generated classes.
"""
@@ -26,7 +15,7 @@ def is_string(txt):
if isinstance(txt, str):
return True
try:
if isinstance(txt, unicode):
if isinstance(txt, str):
return True
except NameError:
pass
@@ -49,7 +38,7 @@ def description_bit(obj):
elif is_string(obj):
return obj
else:
raise StandardError('Expecting a string or something with content, content_ or value attribute')
raise Exception('Expecting a string or something with content, content_ or value attribute')
# If this bit is a paragraph then add one some line breaks.
if hasattr(obj, 'name') and obj.name == 'para':
result += "\n\n"

View File

@@ -1,6 +1,6 @@
/*!
* \defgroup block GNU Radio GR-AIR-MODES C++ Signal Processing Blocks
* \brief All C++ blocks that can be used from the GR-AIR-MODES GNU Radio
/*!
* \defgroup block GNU Radio gr-air-modes C++ Signal Processing Blocks
* \brief All C++ blocks that can be used from the gr-air-modes GNU Radio
* module are listed here or in the subcategories below.
*
*/

View File

@@ -1,8 +1,8 @@
/*! \mainpage
Welcome to the GNU Radio GR-AIR-MODES Block
Welcome to the GNU Radio gr-air-modes Block
This is the intro page for the Doxygen manual generated for the GR-AIR-MODES
This is the intro page for the Doxygen manual generated for the gr-air-modes
block (docs/doxygen/other/main_page.dox). Edit it to add more detailed
documentation about the new GNU Radio modules contained in this
project.

View File

@@ -0,0 +1,19 @@
#ifndef PYDOC_MACROS_H
#define PYDOC_MACROS_H
#define __EXPAND(x) x
#define __COUNT(_1, _2, _3, _4, _5, _6, _7, COUNT, ...) COUNT
#define __VA_SIZE(...) __EXPAND(__COUNT(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1))
#define __CAT1(a, b) a##b
#define __CAT2(a, b) __CAT1(a, b)
#define __DOC1(n1) __doc_##n1
#define __DOC2(n1, n2) __doc_##n1##_##n2
#define __DOC3(n1, n2, n3) __doc_##n1##_##n2##_##n3
#define __DOC4(n1, n2, n3, n4) __doc_##n1##_##n2##_##n3##_##n4
#define __DOC5(n1, n2, n3, n4, n5) __doc_##n1##_##n2##_##n3##_##n4##_##n5
#define __DOC6(n1, n2, n3, n4, n5, n6) __doc_##n1##_##n2##_##n3##_##n4##_##n5##_##n6
#define __DOC7(n1, n2, n3, n4, n5, n6, n7) \
__doc_##n1##_##n2##_##n3##_##n4##_##n5##_##n6##_##n7
#define DOC(...) __EXPAND(__EXPAND(__CAT2(__DOC, __VA_SIZE(__VA_ARGS__)))(__VA_ARGS__))
#endif // PYDOC_MACROS_H

View File

@@ -1,253 +0,0 @@
#
# Copyright 2010,2011 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# GNU Radio 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.
#
# GNU Radio 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 GNU Radio; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
"""
Creates the swig_doc.i SWIG interface file.
Execute using: python swig_doc.py xml_path outputfilename
The file instructs SWIG to transfer the doxygen comments into the
python docstrings.
"""
import sys
try:
from doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base
except ImportError:
from gnuradio.doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base
def py_name(name):
bits = name.split('_')
return '_'.join(bits[1:])
def make_name(name):
bits = name.split('_')
return bits[0] + '_make_' + '_'.join(bits[1:])
class Block(object):
"""
Checks if doxyxml produced objects correspond to a gnuradio block.
"""
@classmethod
def includes(cls, item):
if not isinstance(item, DoxyClass):
return False
# Check for a parsing error.
if item.error():
return False
return item.has_member(make_name(item.name()), DoxyFriend)
def utoascii(text):
"""
Convert unicode text into ascii and escape quotes.
"""
if text is None:
return ''
out = text.encode('ascii', 'replace')
out = out.replace('"', '\\"')
return out
def combine_descriptions(obj):
"""
Combines the brief and detailed descriptions of an object together.
"""
description = []
bd = obj.brief_description.strip()
dd = obj.detailed_description.strip()
if bd:
description.append(bd)
if dd:
description.append(dd)
return utoascii('\n\n'.join(description)).strip()
entry_templ = '%feature("docstring") {name} "{docstring}"'
def make_entry(obj, name=None, templ="{description}", description=None):
"""
Create a docstring entry for a swig interface file.
obj - a doxyxml object from which documentation will be extracted.
name - the name of the C object (defaults to obj.name())
templ - an optional template for the docstring containing only one
variable named 'description'.
description - if this optional variable is set then it's value is
used as the description instead of extracting it from obj.
"""
if name is None:
name=obj.name()
if description is None:
description = combine_descriptions(obj)
docstring = templ.format(description=description)
if not docstring:
return ''
return entry_templ.format(
name=name,
docstring=docstring,
)
def make_func_entry(func, name=None, description=None, params=None):
"""
Create a function docstring entry for a swig interface file.
func - a doxyxml object from which documentation will be extracted.
name - the name of the C object (defaults to func.name())
description - if this optional variable is set then it's value is
used as the description instead of extracting it from func.
params - a parameter list that overrides using func.params.
"""
if params is None:
params = func.params
params = [prm.declname for prm in params]
if params:
sig = "Params: (%s)" % ", ".join(params)
else:
sig = "Params: (NONE)"
templ = "{description}\n\n" + sig
return make_entry(func, name=name, templ=utoascii(templ),
description=description)
def make_class_entry(klass, description=None):
"""
Create a class docstring for a swig interface file.
"""
output = []
output.append(make_entry(klass, description=description))
for func in klass.in_category(DoxyFunction):
name = klass.name() + '::' + func.name()
output.append(make_func_entry(func, name=name))
return "\n\n".join(output)
def make_block_entry(di, block):
"""
Create class and function docstrings of a gnuradio block for a
swig interface file.
"""
descriptions = []
# Get the documentation associated with the class.
class_desc = combine_descriptions(block)
if class_desc:
descriptions.append(class_desc)
# Get the documentation associated with the make function
make_func = di.get_member(make_name(block.name()), DoxyFunction)
make_func_desc = combine_descriptions(make_func)
if make_func_desc:
descriptions.append(make_func_desc)
# Get the documentation associated with the file
try:
block_file = di.get_member(block.name() + ".h", DoxyFile)
file_desc = combine_descriptions(block_file)
if file_desc:
descriptions.append(file_desc)
except base.Base.NoSuchMember:
# Don't worry if we can't find a matching file.
pass
# And join them all together to make a super duper description.
super_description = "\n\n".join(descriptions)
# Associate the combined description with the class and
# the make function.
output = []
output.append(make_class_entry(block, description=super_description))
creator = block.get_member(block.name(), DoxyFunction)
output.append(make_func_entry(make_func, description=super_description,
params=creator.params))
return "\n\n".join(output)
def make_swig_interface_file(di, swigdocfilename, custom_output=None):
output = ["""
/*
* This file was automatically generated using swig_doc.py.
*
* Any changes to it will be lost next time it is regenerated.
*/
"""]
if custom_output is not None:
output.append(custom_output)
# Create docstrings for the blocks.
blocks = di.in_category(Block)
make_funcs = set([])
for block in blocks:
try:
make_func = di.get_member(make_name(block.name()), DoxyFunction)
make_funcs.add(make_func.name())
output.append(make_block_entry(di, block))
except block.ParsingError:
print('Parsing error for block %s' % block.name())
# Create docstrings for functions
# Don't include the make functions since they have already been dealt with.
funcs = [f for f in di.in_category(DoxyFunction) if f.name() not in make_funcs]
for f in funcs:
try:
output.append(make_func_entry(f))
except f.ParsingError:
print('Parsing error for function %s' % f.name())
# Create docstrings for classes
block_names = [block.name() for block in blocks]
klasses = [k for k in di.in_category(DoxyClass) if k.name() not in block_names]
for k in klasses:
try:
output.append(make_class_entry(k))
except k.ParsingError:
print('Parsing error for class %s' % k.name())
# Docstrings are not created for anything that is not a function or a class.
# If this excludes anything important please add it here.
output = "\n\n".join(output)
swig_doc = file(swigdocfilename, 'w')
swig_doc.write(output)
swig_doc.close()
if __name__ == "__main__":
# Parse command line options and set up doxyxml.
err_msg = "Execute using: python swig_doc.py xml_path outputfilename"
if len(sys.argv) != 3:
raise StandardError(err_msg)
xml_path = sys.argv[1]
swigdocfilename = sys.argv[2]
di = DoxyIndex(xml_path)
# gnuradio.gr.msq_queue.insert_tail and delete_head create errors unless docstrings are defined!
# This is presumably a bug in SWIG.
#msg_q = di.get_member(u'gr_msg_queue', DoxyClass)
#insert_tail = msg_q.get_member(u'insert_tail', DoxyFunction)
#delete_head = msg_q.get_member(u'delete_head', DoxyFunction)
output = []
#output.append(make_func_entry(insert_tail, name='gr_py_msg_queue__insert_tail'))
#output.append(make_func_entry(delete_head, name='gr_py_msg_queue__delete_head'))
custom_output = "\n\n".join(output)
# Generate the docstrings interface file.
make_swig_interface_file(di, swigdocfilename, custom_output=custom_output)

View File

@@ -0,0 +1,346 @@
#
# Copyright 2010-2012 Free Software Foundation, Inc.
#
# This file was generated by gr_modtool, a tool from the GNU Radio framework
# This file is a part of gnuradio
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
#
"""
Updates the *pydoc_h files for a module
Execute using: python update_pydoc.py xml_path outputfilename
The file instructs Pybind11 to transfer the doxygen comments into the
python docstrings.
"""
import os, sys, time, glob, re, json
from argparse import ArgumentParser
from doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile
from doxyxml import DoxyOther, base
def py_name(name):
bits = name.split('_')
return '_'.join(bits[1:])
def make_name(name):
bits = name.split('_')
return bits[0] + '_make_' + '_'.join(bits[1:])
class Block(object):
"""
Checks if doxyxml produced objects correspond to a gnuradio block.
"""
@classmethod
def includes(cls, item):
if not isinstance(item, DoxyClass):
return False
# Check for a parsing error.
if item.error():
return False
friendname = make_name(item.name())
is_a_block = item.has_member(friendname, DoxyFriend)
# But now sometimes the make function isn't a friend so check again.
if not is_a_block:
is_a_block = di.has_member(friendname, DoxyFunction)
return is_a_block
class Block2(object):
"""
Checks if doxyxml produced objects correspond to a new style
gnuradio block.
"""
@classmethod
def includes(cls, item):
if not isinstance(item, DoxyClass):
return False
# Check for a parsing error.
if item.error():
return False
is_a_block2 = item.has_member('make', DoxyFunction) and item.has_member('sptr', DoxyOther)
return is_a_block2
def utoascii(text):
"""
Convert unicode text into ascii and escape quotes and backslashes.
"""
if text is None:
return ''
out = text.encode('ascii', 'replace')
# swig will require us to replace blackslash with 4 backslashes
# TODO: evaluate what this should be for pybind11
out = out.replace(b'\\', b'\\\\\\\\')
out = out.replace(b'"', b'\\"').decode('ascii')
return str(out)
def combine_descriptions(obj):
"""
Combines the brief and detailed descriptions of an object together.
"""
description = []
bd = obj.brief_description.strip()
dd = obj.detailed_description.strip()
if bd:
description.append(bd)
if dd:
description.append(dd)
return utoascii('\n\n'.join(description)).strip()
def format_params(parameteritems):
output = ['Args:']
template = ' {0} : {1}'
for pi in parameteritems:
output.append(template.format(pi.name, pi.description))
return '\n'.join(output)
entry_templ = '%feature("docstring") {name} "{docstring}"'
def make_entry(obj, name=None, templ="{description}", description=None, params=[]):
"""
Create a docstring key/value pair, where the key is the object name.
obj - a doxyxml object from which documentation will be extracted.
name - the name of the C object (defaults to obj.name())
templ - an optional template for the docstring containing only one
variable named 'description'.
description - if this optional variable is set then it's value is
used as the description instead of extracting it from obj.
"""
if name is None:
name=obj.name()
if hasattr(obj,'_parse_data') and hasattr(obj._parse_data,'definition'):
name=obj._parse_data.definition.split(' ')[-1]
if "operator " in name:
return ''
if description is None:
description = combine_descriptions(obj)
if params:
description += '\n\n'
description += utoascii(format_params(params))
docstring = templ.format(description=description)
return {name: docstring}
def make_class_entry(klass, description=None, ignored_methods=[], params=None):
"""
Create a class docstring key/value pair.
"""
if params is None:
params = klass.params
output = {}
output.update(make_entry(klass, description=description, params=params))
for func in klass.in_category(DoxyFunction):
if func.name() not in ignored_methods:
name = klass.name() + '::' + func.name()
output.update(make_entry(func, name=name))
return output
def make_block_entry(di, block):
"""
Create class and function docstrings of a gnuradio block
"""
descriptions = []
# Get the documentation associated with the class.
class_desc = combine_descriptions(block)
if class_desc:
descriptions.append(class_desc)
# Get the documentation associated with the make function
make_func = di.get_member(make_name(block.name()), DoxyFunction)
make_func_desc = combine_descriptions(make_func)
if make_func_desc:
descriptions.append(make_func_desc)
# Get the documentation associated with the file
try:
block_file = di.get_member(block.name() + ".h", DoxyFile)
file_desc = combine_descriptions(block_file)
if file_desc:
descriptions.append(file_desc)
except base.Base.NoSuchMember:
# Don't worry if we can't find a matching file.
pass
# And join them all together to make a super duper description.
super_description = "\n\n".join(descriptions)
# Associate the combined description with the class and
# the make function.
output = {}
output.update(make_class_entry(block, description=super_description))
output.update(make_entry(make_func, description=super_description,
params=block.params))
return output
def make_block2_entry(di, block):
"""
Create class and function docstrings of a new style gnuradio block
"""
# For new style blocks all the relevant documentation should be
# associated with the 'make' method.
class_description = combine_descriptions(block)
make_func = block.get_member('make', DoxyFunction)
make_description = combine_descriptions(make_func)
description = class_description + "\n\nConstructor Specific Documentation:\n\n" + make_description
# Associate the combined description with the class and
# the make function.
output = {}
output.update(make_class_entry(
block, description=description,
ignored_methods=['make'], params=make_func.params))
makename = block.name() + '::make'
output.update(make_entry(
make_func, name=makename, description=description,
params=make_func.params))
return output
def get_docstrings_dict(di, custom_output=None):
output = {}
if custom_output:
output.update(custom_output)
# Create docstrings for the blocks.
blocks = di.in_category(Block)
blocks2 = di.in_category(Block2)
make_funcs = set([])
for block in blocks:
try:
make_func = di.get_member(make_name(block.name()), DoxyFunction)
# Don't want to risk writing to output twice.
if make_func.name() not in make_funcs:
make_funcs.add(make_func.name())
output.update(make_block_entry(di, block))
except block.ParsingError:
sys.stderr.write('Parsing error for block {0}\n'.format(block.name()))
raise
for block in blocks2:
try:
make_func = block.get_member('make', DoxyFunction)
make_func_name = block.name() +'::make'
# Don't want to risk writing to output twice.
if make_func_name not in make_funcs:
make_funcs.add(make_func_name)
output.update(make_block2_entry(di, block))
except block.ParsingError:
sys.stderr.write('Parsing error for block {0}\n'.format(block.name()))
raise
# Create docstrings for functions
# Don't include the make functions since they have already been dealt with.
funcs = [f for f in di.in_category(DoxyFunction)
if f.name() not in make_funcs and not f.name().startswith('std::')]
for f in funcs:
try:
output.update(make_entry(f))
except f.ParsingError:
sys.stderr.write('Parsing error for function {0}\n'.format(f.name()))
# Create docstrings for classes
block_names = [block.name() for block in blocks]
block_names += [block.name() for block in blocks2]
klasses = [k for k in di.in_category(DoxyClass)
if k.name() not in block_names and not k.name().startswith('std::')]
for k in klasses:
try:
output.update(make_class_entry(k))
except k.ParsingError:
sys.stderr.write('Parsing error for class {0}\n'.format(k.name()))
# Docstrings are not created for anything that is not a function or a class.
# If this excludes anything important please add it here.
return output
def sub_docstring_in_pydoc_h(pydoc_files, docstrings_dict, output_dir, filter_str=None):
if filter_str:
docstrings_dict = {k: v for k, v in docstrings_dict.items() if k.startswith(filter_str)}
with open(os.path.join(output_dir,'docstring_status'),'w') as status_file:
for pydoc_file in pydoc_files:
if filter_str:
filter_str2 = "::".join((filter_str,os.path.split(pydoc_file)[-1].split('_pydoc_template.h')[0]))
docstrings_dict2 = {k: v for k, v in docstrings_dict.items() if k.startswith(filter_str2)}
else:
docstrings_dict2 = docstrings_dict
file_in = open(pydoc_file,'r').read()
for key, value in docstrings_dict2.items():
file_in_tmp = file_in
try:
doc_key = key.split("::")
# if 'gr' in doc_key:
# doc_key.remove('gr')
doc_key = '_'.join(doc_key)
regexp = r'(__doc_{} =\sR\"doc\()[^)]*(\)doc\")'.format(doc_key)
regexp = re.compile(regexp, re.MULTILINE)
(file_in, nsubs) = regexp.subn(r'\1'+value+r'\2', file_in, count=1)
if nsubs == 1:
status_file.write("PASS: " + pydoc_file + "\n")
except KeyboardInterrupt:
raise KeyboardInterrupt
except: # be permissive, TODO log, but just leave the docstring blank
status_file.write("FAIL: " + pydoc_file + "\n")
file_in = file_in_tmp
output_pathname = os.path.join(output_dir, os.path.basename(pydoc_file).replace('_template.h','.h'))
# FIXME: Remove this debug print
print('output docstrings to {}'.format(output_pathname))
with open(output_pathname,'w') as file_out:
file_out.write(file_in)
def copy_docstring_templates(pydoc_files, output_dir):
with open(os.path.join(output_dir,'docstring_status'),'w') as status_file:
for pydoc_file in pydoc_files:
file_in = open(pydoc_file,'r').read()
output_pathname = os.path.join(output_dir, os.path.basename(pydoc_file).replace('_template.h','.h'))
# FIXME: Remove this debug print
print('copy docstrings to {}'.format(output_pathname))
with open(output_pathname,'w') as file_out:
file_out.write(file_in)
status_file.write("DONE")
def argParse():
"""Parses commandline args."""
desc='Scrape the doxygen generated xml for docstrings to insert into python bindings'
parser = ArgumentParser(description=desc)
parser.add_argument("function", help="Operation to perform on docstrings", choices=["scrape","sub","copy"])
parser.add_argument("--xml_path")
parser.add_argument("--bindings_dir")
parser.add_argument("--output_dir")
parser.add_argument("--json_path")
parser.add_argument("--filter", default=None)
return parser.parse_args()
if __name__ == "__main__":
# Parse command line options and set up doxyxml.
args = argParse()
if args.function.lower() == 'scrape':
di = DoxyIndex(args.xml_path)
docstrings_dict = get_docstrings_dict(di)
with open(args.json_path, 'w') as fp:
json.dump(docstrings_dict, fp)
elif args.function.lower() == 'sub':
with open(args.json_path, 'r') as fp:
docstrings_dict = json.load(fp)
pydoc_files = glob.glob(os.path.join(args.bindings_dir,'*_pydoc_template.h'))
sub_docstring_in_pydoc_h(pydoc_files, docstrings_dict, args.output_dir, args.filter)
elif args.function.lower() == 'copy':
pydoc_files = glob.glob(os.path.join(args.bindings_dir,'*_pydoc_template.h'))
copy_docstring_templates(pydoc_files, args.output_dir)

View File

@@ -21,9 +21,9 @@
# Install public header files
########################################################################
install(FILES
air_modes_preamble.h
air_modes_slicer.h
air_modes_types.h
air_modes_api.h
DESTINATION include/gr-air-modes
preamble.h
slicer.h
types.h
api.h
DESTINATION include/gr_air_modes
)

View File

@@ -22,7 +22,7 @@
#ifndef INCLUDED_AIR_MODES_API_H
#define INCLUDED_AIR_MODES_API_H
#include <gruel/attributes.h>
#include <gnuradio/attributes.h>
#ifdef AIR_MODES_EXPORTS
# define AIR_MODES_API __GR_ATTR_EXPORT

View File

@@ -23,5 +23,8 @@
#ifndef INCLUDED_MODES_CRC_H
#define INCLUDED_MODES_CRC_H
extern const unsigned int modes_crc_table[112];
int modes_check_crc(unsigned char data[], int length);
unsigned int modes_check_crc(unsigned char data[], int length);
bruteResultTypeDef modes_ec_brute(modes_packet &err_packet);
unsigned next_set_of_n_elements(unsigned x);
#endif

View File

@@ -1,5 +1,5 @@
/*
# Copyright 2010 Nick Foster
# Copyright 2013 Nick Foster
#
# This file is part of gr-air-modes
#
@@ -23,40 +23,29 @@
#ifndef INCLUDED_AIR_MODES_PREAMBLE_H
#define INCLUDED_AIR_MODES_PREAMBLE_H
#include <gr_block.h>
#include <air_modes_api.h>
#include <gnuradio/block.h>
#include <air_modes/api.h>
class air_modes_preamble;
typedef boost::shared_ptr<air_modes_preamble> air_modes_preamble_sptr;
AIR_MODES_API air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshold_db);
namespace gr {
namespace air_modes {
/*!
* \brief mode select preamble detection
* \ingroup block
*/
class AIR_MODES_API air_modes_preamble : public gr_block
class AIR_MODES_API preamble : virtual public gr::block
{
private:
friend air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshold_db);
air_modes_preamble(int channel_rate, float threshold_db);
int d_check_width;
int d_chip_rate;
float d_preamble_length_us;
int d_samples_per_chip;
int d_samples_per_symbol;
float d_threshold_db;
float d_threshold;
pmt::pmt_t d_me, d_key;
gr_tag_t d_timestamp;
double d_secs_per_sample;
public:
int general_work (int noutput_items,
gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
typedef std::shared_ptr<preamble> sptr;
static sptr make(float channel_rate, float threshold_db);
virtual void set_rate(float channel_rate) = 0;
virtual void set_threshold(float threshold_db) = 0;
virtual float get_rate(void) = 0;
virtual float get_threshold(void) = 0;
};
} // namespace air_modes
} // namespace gr
#endif /* INCLUDED_AIR_MODES_PREAMBLE_H */

View File

@@ -0,0 +1,47 @@
/*
# Copyright 2013 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.
#
*/
#ifndef INCLUDED_AIR_MODES_SLICER_H
#define INCLUDED_AIR_MODES_SLICER_H
#include <air_modes/api.h>
#include <gnuradio/sync_block.h>
#include <gnuradio/msg_queue.h>
namespace gr {
namespace air_modes {
/*!
* \brief mode select slicer
* \ingroup block
*/
class AIR_MODES_API slicer : virtual public gr::sync_block
{
public:
typedef std::shared_ptr<slicer> sptr;
static sptr make(gr::msg_queue::sptr queue);
};
} //namespace air_modes
} //namespace gr
#endif /* INCLUDED_AIR_MODES_SLICER_H */

View File

@@ -24,6 +24,20 @@
#define AIR_MODES_TYPES_H
typedef enum { No_Packet = 0, Short_Packet = 1, Fruited_Packet = 2, Long_Packet = 3 } framer_packet_type;
typedef enum { No_Error = 0, Solution_Found, Too_Many_LCBs, No_Solution, Multiple_Solutions } bruteResultTypeDef;
struct modes_packet {
unsigned char data[14];
// unsigned char confidence[14]; //112 bits of boolean high/low confidence data for each bit
unsigned char lowconfbits[24]; //positions of low confidence bits within the packet
unsigned long crc;
unsigned int numlowconf;
framer_packet_type type; //what length packet are we
unsigned int message_type;
float reference_level;
double timestamp;
};
struct slice_result_t {
bool decision;

View File

@@ -1,54 +0,0 @@
/*
# 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.
#
*/
#ifndef INCLUDED_AIR_MODES_int_and_dump_H
#define INCLUDED_AIR_MODES_int_and_dump_H
#include <gr_block.h>
class air_modes_int_and_dump;
typedef boost::shared_ptr<air_modes_int_and_dump> air_modes_int_and_dump_sptr;
air_modes_int_and_dump_sptr air_make_modes_int_and_dump(int samples_per_chip);
/*!
* \brief mode select int_and_dump filter
* \ingroup block
*/
class air_modes_int_and_dump : public gr_block
{
private:
friend air_modes_int_and_dump_sptr air_make_modes_int_and_dump(int samples_per_chip);
air_modes_int_and_dump(int samples_per_chip);
int d_samples_per_chip;
float d_acc;
float d_pos;
public:
int general_work (int noutput_items,
gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
};
#endif /* INCLUDED_AIR_MODES_int_and_dump_H */

View File

@@ -22,39 +22,41 @@
########################################################################
include(GrPlatform) #define LIB_SUFFIX
add_library(air_modes SHARED
air_modes_preamble.cc
air_modes_slicer.cc
set(air_modes_sources
preamble_impl.cc
slicer_impl.cc
modes_crc.cc
)
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}")
set(air_modes_sources "${air_modes_sources}" PARENT_SCOPE)
add_library(gnuradio-air_modes SHARED
${air_modes_sources}
)
target_link_libraries(gnuradio-air_modes gnuradio::gnuradio-runtime)
target_include_directories(gnuradio-air_modes
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include>
PUBLIC $<INSTALL_INTERFACE:include>
)
set_target_properties(gnuradio-air_modes PROPERTIES DEFINE_SYMBOL "AIR_MODES_EXPORTS")
set_target_properties(gnuradio-air_modes PROPERTIES SOVERSION "${VERSION_MAJOR}")
set_target_properties(gnuradio-air_modes PROPERTIES VERSION "${VERSION_MAJOR}.${VERSION_MINOR}")
if(APPLE)
set_target_properties(gnuradio-air_modes PROPERTIES
INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib"
)
endif(APPLE)
########################################################################
# Install built library files
########################################################################
install(TARGETS air_modes
LIBRARY DESTINATION lib${LIB_SUFFIX} # .so/.dylib file
ARCHIVE DESTINATION lib${LIB_SUFFIX} # .lib file
RUNTIME DESTINATION bin # .dll file
)
include(GrMiscUtils)
GR_LIBRARY_FOO(gnuradio-air_modes)
########################################################################
# Build and register unit test
# Print summary
########################################################################
#find_package(Boost COMPONENTS unit_test_framework)
#include(GrTest)
#set(GR_TEST_TARGET_DEPS gnuradio-gr-air-modes)
#turn each test cpp file into an executable with an int main() function
#add_definitions(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN)
#add_executable(qa_gr-air-modes_square_ff qa_gr-air-modes_square_ff.cc)
#target_link_libraries(qa_gr-air-modes_square_ff gnuradio-gr-air-modes ${Boost_LIBRARIES})
#GR_ADD_TEST(qa_gr-air-modes_square_ff qa_gr-air-modes_square_ff)
#add_executable(qa_gr-air-modes_square2_ff qa_gr-air-modes_square2_ff.cc)
#target_link_libraries(qa_gr-air-modes_square2_ff gnuradio-gr-air-modes ${Boost_LIBRARIES})
#GR_ADD_TEST(qa_gr-air-modes_square2_ff qa_gr-air-modes_square2_ff)
message(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}")
message(STATUS "Building for version: ${VERSION} / ${LIBVER}")

View File

@@ -1,89 +0,0 @@
/*
# 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.
#
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <air_modes_int_and_dump.h>
#include <gr_io_signature.h>
#include <string.h>
#include <iostream>
air_modes_int_and_dump_sptr air_make_modes_int_and_dump(int samples_per_symbol)
{
return air_modes_int_and_dump_sptr (new air_modes_int_and_dump(samples_per_symbol));
}
air_modes_int_and_dump::air_modes_int_and_dump(int samps_per_chip) :
gr_block ("modes_int_and_dump",
gr_make_io_signature (1, 1, sizeof(float)),
gr_make_io_signature (1, 1, sizeof(float)))
{
d_samples_per_symbol = samples_per_symbol;
set_output_multiple(d_samples_per_symbol);
set_history(d_samples_per_symbol);
d_acc = 0;
d_pos = 0;
}
int air_modes_int_and_dump::general_work(int noutput_items,
gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
const float *in = (const float *) input_items[0];
float *out = (float *) output_items[0];
int input_items = std::min(ninput_items[0], ninput_items[1]); //just in case
//ok first of all we look for "preamble_found" tags in our input range.
//get a vector of these tags, then every time we hit one
//reset the integrator position and accumulator
std::vector<pmt::pmt_t> tags;
uint64_t abs_sample_cnt = nitems_read(0);
get_tags_in_range(tags, 0, abs_sample_cnt, abs_sample_cnt + ninput_items, pmt::pmt_string_to_symbol("preamble_found"));
int out_items = 0;
int offset = gr_tags::get_nitems(&tags[0]) - abs_sample_cnt;
for(int i=0; i<120*2; i++) { //for each symbol in a potential long packet
out[out_items] = 0;
for(int j=0; j<d_samples_per_symbol; j++) { //for each sample in the symbol
out[out_items] += in[offset+i+j]; //integrate
}
out_items++;
}
//insert tag here
add_item_tag(0, //stream ID
nitems_written(0), //sample number
pmt::pmt_string_to_symbol("preamble_found");
pmt::PMT_T,
pmt::pmt_string_to_symbol(unique_id());
);
consume_each(wat);
return out_items;
}

View File

@@ -1,223 +0,0 @@
/*
# 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.
#
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <air_modes_preamble.h>
#include <gr_io_signature.h>
#include <string.h>
#include <iostream>
#include <gr_tags.h>
air_modes_preamble_sptr air_make_modes_preamble(int channel_rate, float threshold_db)
{
return air_modes_preamble_sptr (new air_modes_preamble(channel_rate, threshold_db));
}
air_modes_preamble::air_modes_preamble(int channel_rate, float threshold_db) :
gr_block ("modes_preamble",
gr_make_io_signature2 (2, 2, sizeof(float), sizeof(float)), //stream 0 is received data, stream 1 is moving average for reference
gr_make_io_signature (1, 1, sizeof(float))) //the output packets
{
d_chip_rate = 2000000; //2Mchips per second
d_samples_per_chip = channel_rate / d_chip_rate; //must be integer number of samples per chip to work
d_samples_per_symbol = d_samples_per_chip * 2;
d_check_width = 120 * d_samples_per_symbol; //only search to this far from the end of the stream buffer
d_threshold_db = threshold_db;
d_threshold = powf(10., threshold_db/20.); //the level that the sample must be above the moving average in order to qualify as a pulse
d_secs_per_sample = 1.0 / channel_rate;
set_output_multiple(1+d_check_width*2);
std::stringstream str;
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_samples_per_symbol);
}
static void integrate_and_dump(float *out, const float *in, int chips, int samps_per_chip) {
for(int i=0; i<chips; i++) {
float acc = 0;
for(int j=0; j<samps_per_chip; j++) {
acc += in[i*samps_per_chip+j];
}
out[i] = acc;
}
}
//the preamble pattern in bits
//fixme goes in .h
static const bool preamble_bits[] = {1, 0, 1, 0, 0, 0, 0, 1, 0, 1};
static double correlate_preamble(const float *in, int samples_per_chip) {
double corr = 0.0;
for(int i=0; i<10; i++) {
for(int j=0; j<samples_per_chip;j++)
if(preamble_bits[i]) corr += in[i*samples_per_chip+j];
}
return corr;
}
//takes an rx tag and issues a tag offset appropriately for the preamble
static const pmt::pmt_t offset_stamp(const gr_tag_t &tstamp, const uint64_t abs_sample_cnt, const double secs_per_sample) {
uint64_t ts_sample, last_whole_stamp;
double last_frac_stamp;
if(tstamp.key == NULL || pmt::pmt_symbol_to_string(tstamp.key) != "rx_time") {
last_whole_stamp = 0;
last_frac_stamp = 0;
ts_sample = 0;
} else {
last_whole_stamp = pmt::pmt_to_uint64(pmt::pmt_tuple_ref(tstamp.value, 0));
last_frac_stamp = pmt::pmt_to_double(pmt::pmt_tuple_ref(tstamp.value, 1));
ts_sample = tstamp.offset;
}
double fractime = double(abs_sample_cnt * secs_per_sample) + last_frac_stamp;
last_whole_stamp += int(fractime);
fractime -= int(fractime);
const pmt::pmt_t newval = pmt::pmt_make_tuple(
pmt::pmt_from_uint64(last_whole_stamp),
pmt::pmt_from_double(fractime)
);
return newval;
}
int air_modes_preamble::general_work(int noutput_items,
gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
const float *in = (const float *) input_items[0];
const float *inavg = (const float *) input_items[1];
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;
//fixme move into .h
const int pulse_offsets[4] = { 0,
int(2 * d_samples_per_chip),
int(7 * d_samples_per_chip),
int(9 * d_samples_per_chip)
};
uint64_t abs_sample_cnt = nitems_read(0);
std::vector<gr_tag_t> tstamp_tags;
get_tags_in_range(tstamp_tags, 0, abs_sample_cnt, abs_sample_cnt + ninputs, pmt::pmt_string_to_symbol("rx_time"));
//tags.back() is the most recent timestamp, then.
if(tstamp_tags.size() > 0) {
d_timestamp = tstamp_tags.back();
}
for(int i=0; i < ninputs; i++) {
float pulse_threshold = inavg[i] * d_threshold;
if(in[i] > pulse_threshold) { //hey we got a candidate
if(in[i+1] > in[i]) continue; //wait for the peak
//check to see the rest of the pulses are there
if( in[i+pulse_offsets[1]] < pulse_threshold ) continue;
if( in[i+pulse_offsets[2]] < pulse_threshold ) continue;
if( in[i+pulse_offsets[3]] < pulse_threshold ) continue;
//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++; how_late++; }
//if(early && i>0) { std::cout << "EARLY " << i << std::endl; i--; }
} 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
float avgpeak = ( in[i+pulse_offsets[0]]
+ in[i+pulse_offsets[1]]
+ in[i+pulse_offsets[2]]
+ in[i+pulse_offsets[3]]) / 4.0;
float space_threshold = inavg[i] + (avgpeak - inavg[i])/d_threshold;
bool valid_preamble = true; //f'in c++
for( int j=1.5*d_samples_per_symbol; j<=3*d_samples_per_symbol; j++)
if(in[i+j] > space_threshold) valid_preamble = false;
for( int j=5*d_samples_per_symbol; j<=7.5*d_samples_per_symbol; j++)
if(in[i+j] > space_threshold) valid_preamble = false;
if(!valid_preamble) continue;
//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;
}
//all right i'm prepared to call this a preamble
//let's integrate and dump the output
//FIXME: disable and use center sample
bool life_sucks = true;
if(life_sucks) {
for(int j=0; j<240; j++) {
out[j] = in[i+j*d_samples_per_chip];
}
} else {
i -= d_samples_per_chip-1;
integrate_and_dump(out, &in[i], 240, d_samples_per_chip);
}
const pmt::pmt_t new_pmt = offset_stamp(d_timestamp, abs_sample_cnt + i, d_secs_per_sample);
//now tag the preamble
add_item_tag(0, //stream ID
nitems_written(0), //sample
d_key, //frame_info
new_pmt,
d_me //block src id
);
//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 << "with i=" << i << ", returned 240" << std::endl;
consume_each(i+240*d_samples_per_chip);
return 240;
}
}
//didn't get anything this time
if(0) std::cout << "Preamble consumed " << ninputs << ", returned 0" << std::endl;
consume_each(ninputs);
return 0;
}

View File

@@ -1,190 +0,0 @@
/*
# 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.
#
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <air_modes_slicer.h>
#include <gr_io_signature.h>
#include <air_modes_types.h>
#include <sstream>
#include <iomanip>
#include <modes_crc.h>
#include <iostream>
#include <gr_tags.h>
extern "C"
{
#include <stdio.h>
#include <string.h>
}
air_modes_slicer_sptr air_make_modes_slicer(int channel_rate, gr_msg_queue_sptr queue)
{
return air_modes_slicer_sptr (new air_modes_slicer(channel_rate, queue));
}
air_modes_slicer::air_modes_slicer(int channel_rate, gr_msg_queue_sptr queue) :
gr_sync_block ("modes_slicer",
gr_make_io_signature (1, 1, sizeof(float)), //stream 0 is received data, stream 1 is binary preamble detector output
gr_make_io_signature (0, 0, 0) )
{
//initialize private data here
d_chip_rate = 2000000; //2Mchips per second
d_samples_per_chip = 2;//FIXME this is constant now channel_rate / d_chip_rate;
d_samples_per_symbol = d_samples_per_chip * 2;
d_check_width = 120 * d_samples_per_symbol; //how far you will have to look ahead
d_queue = queue;
set_output_multiple(d_check_width*2); //how do you specify buffer size for sinks?
}
//this slicer is courtesy of Lincoln Labs. supposedly it is more resistant to mode A/C FRUIT.
//see http://adsb.tc.faa.gov/WG3_Meetings/Meeting8/Squitter-Lon.pdf
static slice_result_t slicer(const float bit0, const float bit1, const float ref) {
slice_result_t result;
//3dB limits for bit slicing and confidence measurement
float highlimit=ref*1.414;
float lowlimit=ref*0.707;
bool firstchip_inref = ((bit0 > lowlimit) && (bit0 < highlimit));
bool secondchip_inref = ((bit1 > lowlimit) && (bit1 < highlimit));
if(firstchip_inref && !secondchip_inref) {
result.decision = 1;
result.confidence = 1;
}
else if(secondchip_inref && !firstchip_inref) {
result.decision = 0;
result.confidence = 1;
}
else if(firstchip_inref && secondchip_inref) {
result.decision = bit0 > bit1;
result.confidence = 0;
}
else {//if(!firstchip_inref && !secondchip_inref) {
result.decision = bit0 > bit1;
if(result.decision) {
if(bit1 < lowlimit * 0.5) result.confidence = 1;
else result.confidence = 0;
} else {
if(bit0 < lowlimit * 0.5) result.confidence = 1;
else result.confidence = 0;
}
}
return result;
}
int air_modes_slicer::work(int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
const float *in = (const float *) input_items[0];
int size = noutput_items - d_check_width; //since it's a sync block, i assume that it runs with ninput_items = noutput_items
if(0) std::cout << "Slicer called with " << size << " samples" << std::endl;
std::vector<gr_tag_t> tags;
uint64_t abs_sample_cnt = nitems_read(0);
get_tags_in_range(tags, 0, abs_sample_cnt, abs_sample_cnt + size, pmt::pmt_string_to_symbol("preamble_found"));
std::vector<gr_tag_t>::iterator tag_iter;
for(tag_iter = tags.begin(); tag_iter != tags.end(); tag_iter++) {
uint64_t i = tag_iter->offset - abs_sample_cnt;
memset(&d_data, 0x00, 14 * sizeof(unsigned char));
memset(&d_lowconfbits, 0x00, 24 * sizeof(unsigned char));
unsigned int numlowconf = 0;
//let's use the preamble to get a reference level for the packet
//fixme: a better thing to do is create a bi-level avg 1 and avg 0
//through simple statistics, then take the median for your slice level
//this won't improve decoding but will improve confidence
double reference_level = (in[i]
+ in[i+2]
+ in[i+7]
+ in[i+9]) / 4.0;
i += 16; //move on up to the first bit of the packet data
//now let's slice the header so we can determine if it's a short pkt or a long pkt
unsigned char pkt_hdr = 0;
for(int j=0; j < 5; j++) {
slice_result_t slice_result = slicer(in[i+j*2], in[i+j*2+1], reference_level);
if(slice_result.decision) pkt_hdr += 1 << (4-j);
}
framer_packet_type type;
if(pkt_hdr == 16 or pkt_hdr == 17 or pkt_hdr == 20 or pkt_hdr == 21) type = Long_Packet;
else type = Short_Packet;
int packet_length = (type == Short_Packet) ? 56 : 112;
//it's slice time!
//TODO: don't repeat your work here, you already have the first 5 bits
slice_result_t slice_result;
for(int j = 0; j < packet_length; j++) {
slice_result = slicer(in[i+j*2], in[i+j*2+1], reference_level);
//put the data into the packet
if(slice_result.decision) {
d_data[j/8] += 1 << (7-(j%8));
}
//put the confidence decision into the packet
if(slice_result.confidence) {
//d_confidence[j/8] += 1 << (7-(j%8));
} else {
if(numlowconf < 24) d_lowconfbits[numlowconf++] = j;
}
}
uint64_t timestamp_secs = pmt::pmt_to_uint64(pmt::pmt_tuple_ref(tag_iter->value, 0));
double timestamp_frac = pmt::pmt_to_double(pmt::pmt_tuple_ref(tag_iter->value, 1));
//here you might want to traverse the whole packet and if you find all 0's, just toss it. don't know why these packets turn up, but they pass ECC.
bool zeroes = 1;
for(int m = 0; m < 14; m++) {
if(d_data[m]) zeroes = 0;
}
if(zeroes) {continue;} //toss it
unsigned int message_type = (d_data[0] >> 3) & 0x1F; //get the message type to make decisions on ECC methods
if(type == Short_Packet && message_type != 11 && numlowconf > 0) {continue;}
if(message_type == 11 && numlowconf >= 10) {continue;}
unsigned long crc = modes_check_crc(d_data, packet_length);
//crc for packets that aren't type 11 or type 17 is encoded with the transponder ID, which we don't know
//we forward them if they have no low-confidence bits (see above), but this still lets some crap through.
if(crc && (message_type == 11 || message_type == 17)) {continue;}
std::ostringstream payload;
for(int m = 0; m < packet_length/8; m++) {
payload << std::hex << std::setw(2) << std::setfill('0') << unsigned(d_data[m]);
}
payload << " " << std::setw(6) << crc << " " << std::dec << reference_level
<< " " << timestamp_secs << " " << std::setprecision(20) << timestamp_frac;
gr_message_sptr msg = gr_make_message_from_string(std::string(payload.str()));
d_queue->handle(msg);
}
return size;
}

View File

@@ -1,165 +1,63 @@
/*
* Copyright 2007 Free Software Foundation, Inc.
* Copyright 2013 Nick Foster
*
* This file is part of GNU Radio
* This file is part of gr-air-modes
*
* GNU Radio is free software; you can redistribute it and/or modify
* 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 2, or (at your option)
* any later version.
*
* GNU Radio is distributed in the hope that it will be useful,
* 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 GNU Radio; see the file COPYING. If not, write to
* 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.
*/
//this is copied almost verbatim from Eric Cottrell's gr-air platform.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <air_modes_types.h>
#include <modes_crc.h>
#include <air_modes/types.h>
#include <air_modes/modes_crc.h>
#include <math.h>
#include <stdlib.h>
/* Mode S Parity Table
* Index is bit position with bit 0 being the first bit after preamble
* On short frames an offset of 56 is used.
*/
const unsigned int modes_crc_table[112] =
{
0x3935ea, // Start of Long Frame CRC
0x1c9af5,
0xf1b77e,
0x78dbbf,
0xc397db,
0x9e31e9,
0xb0e2f0,
0x587178,
0x2c38bc,
0x161c5e,
0x0b0e2f,
0xfa7d13,
0x82c48d,
0xbe9842,
0x5f4c21,
0xd05c14,
0x682e0a,
0x341705,
0xe5f186,
0x72f8c3,
0xc68665,
0x9cb936,
0x4e5c9b,
0xd8d449,
0x939020,
0x49c810,
0x24e408,
0x127204,
0x093902,
0x049c81,
0xfdb444,
0x7eda22,
0x3f6d11, // Extended 56 bit field
0xe04c8c,
0x702646,
0x381323,
0xe3f395,
0x8e03ce,
0x4701e7,
0xdc7af7,
0x91c77f,
0xb719bb,
0xa476d9,
0xadc168,
0x56e0b4,
0x2b705a,
0x15b82d,
0xf52612,
0x7a9309,
0xc2b380,
0x6159c0,
0x30ace0,
0x185670,
0x0c2b38,
0x06159c,
0x030ace,
0x018567,
0xff38b7, // Start of Short Frame CRC
0x80665f,
0xbfc92b,
0xa01e91,
0xaff54c,
0x57faa6,
0x2bfd53,
0xea04ad,
0x8af852,
0x457c29,
0xdd4410,
0x6ea208,
0x375104,
0x1ba882,
0x0dd441,
0xf91024,
0x7c8812,
0x3e4409,
0xe0d800,
0x706c00,
0x383600,
0x1c1b00,
0x0e0d80,
0x0706c0,
0x038360,
0x01c1b0,
0x00e0d8,
0x00706c,
0x003836,
0x001c1b,
0xfff409,
0x800000, // 24 PI or PA bits
0x400000,
0x200000,
0x100000,
0x080000,
0x040000,
0x020000,
0x010000,
0x008000,
0x004000,
0x002000,
0x001000,
0x000800,
0x000400,
0x000200,
0x000100,
0x000080,
0x000040,
0x000020,
0x000010,
0x000008,
0x000004,
0x000002,
0x000001,
};
unsigned int crc_table[256];
const unsigned int POLY=0xFFF409;
int modes_check_crc(unsigned char data[], int length)
//generate a bytewise lookup CRC table
void generate_crc_table(void)
{
int crc=0, i;
for(i = 0; i < length; i++)
{
if(data[i/8] & (1 << (7-(i%8))))
{
crc ^= modes_crc_table[i+(112-length)];
}
}
return crc;
unsigned int crc = 0;
for(int n=0; n<256; n++) {
crc = n<<16;
for(int k=0; k<8; k++) {
if(crc & 0x800000) {
crc = ((crc<<1) ^ POLY) & 0xFFFFFF;
} else {
crc = (crc<<1) & 0xFFFFFF;
}
}
crc_table[n] = crc & 0xFFFFFF;
}
}
//Perform a bytewise CRC check
unsigned int modes_check_crc(unsigned char data[], int length)
{
if(crc_table[1] != POLY) generate_crc_table();
unsigned int crc=0;
for(int i=0; i<length; i++) {
crc = crc_table[((crc>>16) ^ data[i]) & 0xff] ^ (crc << 8);
}
return crc & 0xFFFFFF;
}

253
lib/preamble_impl.cc Normal file
View File

@@ -0,0 +1,253 @@
/*
# Copyright 2010 Nick Foster
# Copyright 2013 Nicholas Corgan
#
# 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.
#
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <ciso646>
#include "preamble_impl.h"
#include <gnuradio/io_signature.h>
#include <string.h>
#include <iostream>
#include <gnuradio/tags.h>
namespace gr {
namespace air_modes {
using input_type = float;
using output_type = float;
preamble::sptr preamble::make(float channel_rate, float threshold_db) {
return gnuradio::make_block_sptr<preamble_impl>(channel_rate, threshold_db);
}
preamble_impl::preamble_impl(float channel_rate, float threshold_db)
: gr::block ("preamble",
gr::io_signature::make(2, 2, sizeof(input_type)),
gr::io_signature::make(1, 1, sizeof(output_type)))
{
d_chip_rate = 2000000; //2Mchips per second
set_rate(channel_rate);
set_threshold(threshold_db);
std::stringstream str;
str << name() << unique_id();
d_me = pmt::string_to_symbol(str.str());
d_key = pmt::string_to_symbol("preamble_found");
}
void air_modes::preamble_impl::set_rate(float channel_rate) {
d_samples_per_chip = channel_rate / d_chip_rate;
d_samples_per_symbol = d_samples_per_chip * 2;
d_check_width = 120 * d_samples_per_symbol;
d_sample_rate = channel_rate;
set_output_multiple(1+d_check_width*2);
set_history(d_samples_per_symbol);
}
void preamble_impl::set_threshold(float threshold_db) {
d_threshold_db = threshold_db;
d_threshold = powf(10., threshold_db/20.); //the level that the sample must be above the moving average in order to qualify as a pulse
}
float preamble_impl::get_threshold(void) {
return d_threshold_db;
}
float preamble_impl::get_rate(void) {
return d_sample_rate;
}
static void integrate_and_dump(float *out, const float *in, int chips, int samps_per_chip) {
for(int i=0; i<chips; i++) {
float acc = 0;
for(int j=0; j<samps_per_chip; j++) {
acc += in[i*samps_per_chip+j];
}
out[i] = acc;
}
}
//the preamble pattern in bits
//fixme goes in .h
static const bool preamble_bits[] = {1, 0, 1, 0, 0, 0, 0, 1, 0, 1};
static double correlate_preamble(const float *in, int samples_per_chip) {
double corr = 0.0;
for(int i=0; i<10; i++) {
for(int j=0; j<samples_per_chip;j++)
if(preamble_bits[i]) corr += in[i*samples_per_chip+j];
}
return corr;
}
static pmt::pmt_t tag_to_timestamp(gr::tag_t tstamp, uint64_t abs_sample_cnt, int rate) {
uint64_t last_whole_stamp;
double last_frac_stamp;
pmt::pmt_t tstime = pmt::make_tuple(pmt::from_uint64(0), pmt::from_double(0));
if(tstamp.key == NULL
|| !pmt::is_symbol(tstamp.key)
|| pmt::symbol_to_string(tstamp.key) != "rx_time") {
last_whole_stamp = 0;
last_frac_stamp = 0;
} else {
last_whole_stamp = pmt::to_uint64(pmt::tuple_ref(tstamp.value, 0));
last_frac_stamp = pmt::to_double(pmt::tuple_ref(tstamp.value, 1));
}
//the timestamp tag has tstamp.offset, the sample index of the timestamp tag
//also tstamp.value, a pmt pair with (uint64, double) representing int and
//fractional timestamp, respectively.
//this function also gets an abs_sample_cnt which represents the sample count to
//find a timestamp for. sps is obviously samples per second.
//
//so (abs_sample_cnt - tstamp.offset) is the delay we apply to the tag
// int((abs_sample_cnt - tstamp.offset)/sps) is the integer offset
// (abs_sample_cnt - tstamp.offset)/sps is the fractional offset
uint64_t int_offset = (abs_sample_cnt - tstamp.offset)/rate;
double frac_offset = ((abs_sample_cnt - tstamp.offset) % rate) / double(rate);
uint64_t abs_whole = last_whole_stamp + int_offset;
double abs_frac = last_frac_stamp + frac_offset;
if(abs_frac > 1.0f) {
abs_frac -= 1.0f;
abs_whole += 1;
}
tstime = pmt::make_tuple(pmt::from_uint64(abs_whole), pmt::from_double(abs_frac));
return tstime;
}
int preamble_impl::general_work(int noutput_items,
gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
const float *in = (const float *) input_items[0];
const float *inavg = (const float *) input_items[1];
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 % int(d_samples_per_chip)) - int(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;
//fixme move into .h
const int pulse_offsets[4] = { 0,
int(2 * d_samples_per_chip),
int(7 * d_samples_per_chip),
int(9 * d_samples_per_chip)
};
uint64_t abs_sample_cnt = nitems_read(0);
std::vector<gr::tag_t> tstamp_tags;
get_tags_in_range(tstamp_tags, 0, abs_sample_cnt, abs_sample_cnt + ninputs, pmt::string_to_symbol("rx_time"));
//tags.back() is the most recent timestamp, then.
if(tstamp_tags.size() > 0) {
d_timestamp = tstamp_tags.back();
}
for(int i=0; i < ninputs; i++) {
float pulse_threshold = inavg[i] * d_threshold;
if(in[i] > pulse_threshold) { //hey we got a candidate
if(in[i+1] > in[i]) continue; //wait for the peak
//check to see the rest of the pulses are there
if( in[i+pulse_offsets[1]] < pulse_threshold ) continue;
if( in[i+pulse_offsets[2]] < pulse_threshold ) continue;
if( in[i+pulse_offsets[3]] < pulse_threshold ) continue;
//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++; how_late++; }
//if(early && i>0) { std::cout << "EARLY " << i << std::endl; i--; }
} 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
float avgpeak = ( in[i+pulse_offsets[0]]
+ in[i+pulse_offsets[1]]
+ in[i+pulse_offsets[2]]
+ in[i+pulse_offsets[3]]) / 4.0;
float space_threshold = inavg[i] + (avgpeak - inavg[i])/d_threshold;
bool valid_preamble = true; //f'in c++
for( int j=1.5*d_samples_per_symbol; j<=3*d_samples_per_symbol; j++)
if(in[i+j] > space_threshold) valid_preamble = false;
for( int j=5*d_samples_per_symbol; j<=7.5*d_samples_per_symbol; j++)
if(in[i+j] > space_threshold) valid_preamble = false;
if(!valid_preamble) continue;
//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;
}
//all right i'm prepared to call this a preamble
for(int j=0; j<240; j++) {
out[j] = in[i+int(j*d_samples_per_chip)] - inavg[i];
}
//get the timestamp of the preamble
pmt::pmt_t tstamp = tag_to_timestamp(d_timestamp, abs_sample_cnt + i, d_sample_rate);
//now tag the preamble
add_item_tag(0, //stream ID
nitems_written(0), //sample
d_key, //frame_info
tstamp,
d_me //block src id
);
//produce only one output per work call -- TODO this should probably change
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;
}
}
//didn't get anything this time
if(0) std::cout << "Preamble consumed " << ninputs << ", returned 0" << std::endl;
consume_each(ninputs);
return 0;
}
} //namespace air_modes
} //namespace gr

43
lib/preamble_impl.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef _AIR_MODES_PREAMBLE_IMPL_H_
#define _AIR_MODES_PREAMBLE_IMPL_H_
#include <gnuradio/block.h>
#include <air_modes/api.h>
#include <air_modes/preamble.h>
namespace gr {
namespace air_modes {
class AIR_MODES_API preamble_impl : public preamble
{
private:
int d_check_width;
int d_chip_rate;
float d_preamble_length_us;
float d_samples_per_chip;
float d_samples_per_symbol;
float d_threshold_db;
float d_threshold;
gr::tag_t d_timestamp;
pmt::pmt_t d_me, d_key;
int d_sample_rate;
public:
preamble_impl(float channel_rate, float threshold_db);
int general_work (int noutput_items,
gr_vector_int &ninput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
void set_rate(float channel_rate);
void set_threshold(float threshold_db);
float get_threshold(void);
float get_rate(void);
};
} //namespace air_modes
} //namespace gr
#endif //_AIR_MODES_PREAMBLE_IMPL_H_

202
lib/slicer_impl.cc Normal file
View File

@@ -0,0 +1,202 @@
/*
# Copyright 2010 Nick Foster
# Copyright 2013 Nicholas Corgan
#
# 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.
#
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <ciso646>
#include "slicer_impl.h"
#include <gnuradio/io_signature.h>
#include <air_modes/types.h>
#include <sstream>
#include <iomanip>
#include <air_modes/modes_crc.h>
#include <iostream>
#include <gnuradio/tags.h>
extern "C"
{
#include <stdio.h>
#include <string.h>
}
namespace gr {
namespace air_modes {
slicer::sptr slicer::make(gr::msg_queue::sptr queue) {
return gnuradio::make_block_sptr<slicer_impl>(queue);
}
slicer_impl::slicer_impl(gr::msg_queue::sptr queue) :
gr::sync_block ("slicer",
gr::io_signature::make(1, 1, sizeof(float)),
gr::io_signature::make(0, 0, 0))
{
//initialize private data here
d_chip_rate = 2000000; //2Mchips per second
d_samples_per_chip = 2;//FIXME this is constant now channel_rate / d_chip_rate;
d_samples_per_symbol = d_samples_per_chip * 2;
d_check_width = 120 * d_samples_per_symbol; //how far you will have to look ahead
d_queue = queue;
set_output_multiple(d_check_width*2); //how do you specify buffer size for sinks?
}
//this slicer is courtesy of Lincoln Labs. supposedly it is more resistant to mode A/C FRUIT.
//see http://adsb.tc.faa.gov/WG3_Meetings/Meeting8/Squitter-Lon.pdf
static slice_result_t llslicer(const float bit0, const float bit1, const float ref) {
slice_result_t result;
//3dB limits for bit slicing and confidence measurement
float highlimit=ref*1.414;
float lowlimit=ref*0.707;
bool firstchip_inref = ((bit0 > lowlimit) && (bit0 < highlimit));
bool secondchip_inref = ((bit1 > lowlimit) && (bit1 < highlimit));
if(firstchip_inref && !secondchip_inref) {
result.decision = 1;
result.confidence = 1;
}
else if(secondchip_inref && !firstchip_inref) {
result.decision = 0;
result.confidence = 1;
}
else if(firstchip_inref && secondchip_inref) {
result.decision = bit0 > bit1;
result.confidence = 0;
}
else {//if(!firstchip_inref && !secondchip_inref) {
result.decision = bit0 > bit1;
if(result.decision) {
if(bit1 < lowlimit * 0.5) result.confidence = 1;
else result.confidence = 0;
} else {
if(bit0 < lowlimit * 0.5) result.confidence = 1;
else result.confidence = 0;
}
}
return result;
}
int slicer_impl::work(int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
const float *in = (const float *) input_items[0];
int size = noutput_items - d_check_width; //since it's a sync block, i assume that it runs with ninput_items = noutput_items
if(0) std::cout << "Slicer called with " << size << " samples" << std::endl;
std::vector<gr::tag_t> tags;
uint64_t abs_sample_cnt = nitems_read(0);
get_tags_in_range(tags, 0, abs_sample_cnt, abs_sample_cnt + size, pmt::string_to_symbol("preamble_found"));
std::vector<gr::tag_t>::iterator tag_iter;
for(tag_iter = tags.begin(); tag_iter != tags.end(); tag_iter++) {
uint64_t i = tag_iter->offset - abs_sample_cnt;
modes_packet rx_packet;
memset(&rx_packet.data, 0x00, 14 * sizeof(unsigned char));
memset(&rx_packet.lowconfbits, 0x00, 24 * sizeof(unsigned char));
rx_packet.numlowconf = 0;
//let's use the preamble to get a reference level for the packet
//fixme: a better thing to do is create a bi-level avg 1 and avg 0
//through simple statistics, then take the median for your slice level
//this won't improve decoding but will improve confidence
rx_packet.reference_level = (in[i]
+ in[i+2]
+ in[i+7]
+ in[i+9]) / 4.0;
i += 16; //move on up to the first bit of the packet data
//now let's slice the header so we can determine if it's a short pkt or a long pkt
unsigned char pkt_hdr = 0;
for(int j=0; j < 5; j++) {
slice_result_t slice_result = llslicer(in[i+j*2], in[i+j*2+1], rx_packet.reference_level);
if(slice_result.decision) pkt_hdr += 1 << (4-j);
}
if(pkt_hdr == 16 or pkt_hdr == 17 or pkt_hdr == 20 or pkt_hdr == 21) rx_packet.type = Long_Packet;
else rx_packet.type = Short_Packet;
int packet_length = (rx_packet.type == framer_packet_type(Short_Packet)) ? 56 : 112;
//it's slice time!
//TODO: don't repeat your work here, you already have the first 5 bits
for(int j = 0; j < packet_length; j++) {
slice_result_t slice_result = llslicer(in[i+j*2], in[i+j*2+1], rx_packet.reference_level);
//put the data into the packet
if(slice_result.decision) {
rx_packet.data[j/8] += 1 << (7-(j%8));
}
//put the confidence decision into the packet
if(slice_result.confidence) {
//rx_packet.confidence[j/8] += 1 << (7-(j%8));
} else {
if(rx_packet.numlowconf < 24) rx_packet.lowconfbits[rx_packet.numlowconf++] = j;
}
}
//here you might want to traverse the whole packet and if you find all 0's, just toss it. don't know why these packets turn up, but they pass ECC.
bool zeroes = 1;
for(int m = 0; m < 14; m++) {
if(rx_packet.data[m]) zeroes = 0;
}
if(zeroes) {continue;} //toss it
rx_packet.message_type = (rx_packet.data[0] >> 3) & 0x1F; //get the message type to make decisions on ECC methods
if(rx_packet.type == Short_Packet && rx_packet.message_type != 11 && rx_packet.numlowconf > 0) {continue;}
if(rx_packet.message_type == 11 && rx_packet.numlowconf >= 10) {continue;}
rx_packet.crc = modes_check_crc(rx_packet.data, (packet_length/8)-3);
unsigned int ap = rx_packet.data[packet_length/8-3] << 16
| rx_packet.data[packet_length/8-2] << 8
| rx_packet.data[packet_length/8-1] << 0;
rx_packet.crc ^= ap;
//crc for packets that aren't type 11 or type 17 is encoded with the transponder ID, which we don't know
//therefore we toss 'em if there's syndrome
//crc for the other short packets is usually nonzero, so they can't really be trusted that far
if(rx_packet.crc && (rx_packet.message_type == 11 || rx_packet.message_type == 17)) {continue;}
pmt::pmt_t tstamp = tag_iter->value;
d_payload.str("");
for(int m = 0; m < packet_length/8; m++) {
d_payload << std::hex << std::setw(2) << std::setfill('0') << unsigned(rx_packet.data[m]);
}
d_payload << " " << std::setw(6) << rx_packet.crc << " " << std::dec << rx_packet.reference_level
<< " " << pmt::to_uint64(pmt::tuple_ref(tstamp, 0)) << " " << std::setprecision(10) << pmt::to_double(pmt::tuple_ref(tstamp, 1));
gr::message::sptr msg = gr::message::make_from_string(std::string(d_payload.str()));
d_queue->handle(msg);
}
if(0) std::cout << "Slicer consumed " << size << ", returned " << size << std::endl;
return size;
}
} //namespace air_modes
} //namespace gr

View File

@@ -1,5 +1,5 @@
/*
# Copyright 2010 Nick Foster
# Copyright 2013 Nick Foster
#
# This file is part of gr-air-modes
#
@@ -20,40 +20,37 @@
#
*/
#ifndef INCLUDED_AIR_MODES_slicer_H
#define INCLUDED_AIR_MODES_slicer_H
#ifndef INCLUDED_AIR_MODES_SLICER_IMPL_H
#define INCLUDED_AIR_MODES_SLICER_IMPL_H
#include <gr_sync_block.h>
#include <gr_msg_queue.h>
#include <air_modes_api.h>
#include <gnuradio/sync_block.h>
#include <gnuradio/msg_queue.h>
#include <air_modes/api.h>
#include <air_modes/slicer.h>
class air_modes_slicer;
typedef boost::shared_ptr<air_modes_slicer> air_modes_slicer_sptr;
namespace gr {
namespace air_modes {
AIR_MODES_API air_modes_slicer_sptr air_make_modes_slicer(int channel_rate, gr_msg_queue_sptr queue);
/*!
* \brief mode select slicer detection
* \ingroup block
*/
class AIR_MODES_API air_modes_slicer : public gr_sync_block
class AIR_MODES_API slicer_impl : public slicer
{
private:
friend air_modes_slicer_sptr air_make_modes_slicer(int channel_rate, gr_msg_queue_sptr queue);
air_modes_slicer(int channel_rate, gr_msg_queue_sptr queue);
int d_check_width;
int d_chip_rate;
int d_samples_per_chip;
int d_samples_per_symbol;
gr_msg_queue_sptr d_queue;
unsigned char d_lowconfbits[24];
unsigned char d_data[14];
gr::tag_t d_timestamp;
gr::msg_queue::sptr d_queue;
std::ostringstream d_payload;
public:
slicer_impl(gr::msg_queue::sptr queue);
int work (int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items);
};
#endif /* INCLUDED_AIR_MODES_slicer_H */
} //namespace air_modes
} //namespace gr
#endif /* INCLUDED_AIR_MODES_SLICER_IMPL_H */

View File

@@ -34,28 +34,24 @@ GR_PYTHON_INSTALL(
altitude.py
az_map.py
cpr.py
html_template.py
mlat.py
mlat_client.py
exceptions.py
flightgear.py
gui_model.py
kml.py
modes_types.py
parse.py
msprint.py
radio.py
raw_server.py
rx_path.py
sbs1.py
sql.py
types.py
zmq_socket.py
Quaternion.py
msgq_runner.py
DESTINATION ${GR_PYTHON_DIR}/air_modes
)
########################################################################
# Handle the unit tests
########################################################################
#include(GrTest)
#set(GR_TEST_TARGET_DEPS gnuradio-gr-air-modes)
#set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig)
#GR_ADD_TEST(qa_gr-air-modes ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_gr-air-modes.py)
add_subdirectory(bindings)

View File

@@ -21,57 +21,43 @@
'''
This is the GNU Radio gr-air-modes package. It provides a library and
application for receiving Mode S / ADS-B signals from aircraft. Use
uhd_modes.py as the main application for receiving signals. cpr.py
modes_rx as the main application for receiving signals. cpr.py
provides an implementation of Compact Position Reporting. altitude.py
implements Gray-coded altitude decoding. Various plugins exist for SQL,
KML, and PlanePlotter-compliant SBS-1 emulation output. mlat.py provides
an experimental implementation of a multilateration solver.
'''
# ----------------------------------------------------------------
# Temporary workaround for ticket:181 (swig+python problem)
import sys
_RTLD_GLOBAL = 0
try:
from dl import RTLD_GLOBAL as _RTLD_GLOBAL
except ImportError:
try:
from DLFCN import RTLD_GLOBAL as _RTLD_GLOBAL
except ImportError:
pass
if _RTLD_GLOBAL != 0:
_dlopenflags = sys.getdlopenflags()
sys.setdlopenflags(_dlopenflags|_RTLD_GLOBAL)
# ----------------------------------------------------------------
# import swig generated symbols into the gr-air-modes namespace
from air_modes_swig import *
from .air_modes_python import *
# import any pure python here
#
from rx_path import rx_path
from parse import parse,modes_reply
from msprint import output_print
from sql import output_sql
from sbs1 import output_sbs1
from kml import output_kml
from raw_server import raw_server
from mlat_client import mlat_client
from exceptions import *
from az_map import *
from types import *
try:
import zmq
except ImportError:
raise RuntimeError("PyZMQ not found! Please install libzmq and PyZMQ to run gr-air-modes")
from .rx_path import rx_path
from .zmq_socket import zmq_pubsub_iface
from .parse import *
from .msprint import output_print
from .sql import output_sql
from .sbs1 import output_sbs1
from .kml import output_kml, output_jsonp
from .raw_server import raw_server
from .radio import modes_radio
from .exceptions import *
from .modes_types import *
from .altitude import *
from .cpr import cpr_decoder
from .html_template import html_template
from .msgq_runner import msgq_runner
#this is try/excepted in case the user doesn't have numpy installed
try:
from flightgear import output_flightgear
from Quaternion import *
from .flightgear import output_flightgear
from .Quaternion import *
except ImportError:
print "gr-air-modes warning: numpy+scipy not installed, FlightGear interface not supported"
print("gr-air-modes warning: numpy+scipy not installed, FlightGear interface not supported")
pass
# ----------------------------------------------------------------
# Tail of workaround
if _RTLD_GLOBAL != 0:
sys.setdlopenflags(_dlopenflags) # Restore original flags
# ----------------------------------------------------------------

189
python/altitude.py Executable file → Normal file
View File

@@ -26,120 +26,119 @@
from air_modes.exceptions import *
def decode_alt(alt, bit13):
mbit = alt & 0x0040
qbit = alt & 0x0010
if mbit and bit13:
#nobody uses metric altitude: AFAIK, it's an orphaned part of
#the spec. haven't seen it in three years. as a result, replies
#with mbit set can be considered spurious, and so we discard them here.
#bits 20-25, 27-31 encode alt in meters
#remember that bits are LSB (bit 20 is MSB)
#meters_alt = 0
#for (shift, bit) in enumerate(range(31,26,-1)+range(25,19,-1)):
# meters_alt += ((alt & (1<<bit)) != 0) << shift
#decoded_alt = meters_alt / 0.3048
raise MetricAltError
mbit = alt & 0x0040
qbit = alt & 0x0010
if mbit and bit13:
#nobody uses metric altitude: AFAIK, it's an orphaned part of
#the spec. haven't seen it in three years. as a result, replies
#with mbit set can be considered spurious, and so we discard them here.
#bits 20-25, 27-31 encode alt in meters
#remember that bits are LSB (bit 20 is MSB)
#meters_alt = 0
#for (shift, bit) in enumerate(range(31,26,-1)+range(25,19,-1)):
# meters_alt += ((alt & (1<<bit)) != 0) << shift
#decoded_alt = meters_alt / 0.3048
raise MetricAltError
if qbit: #a mode S-style reply
#bit13 is false for BDS0,5 ADS-B squitters, and is true otherwise
if bit13:
#in this representation, the altitude bits are as follows:
# 12 11 10 9 8 7 (6) 5 (4) 3 2 1 0
# so bits 6 and 4 are the M and Q bits, respectively.
tmp1 = (alt & 0x3F80) >> 2
tmp2 = (alt & 0x0020) >> 1
else:
tmp1 = (alt & 0x1FE0) >> 1
tmp2 = 0
if qbit: #a mode S-style reply
#bit13 is false for BDS0,5 ADS-B squitters, and is true otherwise
if bit13:
#in this representation, the altitude bits are as follows:
# 12 11 10 9 8 7 (6) 5 (4) 3 2 1 0
# so bits 6 and 4 are the M and Q bits, respectively.
tmp1 = (alt & 0x3F80) >> 2
tmp2 = (alt & 0x0020) >> 1
else:
tmp1 = (alt & 0x1FE0) >> 1
tmp2 = 0
decoded_alt = ((alt & 0x0F) | tmp1 | tmp2) * 25 - 1000
decoded_alt = ((alt & 0x0F) | tmp1 | tmp2) * 25 - 1000
else: #a mode C-style reply
#okay, the order they come in is:
#C1 A1 C2 A2 C4 A4 X B1 D1 B2 D2 B4 D4
#the order we want them in is:
#D2 D4 A1 A2 A4 B1 B2 B4
#so we'll reassemble into a Gray-coded representation
else: #a mode C-style reply
#okay, the order they come in is:
#C1 A1 C2 A2 C4 A4 X B1 D1 B2 D2 B4 D4
#the order we want them in is:
#D2 D4 A1 A2 A4 B1 B2 B4
#so we'll reassemble into a Gray-coded representation
if bit13 is False:
alt = (alt & 0x003F) | (alt & 0x0FC0 << 1)
if bit13 is False:
alt = (alt & 0x003F) | (alt & 0x0FC0 << 1)
C1 = 0x1000
A1 = 0x0800
C2 = 0x0400
A2 = 0x0200 #this represents the order in which the bits come
C4 = 0x0100
A4 = 0x0080
B1 = 0x0020
D1 = 0x0010
B2 = 0x0008
D2 = 0x0004
B4 = 0x0002
D4 = 0x0001
C1 = 0x1000
A1 = 0x0800
C2 = 0x0400
A2 = 0x0200 #this represents the order in which the bits come
C4 = 0x0100
A4 = 0x0080
B1 = 0x0020
D1 = 0x0010
B2 = 0x0008
D2 = 0x0004
B4 = 0x0002
D4 = 0x0001
bigpart = ((alt & B4) >> 1) \
+ ((alt & B2) >> 2) \
+ ((alt & B1) >> 3) \
+ ((alt & A4) >> 4) \
+ ((alt & A2) >> 5) \
+ ((alt & A1) >> 6) \
+ ((alt & D4) << 6) \
+ ((alt & D2) << 5)
bigpart = ((alt & B4) >> 1) \
+ ((alt & B2) >> 2) \
+ ((alt & B1) >> 3) \
+ ((alt & A4) >> 4) \
+ ((alt & A2) >> 5) \
+ ((alt & A1) >> 6) \
+ ((alt & D4) << 6) \
+ ((alt & D2) << 5)
#bigpart is now the 500-foot-resolution Gray-coded binary part
decoded_alt = gray2bin(bigpart)
#real_alt is now the 500-foot-per-tick altitude
#bigpart is now the 500-foot-resolution Gray-coded binary part
decoded_alt = gray2bin(bigpart)
#real_alt is now the 500-foot-per-tick altitude
cbits = ((alt & C4) >> 8) + ((alt & C2) >> 9) + ((alt & C1) >> 10)
cval = gray2bin(cbits) #turn them into a real number
cbits = ((alt & C4) >> 8) + ((alt & C2) >> 9) + ((alt & C1) >> 10)
cval = gray2bin(cbits) #turn them into a real number
if cval == 7:
cval = 5 #not a real gray code after all
if cval == 7:
cval = 5 #not a real gray code after all
if decoded_alt % 2:
cval = 6 - cval #since the code is symmetric this unwraps it to see whether to subtract the C bits or add them
if decoded_alt % 2:
cval = 6 - cval #since the code is symmetric this unwraps it to see whether to subtract the C bits or add them
decoded_alt *= 500 #take care of the A,B,D data
decoded_alt += cval * 100 #factor in the C data
decoded_alt -= 1300 #subtract the offset
decoded_alt *= 500 #take care of the A,B,D data
decoded_alt += cval * 100 #factor in the C data
decoded_alt -= 1300 #subtract the offset
return decoded_alt
return decoded_alt
def gray2bin(gray):
i = gray >> 1
i = gray >> 1
while i != 0:
gray ^= i
i >>= 1
while i != 0:
gray ^= i
i >>= 1
return gray
return gray
def encode_alt_modes(alt, bit13):
mbit = False
qbit = True
encalt = (int(alt) + 1000) / 25
mbit = False
qbit = True
encalt = (int(alt) + 1000) / 25
if bit13 is True:
tmp1 = (encalt & 0xfe0) << 2
tmp2 = (encalt & 0x010) << 1
else:
tmp1 = (encalt & 0xff8) << 1
tmp2 = 0
if bit13 is True:
tmp1 = (encalt & 0xfe0) << 2
tmp2 = (encalt & 0x010) << 1
else:
tmp1 = (encalt & 0xff8) << 1
tmp2 = 0
return (encalt & 0x0F) | tmp1 | tmp2 | (mbit << 6) | (qbit << 4)
return (encalt & 0x0F) | tmp1 | tmp2 | (mbit << 6) | (qbit << 4)
if __name__ == "__main__":
try:
for alt in range(-1000, 101400, 25):
dec = decode_alt(encode_alt_modes(alt, False), False)
if dec != alt:
print "Failure at %i with bit13 clear (got %s)" % (alt, dec)
for alt in range(-1000, 101400, 25):
dec = decode_alt(encode_alt_modes(alt, True), True)
if dec != alt:
print "Failure at %i with bit13 set (got %s)" % (alt, dec)
except MetricAltError:
print "Failure at %i due to metric alt bit" % alt
try:
for alt in range(-1000, 101400, 25):
dec = decode_alt(encode_alt_modes(alt, False), False)
if dec != alt:
print("Failure at %i with bit13 clear (got %s)" % (alt, dec))
for alt in range(-1000, 101400, 25):
dec = decode_alt(encode_alt_modes(alt, True), True)
if dec != alt:
print("Failure at %i with bit13 set (got %s)" % (alt, dec))
except MetricAltError:
print("Failure at %i due to metric alt bit" % alt)

76
python/az_map.py Executable file → Normal file
View File

@@ -25,8 +25,8 @@
from PyQt4 import QtCore, QtGui
import threading
import math
import air_modes
from air_modes.exceptions import *
import numpy as np
# model has max range vs. azimuth in n-degree increments
# contains separate max range for a variety of altitudes so
@@ -52,7 +52,7 @@ class az_map_model(QtCore.QObject):
def data(self, row, col):
return self._data[row][col]
def addRecord(self, bearing, altitude, distance):
with self.lock:
#round up to nearest altitude in altitudes list
@@ -84,18 +84,15 @@ class az_map_model(QtCore.QObject):
# the azimuth map widget
class az_map(QtGui.QWidget):
maxrange = 450
ringsize = 100
maxrange = 200
bgcolor = QtCore.Qt.black
ringpen = QtGui.QPen(QtGui.QColor(0, 96, 127, 255), 1.3)
#rangepen = QtGui.QPen(QtGui.QColor(255, 255, 0, 255), 1.0)
def __init__(self, parent=None):
super(az_map, self).__init__(parent)
self._model = None
self._paths = []
self.maxrange = az_map.maxrange
self.ringsize = az_map.ringsize
def minimumSizeHint(self):
return QtCore.QSize(50, 50)
@@ -117,7 +114,7 @@ class az_map(QtGui.QWidget):
#set background
painter.fillRect(event.rect(), QtGui.QBrush(az_map.bgcolor))
#draw the range rings
self.drawRangeRings(painter)
for i in range(len(self._paths)):
@@ -135,12 +132,12 @@ class az_map(QtGui.QWidget):
bearing = (i+0.5) * 360./az_map_model.npoints
distance = self._model._data[i][alt]
radius = min(self.width(), self.height()) / 2.0
scale = radius * distance / self.maxrange
scale = radius * distance / self.get_range()
#convert bearing,distance to x,y
xpts = scale * math.sin(bearing * math.pi / 180)
ypts = scale * math.cos(bearing * math.pi / 180)
#get the bounding rectangle of the arc
arcrect = QtCore.QRectF(QtCore.QPointF(0-scale, 0-scale),
QtCore.QPointF(scale, scale))
@@ -152,46 +149,57 @@ class az_map(QtGui.QWidget):
self._paths.append(path)
#this is just to add a little buffer space for showing the ring & range
def get_range(self):
return int(self.maxrange * 1.1)
def drawRangeRings(self, painter):
painter.translate(self.width()/2, self.height()/2)
painter.setPen(az_map.ringpen)
for i in range(0, self.maxrange, self.ringsize):
diameter = (float(i) / az_map.maxrange) * min(self.width(), self.height())
#choose intelligent range step -- keep it between 3-5 rings
rangestep = 100
while self.get_range() / rangestep < 3:
rangestep /= 2.0
for i in np.arange(rangestep, self.get_range(), rangestep):
diameter = (float(i) / self.get_range()) * min(self.width(), self.height())
painter.setPen(az_map.ringpen)
painter.drawEllipse(QtCore.QRectF(-diameter / 2.0,
-diameter / 2.0, diameter, diameter))
painter.setPen(QtGui.QColor(255,127,0,255))
painter.drawText(0-70/2.0, diameter/2.0, 70, 30, QtCore.Qt.AlignHCenter,
"%.1fnm" % i)
def setMaxRange(self, maxrange):
maxrange = max(3.25, maxrange)
maxrange = min(500., maxrange)
self.maxrange = maxrange
self.drawPath()
self.repaint()
def setRingSize(self, ringsize):
self.ringsize = ringsize
self.drawPath()
def wheelEvent(self, event):
self.setMaxRange(self.maxrange + (event.delta()/120.)*self.maxrange/4.)
class az_map_output(air_modes.parse):
def __init__(self, mypos, model):
air_modes.parse.__init__(self, mypos)
class az_map_output:
def __init__(self, cprdec, model, pub):
self._cpr = cprdec
self.model = model
pub.subscribe("type17_dl", self.output)
def output(self, msg):
[data, ecc, reference, timestamp] = msg.split()
data = air_modes.modes_reply(long(data, 16))
ecc = long(ecc, 16)
rssi = 10.*math.log10(float(reference))
msgtype = data["df"]
now = time.time()
if msgtype == 17:
icao = data["aa"]
subtype = data["ftc"]
try:
now = time.time()
icao = msg.data["aa"]
subtype = msg.data["ftc"]
distance, altitude, bearing = [0,0,0]
if 5 <= subtype <= 8:
(ground_track, decoded_lat, decoded_lon, distance, bearing) = self.parseBDS06(data)
(ground_track, decoded_lat, decoded_lon, distance, bearing) = air_modes.parseBDS06(msg.data, self._cpr)
altitude = 0
elif 9 <= subtype <= 18:
(altitude, decoded_lat, decoded_lon, distance, bearing) = self.parseBDS05(data)
(altitude, decoded_lat, decoded_lon, distance, bearing) = air_modes.parseBDS05(msg.data, self._cpr)
self.model.addRecord(bearing, altitude, distance)
except ADSBError:
pass
##############################

View File

@@ -0,0 +1,41 @@
# Copyright 2020 Free Software Foundation, Inc.
#
# This file is part of GNU Radio
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
########################################################################
# Check if there is C++ code at all
########################################################################
if(NOT air_modes_sources)
MESSAGE(STATUS "No C++ sources... skipping python bindings")
return()
endif(NOT air_modes_sources)
########################################################################
# Check for pygccxml
########################################################################
GR_PYTHON_CHECK_MODULE_RAW(
"pygccxml"
"import pygccxml"
PYGCCXML_FOUND
)
include(GrPybind)
########################################################################
# Python Bindings
########################################################################
list(APPEND air_modes_python_files
python_bindings.cc
preamble_python.cc
slicer_python.cc)
GR_PYBIND_MAKE_OOT(air_modes
../..
gr::air_modes
"${air_modes_python_files}")
install(TARGETS air_modes_python DESTINATION ${GR_PYTHON_DIR}/air_modes COMPONENT pythonapi)

View File

View File

@@ -0,0 +1,57 @@
import warnings
import argparse
import os
from gnuradio.bindtool import BindingGenerator
import pathlib
import sys
parser = argparse.ArgumentParser(description='Bind a GR Out of Tree Block')
parser.add_argument('--module', type=str,
help='Name of gr module containing file to bind (e.g. fft digital analog)')
parser.add_argument('--output_dir', default='/tmp',
help='Output directory of generated bindings')
parser.add_argument('--prefix', help='Prefix of Installed GNU Radio')
parser.add_argument('--src', help='Directory of gnuradio source tree',
default=os.path.dirname(os.path.abspath(__file__))+'/../../..')
parser.add_argument(
'--filename', help="File to be parsed")
parser.add_argument(
'--defines', help='Set additional defines for precompiler',default=(), nargs='*')
parser.add_argument(
'--include', help='Additional Include Dirs, separated', default=(), nargs='*')
parser.add_argument(
'--status', help='Location of output file for general status (used during cmake)', default=None
)
parser.add_argument(
'--flag_automatic', default='0'
)
parser.add_argument(
'--flag_pygccxml', default='0'
)
args = parser.parse_args()
prefix = args.prefix
output_dir = args.output_dir
defines = tuple(','.join(args.defines).split(','))
includes = ','.join(args.include)
name = args.module
namespace = ['gr', name]
prefix_include_root = name
with warnings.catch_warnings():
warnings.filterwarnings("ignore", category=DeprecationWarning)
bg = BindingGenerator(prefix, namespace,
prefix_include_root, output_dir, define_symbols=defines, addl_includes=includes,
catch_exceptions=False, write_json_output=False, status_output=args.status,
flag_automatic=True if args.flag_automatic.lower() in [
'1', 'true'] else False,
flag_pygccxml=True if args.flag_pygccxml.lower() in ['1', 'true'] else False)
bg.gen_file_binding(args.filename)

View File

@@ -0,0 +1 @@
This directory stores templates for docstrings that are scraped from the include header files for each block

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2021 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include "pydoc_macros.h"
#define D(...) DOC(gr,air_modes, __VA_ARGS__ )
/*
This file contains placeholders for docstrings for the Python bindings.
Do not edit! These were automatically extracted during the binding process
and will be overwritten during the build process
*/
static const char *__doc_gr_air_modes_preamble = R"doc()doc";
static const char *__doc_gr_air_modes_preamble_preamble_0 = R"doc()doc";
static const char *__doc_gr_air_modes_preamble_preamble_1 = R"doc()doc";
static const char *__doc_gr_air_modes_preamble_make = R"doc()doc";
static const char *__doc_gr_air_modes_preamble_set_rate = R"doc()doc";
static const char *__doc_gr_air_modes_preamble_set_threshold = R"doc()doc";
static const char *__doc_gr_air_modes_preamble_get_rate = R"doc()doc";
static const char *__doc_gr_air_modes_preamble_get_threshold = R"doc()doc";

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2021 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include "pydoc_macros.h"
#define D(...) DOC(gr,air_modes, __VA_ARGS__ )
/*
This file contains placeholders for docstrings for the Python bindings.
Do not edit! These were automatically extracted during the binding process
and will be overwritten during the build process
*/
static const char *__doc_gr_air_modes_slicer = R"doc()doc";
static const char *__doc_gr_air_modes_slicer_slicer = R"doc()doc";
static const char *__doc_gr_air_modes_slicer_make = R"doc()doc";

View File

@@ -0,0 +1,78 @@
# Utilities for reading values in header files
from argparse import ArgumentParser
import re
class PybindHeaderParser:
def __init__(self, pathname):
with open(pathname,'r') as f:
self.file_txt = f.read()
def get_flag_automatic(self):
# p = re.compile(r'BINDTOOL_GEN_AUTOMATIC\(([^\s])\)')
# m = p.search(self.file_txt)
m = re.search(r'BINDTOOL_GEN_AUTOMATIC\(([^\s])\)', self.file_txt)
if (m and m.group(1) == '1'):
return True
else:
return False
def get_flag_pygccxml(self):
# p = re.compile(r'BINDTOOL_USE_PYGCCXML\(([^\s])\)')
# m = p.search(self.file_txt)
m = re.search(r'BINDTOOL_USE_PYGCCXML\(([^\s])\)', self.file_txt)
if (m and m.group(1) == '1'):
return True
else:
return False
def get_header_filename(self):
# p = re.compile(r'BINDTOOL_HEADER_FILE\(([^\s]*)\)')
# m = p.search(self.file_txt)
m = re.search(r'BINDTOOL_HEADER_FILE\(([^\s]*)\)', self.file_txt)
if (m):
return m.group(1)
else:
return None
def get_header_file_hash(self):
# p = re.compile(r'BINDTOOL_HEADER_FILE_HASH\(([^\s]*)\)')
# m = p.search(self.file_txt)
m = re.search(r'BINDTOOL_HEADER_FILE_HASH\(([^\s]*)\)', self.file_txt)
if (m):
return m.group(1)
else:
return None
def get_flags(self):
return f'{self.get_flag_automatic()};{self.get_flag_pygccxml()};{self.get_header_filename()};{self.get_header_file_hash()};'
def argParse():
"""Parses commandline args."""
desc='Reads the parameters from the comment block in the pybind files'
parser = ArgumentParser(description=desc)
parser.add_argument("function", help="Operation to perform on comment block of pybind file", choices=["flag_auto","flag_pygccxml","header_filename","header_file_hash","all"])
parser.add_argument("pathname", help="Pathname of pybind c++ file to read, e.g. blockname_python.cc")
return parser.parse_args()
if __name__ == "__main__":
# Parse command line options and set up doxyxml.
args = argParse()
pbhp = PybindHeaderParser(args.pathname)
if args.function == "flag_auto":
print(pbhp.get_flag_automatic())
elif args.function == "flag_pygccxml":
print(pbhp.get_flag_pygccxml())
elif args.function == "header_filename":
print(pbhp.get_header_filename())
elif args.function == "header_file_hash":
print(pbhp.get_header_file_hash())
elif args.function == "all":
print(pbhp.get_flags())

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2021 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
/***********************************************************************************/
/* This file is automatically generated using bindtool and can be manually edited */
/* The following lines can be configured to regenerate this file during cmake */
/* If manual edits are made, the following tags should be modified accordingly. */
/* BINDTOOL_GEN_AUTOMATIC(0) */
/* BINDTOOL_USE_PYGCCXML(0) */
/* BINDTOOL_HEADER_FILE(preamble.h) */
/* BINDTOOL_HEADER_FILE_HASH(7ce2f89dad5c95a56a123d11871bad67) */
/***********************************************************************************/
#include <pybind11/complex.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;
#include <air_modes/preamble.h>
// pydoc.h is automatically generated in the build directory
#include <preamble_pydoc.h>
void bind_preamble(py::module& m)
{
using preamble = ::gr::air_modes::preamble;
py::class_<preamble, gr::block, gr::basic_block,
std::shared_ptr<preamble>>(m, "preamble", D(preamble))
.def(py::init(&preamble::make),
py::arg("channel_rate"),
py::arg("threshold_db"),
D(preamble,make)
)
.def("set_rate",&preamble::set_rate,
py::arg("channel_rate"),
D(preamble,set_rate)
)
.def("set_threshold",&preamble::set_threshold,
py::arg("threshold_db"),
D(preamble,set_threshold)
)
.def("get_rate",&preamble::get_rate,
D(preamble,get_rate)
)
.def("get_threshold",&preamble::get_threshold,
D(preamble,get_threshold)
)
;
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2020 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include <pybind11/pybind11.h>
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
#include <numpy/arrayobject.h>
namespace py = pybind11;
// Headers for binding functions
/**************************************/
/* The following comment block is used for
/* gr_modtool to insert function prototypes
/* Please do not delete
/**************************************/
// BINDING_FUNCTION_PROTOTYPES(
void bind_preamble(py::module& m);
void bind_slicer(py::module& m);
// ) END BINDING_FUNCTION_PROTOTYPES
// We need this hack because import_array() returns NULL
// for newer Python versions.
// This function is also necessary because it ensures access to the C API
// and removes a warning.
void* init_numpy()
{
import_array();
return NULL;
}
PYBIND11_MODULE(air_modes_python, m)
{
// Initialize the numpy C API
// (otherwise we will see segmentation faults)
init_numpy();
// Allow access to base block methods
py::module::import("gnuradio.gr");
/**************************************/
/* The following comment block is used for
/* gr_modtool to insert binding function calls
/* Please do not delete
/**************************************/
// BINDING_FUNCTION_CALLS(
bind_preamble(m);
bind_slicer(m);
// ) END BINDING_FUNCTION_CALLS
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2021 Free Software Foundation, Inc.
*
* This file is part of GNU Radio
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
/***********************************************************************************/
/* This file is automatically generated using bindtool and can be manually edited */
/* The following lines can be configured to regenerate this file during cmake */
/* If manual edits are made, the following tags should be modified accordingly. */
/* BINDTOOL_GEN_AUTOMATIC(0) */
/* BINDTOOL_USE_PYGCCXML(0) */
/* BINDTOOL_HEADER_FILE(slicer.h) */
/* BINDTOOL_HEADER_FILE_HASH(de5bad12b8ae0f22c88c9cd886eaf8cc) */
/***********************************************************************************/
#include <pybind11/complex.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace py = pybind11;
#include <air_modes/slicer.h>
// pydoc.h is automatically generated in the build directory
#include <slicer_pydoc.h>
void bind_slicer(py::module& m)
{
using slicer = ::gr::air_modes::slicer;
py::class_<slicer, gr::sync_block, gr::block, gr::basic_block,
std::shared_ptr<slicer>>(m, "slicer", D(slicer))
.def(py::init(&slicer::make),
py::arg("queue"),
D(slicer,make)
)
;
}

442
python/cpr.py Executable file → Normal file
View File

@@ -31,302 +31,302 @@ from air_modes.exceptions import *
latz = 15
def nz(ctype):
return 4 * latz - ctype
return 4 * latz - ctype
def dlat(ctype, surface):
if surface == 1:
tmp = 90.0
else:
tmp = 360.0
if surface == 1:
tmp = 90.0
else:
tmp = 360.0
nzcalc = nz(ctype)
if nzcalc == 0:
return tmp
else:
return tmp / nzcalc
nzcalc = nz(ctype)
if nzcalc == 0:
return tmp
else:
return tmp / nzcalc
def nl(declat_in):
if abs(declat_in) >= 87.0:
return 1.0
return math.floor( (2.0*math.pi) * math.acos(1.0- (1.0-math.cos(math.pi/(2.0*latz))) / math.cos( (math.pi/180.0)*abs(declat_in) )**2 )**-1)
if abs(declat_in) >= 87.0:
return 1.0
return math.floor( (2.0*math.pi) * math.acos(1.0- (1.0-math.cos(math.pi/(2.0*latz))) / math.cos( (math.pi/180.0)*abs(declat_in) )**2 )**-1)
def dlon(declat_in, ctype, surface):
if surface:
tmp = 90.0
else:
tmp = 360.0
nlcalc = max(nl(declat_in)-ctype, 1)
return tmp / nlcalc
if surface:
tmp = 90.0
else:
tmp = 360.0
nlcalc = max(nl(declat_in)-ctype, 1)
return tmp / nlcalc
def decode_lat(enclat, ctype, my_lat, surface):
tmp1 = dlat(ctype, surface)
tmp2 = float(enclat) / (2**17)
j = math.floor(my_lat/tmp1) + math.floor(0.5 + ((my_lat % tmp1) / tmp1) - tmp2)
tmp1 = dlat(ctype, surface)
tmp2 = float(enclat) / (2**17)
j = math.floor(my_lat/tmp1) + math.floor(0.5 + ((my_lat % tmp1) / tmp1) - tmp2)
return tmp1 * (j + tmp2)
return tmp1 * (j + tmp2)
def decode_lon(declat, enclon, ctype, my_lon, surface):
tmp1 = dlon(declat, ctype, surface)
tmp2 = float(enclon) / (2**17)
m = math.floor(my_lon / tmp1) + math.floor(0.5 + ((my_lon % tmp1) / tmp1) - tmp2)
tmp1 = dlon(declat, ctype, surface)
tmp2 = float(enclon) / (2**17)
m = math.floor(my_lon / tmp1) + math.floor(0.5 + ((my_lon % tmp1) / tmp1) - tmp2)
return tmp1 * (m + tmp2)
return tmp1 * (m + tmp2)
def cpr_resolve_local(my_location, encoded_location, ctype, surface):
[my_lat, my_lon] = my_location
[enclat, enclon] = encoded_location
[my_lat, my_lon] = my_location
[enclat, enclon] = encoded_location
decoded_lat = decode_lat(enclat, ctype, my_lat, surface)
decoded_lon = decode_lon(decoded_lat, enclon, ctype, my_lon, surface)
decoded_lat = decode_lat(enclat, ctype, my_lat, surface)
decoded_lon = decode_lon(decoded_lat, enclon, ctype, my_lon, surface)
return [decoded_lat, decoded_lon]
return [decoded_lat, decoded_lon]
def cpr_resolve_global(evenpos, oddpos, mypos, mostrecent, surface):
#cannot resolve surface positions unambiguously without knowing receiver position
if surface and mypos is None:
raise CPRNoPositionError
dlateven = dlat(0, surface)
dlatodd = dlat(1, surface)
#cannot resolve surface positions unambiguously without knowing receiver position
if surface and mypos is None:
raise CPRNoPositionError
dlateven = dlat(0, surface)
dlatodd = dlat(1, surface)
evenpos = [float(evenpos[0]), float(evenpos[1])]
oddpos = [float(oddpos[0]), float(oddpos[1])]
j = math.floor(((nz(1)*evenpos[0] - nz(0)*oddpos[0])/2**17) + 0.5) #latitude index
evenpos = [float(evenpos[0]), float(evenpos[1])]
oddpos = [float(oddpos[0]), float(oddpos[1])]
j = math.floor(((nz(1)*evenpos[0] - nz(0)*oddpos[0])/2**17) + 0.5) #latitude index
rlateven = dlateven * ((j % nz(0))+evenpos[0]/2**17)
rlatodd = dlatodd * ((j % nz(1))+ oddpos[0]/2**17)
rlateven = dlateven * ((j % nz(0))+evenpos[0]/2**17)
rlatodd = dlatodd * ((j % nz(1))+ oddpos[0]/2**17)
#limit to -90, 90
if rlateven > 270.0:
rlateven -= 360.0
if rlatodd > 270.0:
rlatodd -= 360.0
#limit to -90, 90
if rlateven > 270.0:
rlateven -= 360.0
if rlatodd > 270.0:
rlatodd -= 360.0
#This checks to see if the latitudes of the reports straddle a transition boundary
#If so, you can't get a globally-resolvable location.
if nl(rlateven) != nl(rlatodd):
raise CPRBoundaryStraddleError
#This checks to see if the latitudes of the reports straddle a transition boundary
#If so, you can't get a globally-resolvable location.
if nl(rlateven) != nl(rlatodd):
raise CPRBoundaryStraddleError
if mostrecent == 0:
rlat = rlateven
else:
rlat = rlatodd
if mostrecent == 0:
rlat = rlateven
else:
rlat = rlatodd
#disambiguate latitude
if surface:
if mypos[0] < 0:
rlat -= 90
#disambiguate latitude
if surface:
if mypos[0] < 0:
rlat -= 90
dl = dlon(rlat, mostrecent, surface)
nl_rlat = nl(rlat)
dl = dlon(rlat, mostrecent, surface)
nl_rlat = nl(rlat)
m = math.floor(((evenpos[1]*(nl_rlat-1)-oddpos[1]*nl_rlat)/2**17)+0.5) #longitude index
#when surface positions straddle a disambiguation boundary (90 degrees),
#surface decoding will fail. this might never be a problem in real life, but it'll fail in the
#test case. the documentation doesn't mention it.
m = math.floor(((evenpos[1]*(nl_rlat-1)-oddpos[1]*nl_rlat)/2**17)+0.5) #longitude index
#when surface positions straddle a disambiguation boundary (90 degrees),
#surface decoding will fail. this might never be a problem in real life, but it'll fail in the
#test case. the documentation doesn't mention it.
if mostrecent == 0:
enclon = evenpos[1]
else:
enclon = oddpos[1]
if mostrecent == 0:
enclon = evenpos[1]
else:
enclon = oddpos[1]
rlon = dl * ((m % max(nl_rlat-mostrecent,1)) + enclon/2.**17)
rlon = dl * ((m % max(nl_rlat-mostrecent,1)) + enclon/2.**17)
#print "DL: %f nl: %f m: %f rlon: %f" % (dl, nl_rlat, m, rlon)
#print "evenpos: %x, oddpos: %x, mostrecent: %i" % (evenpos[1], oddpos[1], mostrecent)
#print "DL: %f nl: %f m: %f rlon: %f" % (dl, nl_rlat, m, rlon)
#print "evenpos: %x, oddpos: %x, mostrecent: %i" % (evenpos[1], oddpos[1], mostrecent)
if surface:
#longitudes need to be resolved to the nearest 90 degree segment to the receiver.
wat = mypos[1]
if wat < 0:
wat += 360
zone = lambda lon: 90 * (int(lon) / 90)
rlon += (zone(wat) - zone(rlon))
if surface:
#longitudes need to be resolved to the nearest 90 degree segment to the receiver.
wat = mypos[1]
if wat < 0:
wat += 360
zone = lambda lon: 90 * (int(lon) / 90)
rlon += (zone(wat) - zone(rlon))
#limit to (-180, 180)
if rlon > 180:
rlon -= 360.0
#limit to (-180, 180)
if rlon > 180:
rlon -= 360.0
return [rlat, rlon]
return [rlat, rlon]
#calculate range and bearing between two lat/lon points
#should probably throw this in the mlat py somewhere or make another lib
def range_bearing(loc_a, loc_b):
[a_lat, a_lon] = loc_a
[b_lat, b_lon] = loc_b
[a_lat, a_lon] = loc_a
[b_lat, b_lon] = loc_b
esquared = (1/298.257223563)*(2-(1/298.257223563))
earth_radius_mi = 3963.19059 * (math.pi / 180)
esquared = (1/298.257223563)*(2-(1/298.257223563))
earth_radius_mi = 3963.19059 * (math.pi / 180)
delta_lat = b_lat - a_lat
delta_lon = b_lon - a_lon
delta_lat = b_lat - a_lat
delta_lon = b_lon - a_lon
avg_lat = ((a_lat + b_lat) / 2.0) * math.pi / 180
avg_lat = ((a_lat + b_lat) / 2.0) * math.pi / 180
R1 = earth_radius_mi*(1.0-esquared)/pow((1.0-esquared*pow(math.sin(avg_lat),2)),1.5)
R1 = earth_radius_mi*(1.0-esquared)/pow((1.0-esquared*pow(math.sin(avg_lat),2)),1.5)
R2 = earth_radius_mi/math.sqrt(1.0-esquared*pow(math.sin(avg_lat),2))
R2 = earth_radius_mi/math.sqrt(1.0-esquared*pow(math.sin(avg_lat),2))
distance_North = R1*delta_lat
distance_East = R2*math.cos(avg_lat)*delta_lon
distance_North = R1*delta_lat
distance_East = R2*math.cos(avg_lat)*delta_lon
bearing = math.atan2(distance_East,distance_North) * (180.0 / math.pi)
if bearing < 0.0:
bearing += 360.0
bearing = math.atan2(distance_East,distance_North) * (180.0 / math.pi)
if bearing < 0.0:
bearing += 360.0
rnge = math.hypot(distance_East,distance_North)
return [rnge, bearing]
rnge = math.hypot(distance_East,distance_North)
return [rnge, bearing]
class cpr_decoder:
def __init__(self, my_location):
self.my_location = my_location
self.evenlist = {}
self.oddlist = {}
self.evenlist_sfc = {}
self.oddlist_sfc = {}
def __init__(self, my_location):
self.my_location = my_location
self.evenlist = {}
self.oddlist = {}
self.evenlist_sfc = {}
self.oddlist_sfc = {}
def set_location(new_location):
self.my_location = new_location
def set_location(self, new_location):
self.my_location = new_location
def weed_poslists(self):
for poslist in [self.evenlist, self.oddlist]:
for key, item in poslist.items():
if time.time() - item[2] > 10:
del poslist[key]
for poslist in [self.evenlist_sfc, self.oddlist_sfc]:
for key, item in poslist.items():
if time.time() - item[2] > 25:
del poslist[key]
def weed_poslists(self):
for poslist in [self.evenlist, self.oddlist]:
for key, item in tuple(poslist.items()):
if time.time() - item[2] > 10:
del poslist[key]
for poslist in [self.evenlist_sfc, self.oddlist_sfc]:
for key, item in tuple(poslist.items()):
if time.time() - item[2] > 25:
del poslist[key]
def decode(self, icao24, encoded_lat, encoded_lon, cpr_format, surface):
if surface:
oddlist = self.oddlist_sfc
evenlist = self.evenlist_sfc
else:
oddlist = self.oddlist
evenlist = self.evenlist
def decode(self, icao24, encoded_lat, encoded_lon, cpr_format, surface):
if surface:
oddlist = self.oddlist_sfc
evenlist = self.evenlist_sfc
else:
oddlist = self.oddlist
evenlist = self.evenlist
#add the info to the position reports list for global decoding
if cpr_format==1:
oddlist[icao24] = [encoded_lat, encoded_lon, time.time()]
else:
evenlist[icao24] = [encoded_lat, encoded_lon, time.time()]
#add the info to the position reports list for global decoding
if cpr_format==1:
oddlist[icao24] = [encoded_lat, encoded_lon, time.time()]
else:
evenlist[icao24] = [encoded_lat, encoded_lon, time.time()]
[decoded_lat, decoded_lon] = [None, None]
[decoded_lat, decoded_lon] = [None, None]
#okay, let's traverse the lists and weed out those entries that are older than 10 seconds
self.weed_poslists()
#okay, let's traverse the lists and weed out those entries that are older than 10 seconds
self.weed_poslists()
if (icao24 in evenlist) \
and (icao24 in oddlist):
newer = (oddlist[icao24][2] - evenlist[icao24][2]) > 0 #figure out which report is newer
[decoded_lat, decoded_lon] = cpr_resolve_global(evenlist[icao24][0:2], oddlist[icao24][0:2], self.my_location, newer, surface) #do a global decode
else:
raise CPRNoPositionError
if (icao24 in evenlist) \
and (icao24 in oddlist):
newer = (oddlist[icao24][2] - evenlist[icao24][2]) > 0 #figure out which report is newer
[decoded_lat, decoded_lon] = cpr_resolve_global(evenlist[icao24][0:2], oddlist[icao24][0:2], self.my_location, newer, surface) #do a global decode
else:
raise CPRNoPositionError
if self.my_location is not None:
[rnge, bearing] = range_bearing(self.my_location, [decoded_lat, decoded_lon])
else:
rnge = None
bearing = None
if self.my_location is not None:
[rnge, bearing] = range_bearing(self.my_location, [decoded_lat, decoded_lon])
else:
rnge = None
bearing = None
return [decoded_lat, decoded_lon, rnge, bearing]
return [decoded_lat, decoded_lon, rnge, bearing]
#encode CPR position
def cpr_encode(lat, lon, ctype, surface):
if surface is True:
scalar = 2.**19
else:
scalar = 2.**17
if surface is True:
scalar = 2.**19
else:
scalar = 2.**17
#encode using 360 constant for segment size.
dlati = dlat(ctype, False)
yz = math.floor(scalar * ((lat % dlati)/dlati) + 0.5)
rlat = dlati * ((yz / scalar) + math.floor(lat / dlati))
#encode using 360 constant for segment size.
dlati = dlat(ctype, False)
yz = math.floor(scalar * ((lat % dlati)/dlati) + 0.5)
rlat = dlati * ((yz / scalar) + math.floor(lat / dlati))
#encode using 360 constant for segment size.
dloni = dlon(lat, ctype, False)
xz = math.floor(scalar * ((lon % dloni)/dloni) + 0.5)
#encode using 360 constant for segment size.
dloni = dlon(lat, ctype, False)
xz = math.floor(scalar * ((lon % dloni)/dloni) + 0.5)
yz = int(yz) & (2**17-1)
xz = int(xz) & (2**17-1)
yz = int(yz) & (2**17-1)
xz = int(xz) & (2**17-1)
return (yz, xz) #lat, lon
return (yz, xz) #lat, lon
if __name__ == '__main__':
import sys, random
rounds = 10001
threshold = 1e-3 #0.001 deg lat/lon
#this accuracy is highly dependent on latitude, since at high
#latitudes the corresponding error in longitude is greater
import sys, random
rounds = 10001
threshold = 1e-3 #0.001 deg lat/lon
#this accuracy is highly dependent on latitude, since at high
#latitudes the corresponding error in longitude is greater
bs = 0
surface = False
bs = 0
surface = False
lats = [i/(rounds/170.)-85 for i in range(0,rounds)]
lons = [i/(rounds/360.)-180 for i in range(0,rounds)]
lats = [i/(rounds/170.)-85 for i in range(0,rounds)]
lons = [i/(rounds/360.)-180 for i in range(0,rounds)]
for i in range(0, rounds):
even_lat = lats[i]
#even_lat = random.uniform(-85, 85)
even_lon = lons[i]
#even_lon = random.uniform(-180, 180)
odd_lat = even_lat + 1e-3
odd_lon = min(even_lon + 1e-3, 180)
decoder = cpr_decoder([odd_lat, odd_lon])
for i in range(0, rounds):
even_lat = lats[i]
#even_lat = random.uniform(-85, 85)
even_lon = lons[i]
#even_lon = random.uniform(-180, 180)
odd_lat = even_lat + 1e-3
odd_lon = min(even_lon + 1e-3, 180)
decoder = cpr_decoder([odd_lat, odd_lon])
#encode that position
(evenenclat, evenenclon) = cpr_encode(even_lat, even_lon, False, surface)
(oddenclat, oddenclon) = cpr_encode(odd_lat, odd_lon, True, surface)
#encode that position
(evenenclat, evenenclon) = cpr_encode(even_lat, even_lon, False, surface)
(oddenclat, oddenclon) = cpr_encode(odd_lat, odd_lon, True, surface)
#try to perform a global decode -- this should fail since the decoder
#only has heard one position. need two for global decoding.
icao = random.randint(0, 0xffffff)
try:
evenpos = decoder.decode(icao, evenenclat, evenenclon, False, surface)
raise Exception("CPR test failure: global decode with only one report")
except CPRNoPositionError:
pass
#try to perform a global decode -- this should fail since the decoder
#only has heard one position. need two for global decoding.
icao = random.randint(0, 0xffffff)
try:
evenpos = decoder.decode(icao, evenenclat, evenenclon, False, surface)
raise Exception("CPR test failure: global decode with only one report")
except CPRNoPositionError:
pass
#now try to do a real decode with the last packet's odd complement
#watch for a boundary straddle -- this isn't fatal, it just indicates
#that the even and odd reports lie on either side of a longitudinal boundary
#and so you can't get a position
try:
(odddeclat, odddeclon, rng, brg) = decoder.decode(icao, oddenclat, oddenclon, True, surface)
except CPRBoundaryStraddleError:
bs += 1
continue
except CPRNoPositionError:
raise Exception("CPR test failure: no decode after even/odd inputs")
#now try to do a real decode with the last packet's odd complement
#watch for a boundary straddle -- this isn't fatal, it just indicates
#that the even and odd reports lie on either side of a longitudinal boundary
#and so you can't get a position
try:
(odddeclat, odddeclon, rng, brg) = decoder.decode(icao, oddenclat, oddenclon, True, surface)
except CPRBoundaryStraddleError:
bs += 1
continue
except CPRNoPositionError:
raise Exception("CPR test failure: no decode after even/odd inputs")
if abs(odddeclat - odd_lat) > threshold or abs(odddeclon - odd_lon) > threshold:
print "F odddeclat: %f odd_lat: %f" % (odddeclat, odd_lat)
print "F odddeclon: %f odd_lon: %f" % (odddeclon, odd_lon)
raise Exception("CPR test failure: global decode error greater than threshold")
# else:
# print "S odddeclat: %f odd_lat: %f" % (odddeclat, odd_lat)
# print "S odddeclon: %f odd_lon: %f" % (odddeclon, odd_lon)
if abs(odddeclat - odd_lat) > threshold or abs(odddeclon - odd_lon) > threshold:
print("F odddeclat: %f odd_lat: %f" % (odddeclat, odd_lat))
print( "F odddeclon: %f odd_lon: %f" % (odddeclon, odd_lon))
raise Exception("CPR test failure: global decode error greater than threshold")
# else:
# print("S odddeclat: %f odd_lat: %f" % (odddeclat, odd_lat))
# print("S odddeclon: %f odd_lon: %f" % (odddeclon, odd_lon))
nexteven_lat = odd_lat + 1e-3
nexteven_lon = min(odd_lon + 1e-3, 180)
nexteven_lat = odd_lat + 1e-3
nexteven_lon = min(odd_lon + 1e-3, 180)
(nexteven_enclat, nexteven_enclon) = cpr_encode(nexteven_lat, nexteven_lon, False, surface)
(nexteven_enclat, nexteven_enclon) = cpr_encode(nexteven_lat, nexteven_lon, False, surface)
#try a locally-referenced decode
try:
(evendeclat, evendeclon) = cpr_resolve_local([even_lat, even_lon], [nexteven_enclat, nexteven_enclon], False, surface)
except CPRNoPositionError:
raise Exception("CPR test failure: local decode failure to resolve")
#try a locally-referenced decode
try:
(evendeclat, evendeclon) = cpr_resolve_local([even_lat, even_lon], [nexteven_enclat, nexteven_enclon], False, surface)
except CPRNoPositionError:
raise Exception("CPR test failure: local decode failure to resolve")
#check to see if the positions were valid
if abs(evendeclat - nexteven_lat) > threshold or abs(evendeclon - nexteven_lon) > threshold:
print "F evendeclat: %f nexteven_lat: %f evenlat: %f" % (evendeclat, nexteven_lat, even_lat)
print "F evendeclon: %f nexteven_lon: %f evenlon: %f" % (evendeclon, nexteven_lon, even_lon)
raise Exception("CPR test failure: local decode error greater than threshold")
#check to see if the positions were valid
if abs(evendeclat - nexteven_lat) > threshold or abs(evendeclon - nexteven_lon) > threshold:
print("F evendeclat: %f nexteven_lat: %f evenlat: %f" % (evendeclat, nexteven_lat, even_lat))
print("F evendeclon: %f nexteven_lon: %f evenlon: %f" % (evendeclon, nexteven_lon, even_lon))
raise Exception("CPR test failure: local decode error greater than threshold")
print "CPR test successful. There were %i boundary straddles over %i rounds." % (bs, rounds)
print("CPR test successful. There were %i boundary straddles over %i rounds." % (bs, rounds))

View File

@@ -24,12 +24,12 @@ class ADSBError(Exception):
class MetricAltError(ADSBError):
pass
class ParserError(ADSBError):
pass
class NoHandlerError(ADSBError):
def __init__(self, msgtype):
def __init__(self, msgtype=None):
self.msgtype = msgtype
class MlatNonConvergeError(ADSBError):
@@ -44,3 +44,4 @@ class CPRBoundaryStraddleError(CPRNoPositionError):
class FieldNotInPacket(ParserError):
def __init__(self, item):
self.item = item

33
python/flightgear.py Executable file → Normal file
View File

@@ -14,53 +14,52 @@ from Quaternion import Quat
import numpy
from air_modes.exceptions import *
class output_flightgear(air_modes.parse):
def __init__(self, localpos, hostname, port):
air_modes.parse.__init__(self, localpos)
class output_flightgear:
def __init__(self, cprdec, hostname, port, pub):
self.hostname = hostname
self.port = port
self.localpos = localpos
self.positions = {}
self.velocities = {}
self.callsigns = {}
self._cpr = cprdec
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.connect((self.hostname, self.port))
# self.sock.connect((self.hostname, self.port))
pub.subscribe("type17_dl", self.output)
def output(self, msg):
try:
msgtype = msg.data["df"]
if msgtype == 17: #ADS-B report
icao24 = msg.data["aa"]
bdsreg = msg.data["me"].get_type()
if bdsreg == 0x08: #ident packet
(ident, actype) = self.parseBDS08(msg.data)
(ident, actype) = air_modes.parseBDS08(msg.data)
#select model based on actype
self.callsigns[icao24] = [ident, actype]
elif bdsreg == 0x06: #BDS0,6 pos
[ground_track, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS06(msg.data)
[ground_track, decoded_lat, decoded_lon, rnge, bearing] = air_modes.parseBDS06(msg.data, self._cpr)
self.positions[icao24] = [decoded_lat, decoded_lon, 0]
self.update(icao24)
elif bdsreg == 0x05: #BDS0,5 pos
[altitude, decoded_lat, decoded_lon, rnge, bearing] = self.parseBDS05(msg.data)
[altitude, decoded_lat, decoded_lon, rnge, bearing] = air_modes.parseBDS05(msg.data, self._cpr)
self.positions[icao24] = [decoded_lat, decoded_lon, altitude]
self.update(icao24)
elif bdsreg == 0x09: #velocity
subtype = data["bds09"].get_type()
subtype = msg.data["bds09"].get_type()
if subtype == 0:
[velocity, heading, vert_spd, turnrate] = self.parseBDS09_0(msg.data)
[velocity, heading, vert_spd, turnrate] = air_modes.parseBDS09_0(msg.data)
elif subtype == 1:
[velocity, heading, vert_spd] = self.parseBDS09_1(msg.data)
[velocity, heading, vert_spd] = air_modes.parseBDS09_1(msg.data)
turnrate = 0
else:
return
self.velocities[icao24] = [velocity, heading, vert_spd, turnrate]
except ADSBError:
pass
@@ -70,7 +69,7 @@ class output_flightgear(air_modes.parse):
and (icao24 in self.velocities)\
and (icao24 in self.callsigns)
if complete:
print "FG update: %s" % (self.callsigns[icao24][0])
print("FG update: %s" % (self.callsigns[icao24][0]))
msg = fg_posmsg(self.callsigns[icao24][0],
self.callsigns[icao24][1],
self.positions[icao24][0],
@@ -81,7 +80,7 @@ class output_flightgear(air_modes.parse):
self.velocities[icao24][2],
self.velocities[icao24][3]).pack()
self.sock.send(msg)
self.sock.sendto(msg, (self.hostname, self.port))
class fg_header:
def __init__(self):
@@ -120,7 +119,7 @@ modelmap = { None: 'Aircraft/777-200/Models/777-200ER.xml'
"SMALL": 'Aircraft/CitationX/Models/Citation-X.xml',
"LARGE": 'Aircraft/CRJ700-family/Models/CRJ700.xml',
"LARGE HIGH VORTEX": 'Aircraft/757-200/Models/757-200.xml',
"HEAVY": 'Aircraft/747-200/Models/boeing747-200.xml',
"HEAVY": 'Aircraft/IDG-A32X/Models/A320neo-CFM.xml',
"HIGH PERFORMANCE": 'Aircraft/SR71-BlackBird/Models/Blackbird-SR71B.xml', #yeah i know
"ROTORCRAFT": 'Aircraft/ec130/Models/ec130b4.xml',
"GLIDER": 'Aircraft/ASK21-MI/Models/ask21mi.xml',

21
python/get_uniq.py Normal file
View File

@@ -0,0 +1,21 @@
#!/usr/bin/env python
import sys, re
if __name__== '__main__':
data = sys.stdin.readlines()
icaos = []
num_icaos = 0
for line in data:
match = re.match(".*from (\w+)", line)
if match is not None:
icao = int(match.group(1), 16)
icaos.append(icao)
#get dupes
dupes = sorted([icao for icao in set(icaos) if icaos.count(icao) > 1])
for icao in dupes:
print "%x" % icao
print "Found non-unique replies from %i aircraft" % len(dupes)

View File

@@ -25,6 +25,7 @@
from PyQt4 import QtCore, QtGui
import air_modes
import threading, math, time
from air_modes.exceptions import *
#fades the ICAOs out as their last report gets older,
#and display ident if available, ICAO otherwise
@@ -130,7 +131,7 @@ class dashboard_data_model(QtCore.QAbstractTableModel):
self.lock.release()
self.prune()
#weeds out ICAOs older than 5 minutes
#weeds out ICAOs older than 1 minute
def prune(self):
self.lock.acquire()
for (index,row) in enumerate(self._data):
@@ -140,59 +141,61 @@ class dashboard_data_model(QtCore.QAbstractTableModel):
self.endRemoveRows()
self.lock.release()
class dashboard_output(air_modes.parse):
def __init__(self, mypos, model):
air_modes.parse.__init__(self, mypos)
class dashboard_output:
def __init__(self, cprdec, model, pub):
self.model = model
self._cpr = cprdec
pub.subscribe("modes_dl", self.output)
def output(self, msg):
[data, ecc, reference, timestamp] = msg.split()
data = air_modes.modes_reply(long(data, 16))
ecc = long(ecc, 16)
rssi = 10.*math.log10(float(reference))
msgtype = data["df"]
now = time.time()
newrow = {"rssi": rssi, "seen": now}
if msgtype in [0, 4, 20]:
newrow["altitude"] = air_modes.altitude.decode_alt(data["ac"], True)
newrow["icao"] = ecc
self.model.addRecord(newrow)
elif msgtype == 17:
icao = data["aa"]
newrow["icao"] = icao
subtype = data["ftc"]
if subtype == 4:
(ident, actype) = self.parseBDS08(data)
newrow["ident"] = ident
newrow["type"] = actype
elif 5 <= subtype <= 8:
(ground_track, decoded_lat, decoded_lon, rnge, bearing) = self.parseBDS06(data)
newrow["heading"] = ground_track
newrow["latitude"] = decoded_lat
newrow["longitude"] = decoded_lon
newrow["altitude"] = 0
if rnge is not None:
newrow["range"] = rnge
newrow["bearing"] = bearing
elif 9 <= subtype <= 18:
(altitude, decoded_lat, decoded_lon, rnge, bearing) = self.parseBDS05(data)
newrow["altitude"] = altitude
newrow["latitude"] = decoded_lat
newrow["longitude"] = decoded_lon
if rnge is not None:
newrow["range"] = rnge
newrow["bearing"] = bearing
elif subtype == 19:
subsubtype = data["sub"]
velocity = None
heading = None
vert_spd = None
if subsubtype == 0:
(velocity, heading, vert_spd) = self.parseBDS09_0(data)
elif 1 <= subsubtype <= 2:
(velocity, heading, vert_spd) = self.parseBDS09_1(data)
newrow["speed"] = velocity
newrow["heading"] = heading
newrow["vertical"] = vert_spd
try:
msgtype = msg.data["df"]
now = time.time()
newrow = {"rssi": msg.rssi, "seen": now}
if msgtype in [0, 4, 20]:
newrow["altitude"] = air_modes.altitude.decode_alt(msg.data["ac"], True)
newrow["icao"] = msg.ecc
self.model.addRecord(newrow)
elif msgtype == 17:
icao = msg.data["aa"]
newrow["icao"] = icao
subtype = msg.data["ftc"]
if subtype == 4:
(ident, actype) = air_modes.parseBDS08(msg.data)
newrow["ident"] = ident
newrow["type"] = actype
elif 5 <= subtype <= 8:
(ground_track, decoded_lat, decoded_lon, rnge, bearing) = air_modes.parseBDS06(msg.data, self._cpr)
newrow["heading"] = ground_track
newrow["latitude"] = decoded_lat
newrow["longitude"] = decoded_lon
newrow["altitude"] = 0
if rnge is not None:
newrow["range"] = rnge
newrow["bearing"] = bearing
elif 9 <= subtype <= 18:
(altitude, decoded_lat, decoded_lon, rnge, bearing) = air_modes.parseBDS05(msg.data, self._cpr)
newrow["altitude"] = altitude
newrow["latitude"] = decoded_lat
newrow["longitude"] = decoded_lon
if rnge is not None:
newrow["range"] = rnge
newrow["bearing"] = bearing
elif subtype == 19:
subsubtype = msg.data["sub"]
velocity = None
heading = None
vert_spd = None
if subsubtype == 0:
(velocity, heading, vert_spd) = air_modes.parseBDS09_0(msg.data)
elif 1 <= subsubtype <= 2:
(velocity, heading, vert_spd) = air_modes.parseBDS09_1(msg.data)
newrow["speed"] = velocity
newrow["heading"] = heading
newrow["vertical"] = vert_spd
self.model.addRecord(newrow)
except ADSBError:
return
self.model.addRecord(newrow)

155
python/html_template.py Normal file
View File

@@ -0,0 +1,155 @@
#!/usr/bin/env python
#HTML template for Mode S map display
#Nick Foster, 2013
def html_template(my_apikey, my_position, json_file):
if my_position is None:
my_position = [37, -122]
return """
<html>
<head>
<title>ADS-B Aircraft Map</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<style type="text/css">
.labels {
color: blue;
background-color: white;
font-family: "Lucida Grande", "Arial", sans-serif;
font-size: 13px;
font-weight: bold;
text-align: center;
width: 70px;
border: none;
white-space: nowrap;
}
</style>
<script type="text/javascript" src="http://maps.google.com/maps/api/js?key=%s">
</script>
<script type="text/javascript" src="https://raw.githubusercontent.com/googlemaps/v3-utility-library/master/markerwithlabel/src/markerwithlabel.js">
</script>
<script type="text/javascript">
var map;
var markers = [];
var defaultLocation = new google.maps.LatLng(%f, %f);
var defaultZoomLevel = 9;
function requestJSONP() {
var script = document.createElement("script");
script.src = "%s?" + Math.random();
script.params = Math.random();
document.getElementsByTagName('head')[0].appendChild(script);
};
var planeMarker;
var planes = [];
function clearMarkers() {
for (var i = 0; i < planes.length; i++) {
planes[i].setMap(null);
}
planes = [];
};
function jsonp_callback(results) { // from JSONP
airplanes = {};
for (var i = 0; i < results.length; i++) {
airplanes[results[i].icao] = {
center: new google.maps.LatLng(results[i].lat, results[i].lon),
heading: results[i].hdg,
altitude: results[i].alt,
type: results[i].type,
ident: results[i].ident,
speed: results[i].speed,
vertical: results[i].vertical,
highlight: results[i].highlight
};
}
// clearMarkers();
refreshIcons();
}
function refreshIcons() {
//prune the list
for(var i = 0; i < planes.length; i++) {
icao = planes[i].get("icao")
if(!(icao in airplanes)) {
planes[i].setMap(null)
planes.splice(i, 1);
};
};
for (var airplane in airplanes) {
if (airplanes[airplane].highlight != 0) {
icon_file = "http://www.nerdnetworks.org/~bistromath/airplane_sprite_highlight.png";
} else {
icon_file = "http://www.nerdnetworks.org/~bistromath/airplane_sprite.png";
};
var plane_icon = {
url: icon_file,
size: new google.maps.Size(128,128),
origin: new google.maps.Point(parseInt(airplanes[airplane].heading/10)*128,0),
anchor: new google.maps.Point(64,64),
//scaledSize: new google.maps.Size(4608,126)
};
if (airplanes[airplane].ident.length != 8) {
identstr = airplane;
} else {
identstr = airplanes[airplane].ident;
};
var planeOptions = {
map: map,
position: airplanes[airplane].center,
icao: airplane,
icon: plane_icon,
labelContent: identstr,
labelAnchor: new google.maps.Point(35, -32),
labelClass: "labels",
labelStyle: {opacity: 0.75}
};
var i = 0;
for(i; i<planes.length; i++) {
if(planes[i].get("icao") == airplane) {
planes[i].setPosition(airplanes[airplane].center);
if(planes[i].get("icon") != plane_icon) {
planes[i].setIcon(plane_icon); //handles highlight and heading
};
if(planes[i].get("labelContent") != identstr) {
planes[i].set("labelContent", identstr);
};
break;
};
};
if(i == planes.length) {
planeMarker = new MarkerWithLabel(planeOptions);
planes.push(planeMarker);
};
};
};
function initialize()
{
var myOptions =
{
zoom: defaultZoomLevel,
center: defaultLocation,
disableDefaultUI: true,
mapTypeId: google.maps.MapTypeId.TERRAIN
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
requestJSONP();
setInterval("requestJSONP()", 1000);
};
</script>
</head>
<body onload="initialize()">
<div id="map_canvas" style="width:100%%; height:100%%">
</div>
</body>
</html>""" % (my_apikey, my_position[0], my_position[1], json_file)

View File

@@ -20,7 +20,7 @@
#
import sqlite3
import string, math, threading, time
import math, threading, time
class output_kml(threading.Thread):
def __init__(self, filename, dbname, localpos, lock, timeout=5):
@@ -30,21 +30,30 @@ class output_kml(threading.Thread):
self.my_coords = localpos
self._timeout = timeout
self._lock = lock
self.done = False
self.shutdown = threading.Event()
self.finished = threading.Event()
self.setDaemon(1)
self.start()
def run(self):
self._db = sqlite3.connect(self._dbname) #read from the db
while self.done is False:
while self.shutdown.is_set() is False:
time.sleep(self._timeout)
self.writekml()
time.sleep(self._timeout)
self.done = True
self._db.close()
self._db = None
self.finished.set()
def close(self):
self.shutdown.set()
self.finished.wait(0.2)
#there's a bug here where self._timeout is long and close() has
#to wait for the sleep to expire before closing. we just bail
#instead with the 0.2 param above.
def writekml(self):
kmlstr = self.genkml()
if kmlstr is not None:
@@ -55,7 +64,7 @@ class output_kml(threading.Thread):
def locked_execute(self, c, query):
with self._lock:
c.execute(query)
def draw_circle(self, center, rng):
retstr = ""
steps=30
@@ -80,9 +89,9 @@ class output_kml(threading.Thread):
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)
retstr = retstr.lstrip()
return retstr
def genkml(self):
#first let's draw the static content
retstr="""<?xml version="1.0" encoding="UTF-8"?>\n<kml xmlns="http://www.opengis.net/kml/2.2">\n<Document>\n\t<Style id="airplane">\n\t\t<IconStyle>\n\t\t\t<Icon><href>airports.png</href></Icon>\n\t\t</IconStyle>\n\t</Style>\n\t<Style id="rangering">\n\t<LineStyle>\n\t\t<color>9f4f4faf</color>\n\t\t<width>2</width>\n\t</LineStyle>\n\t</Style>\n\t<Style id="track">\n\t<LineStyle>\n\t\t<color>5fff8f8f</color>\n\t\t<width>4</width>\n\t</LineStyle>\n\t</Style>"""
@@ -92,7 +101,7 @@ class output_kml(threading.Thread):
for rng in [100, 200, 300]:
retstr += """\n\t\t<Placemark>\n\t\t\t<name>%inm</name>\n\t\t\t<styleUrl>#rangering</styleUrl>\n\t\t\t<LinearRing>\n\t\t\t\t<coordinates>%s</coordinates>\n\t\t\t</LinearRing>\n\t\t</Placemark>""" % (rng, self.draw_circle(self.my_coords, rng),)
retstr += """\t</Folder>\n"""
retstr += """\t<Folder>\n\t\t<name>Aircraft locations</name>\n\t\t<open>0</open>"""
#read the database and add KML
@@ -115,15 +124,15 @@ class output_kml(threading.Thread):
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)
trackstr = trackstr.lstrip()
else:
alt = 0
metric_alt = 0
@@ -153,7 +162,7 @@ class output_kml(threading.Thread):
seen = 0
speed = 0
heading = 0
vertical = 0
vertical = 0
#now generate some KML
retstr+= "\n\t\t<Placemark>\n\t\t\t<name>%s</name>\n\t\t\t<Style><IconStyle><heading>%i</heading></IconStyle></Style>\n\t\t\t<styleUrl>#airplane</styleUrl>\n\t\t\t<description>\n\t\t\t\t<![CDATA[Altitude: %s<br/>Heading: %i<br/>Speed: %i<br/>Vertical speed: %i<br/>ICAO: %x<br/>Last seen: %s]]>\n\t\t\t</description>\n\t\t\t<Point>\n\t\t\t\t<altitudeMode>absolute</altitudeMode>\n\t\t\t\t<extrude>1</extrude>\n\t\t\t\t<coordinates>%s,%s,%i</coordinates>\n\t\t\t</Point>\n\t\t</Placemark>" % (ident, heading, alt, heading, speed, vertical, icao[0], seen, lon, lat, metric_alt, )
@@ -161,3 +170,79 @@ class output_kml(threading.Thread):
retstr+= '\n\t</Folder>\n</Document>\n</kml>'
return retstr
#we just inherit from output_kml because we're doing the same thing, only in a different format.
class output_jsonp(output_kml):
def set_highlight(self, icao):
self.highlight = icao
def genkml(self):
retstr="""jsonp_callback(["""
# if self.my_coords is not None:
# retstr += """\n\t<Folder>\n\t\t<name>Range rings</name>\n\t\t<open>0</open>"""
# for rng in [100, 200, 300]:
# retstr += """\n\t\t<Placemark>\n\t\t\t<name>%inm</name>\n\t\t\t<styleUrl>#rangering</styleUrl>\n\t\t\t<LinearRing>\n\t\t\t\t<coordinates>%s</coordinates>\n\t\t\t</LinearRing>\n\t\t</Placemark>""" % (rng, self.draw_circle(self.my_coords, rng),)
# retstr += """\t</Folder>\n"""
# retstr += """\t<Folder>\n\t\t<name>Aircraft locations</name>\n\t\t<open>0</open>"""
#read the database and add KML
q = "select distinct icao from positions where seen > datetime('now', '-1 minute')"
c = self._db.cursor()
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:
icao = icao[0]
#now get metadata
q = "select ident, type from ident where icao=%i" % icao
self.locked_execute(c, q)
r = c.fetchall()
if len(r) != 0:
ident = r[0][0]
actype = r[0][1]
else:
ident=""
actype = ""
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
self.locked_execute(c, 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
q = "select lat, lon, alt from positions where icao=%i order by seen desc limit 1" % icao
self.locked_execute(c, q)
r = c.fetchall()
if len(r) != 0:
lat = r[0][0]
lon = r[0][1]
alt = r[0][2]
else:
lat = 0
lon = 0
alt = 0
highlight = 0
if hasattr(self, 'highlight'):
if self.highlight == icao:
highlight = 1
#now generate some JSONP
retstr+= """{"icao": "%.6x", "lat": %f, "lon": %f, "alt": %i, "hdg": %i, "speed": %i, "vertical": %i, "ident": "%s", "type": "%s", "highlight": %i},""" % (icao, lat, lon, alt, heading, speed, vertical, ident, actype, highlight)
retstr+= """]);"""
return retstr

150
python/mlat.py Executable file → Normal file
View File

@@ -55,7 +55,8 @@ wgs84_b2 = wgs84_b**2
#convert ECEF to lat/lon/alt without geoid correction
#returns alt in meters
def ecef2llh((x,y,z)):
def ecef2llh(ecef):
x, y, z = ecef
ep = math.sqrt((wgs84_a2 - wgs84_b2) / wgs84_b2)
p = math.sqrt(x**2+y**2)
th = math.atan2(wgs84_a*z, wgs84_b*p)
@@ -71,7 +72,8 @@ def ecef2llh((x,y,z)):
#convert lat/lon/alt coords to ECEF without geoid correction, WGS84 model
#remember that alt is in meters
def llh2ecef((lat, lon, alt)):
def llh2ecef(lla):
lat, lon, alt = lla
lat *= (math.pi / 180.0)
lon *= (math.pi / 180.0)
@@ -84,7 +86,8 @@ def llh2ecef((lat, lon, alt)):
return [x,y,z]
#do both of the above to get a geoid-corrected x,y,z position
def llh2geoid((lat, lon, alt)):
def llh2geoid(lla):
lat, lon, alt = lla
(x,y,z) = llh2ecef((lat, lon, alt + wgs84_height(lat, lon)))
return [x,y,z]
@@ -95,31 +98,28 @@ c = 299792458 / 1.0003 #modified for refractive index of air, why not
#we use limit as a goal to stop solving when we get "close enough" (error magnitude in meters for that iteration)
#basically 20 meters is way less than the anticipated error of the system so it doesn't make sense to continue
#it's possible this could fail in situations where the solution converges slowly
#THIS WORKS PLEASE DON'T MESS WITH IT
def mlat_iter(stations, prange_obs, guess = [0,0,0], limit = 20, maxrounds = 100):
xerr = [1e9, 1e9, 1e9, 1e9]
#TODO: this fails to converge for some seriously advantageous geometry
def mlat_iter(rel_stations, prange_obs, xguess = [0,0,0], limit = 20, maxrounds = 100):
xerr = [1e9, 1e9, 1e9]
rounds = 0
while numpy.linalg.norm(xerr[:3]) > limit:
#get p_i, the estimated pseudoranges based on the latest position guess
prange_est = [[numpy.linalg.norm(station - guess)] for station in stations]
#get the difference d_p^ between the observed and calculated pseudoranges
while numpy.linalg.norm(xerr) > limit:
prange_est = [[numpy.linalg.norm(station - xguess)] for station in rel_stations]
dphat = prange_obs - prange_est
#create a matrix of partial differentials to find the slope of the error in X,Y,Z,t directions
H = numpy.array([(numpy.array(-stations[row,:])+guess) / prange_est[row] for row in range(len(stations))])
H = numpy.append(H, numpy.ones(len(prange_obs)).reshape(len(prange_obs),1)*c, axis=1)
H = numpy.array([(numpy.array(-rel_stations[row,:])+xguess) / prange_est[row] for row in range(0,len(rel_stations))])
#now we have H, the Jacobian, and can solve for residual error
solved = numpy.linalg.lstsq(H, dphat)
xerr = solved[0].flatten()
guess += xerr[:3] #we ignore the time error for xguess
xerr = numpy.linalg.lstsq(H, dphat)[0].flatten()
xguess += xerr
#print xguess, xerr
rounds += 1
if rounds > maxrounds:
raise MlatNonConvergeError("Failed to converge!")
return (guess, xerr[3])
raise Exception("Failed to converge!")
break
return xguess
#func mlat:
#uses a modified GPS pseudorange solver to locate aircraft by multilateration.
#replies is a list of reports, in ([lat, lon, alt], timestamp) format
#altitude is the barometric altitude of the aircraft as returned by the aircraft, if any
#altitude is the barometric altitude of the aircraft as returned by the aircraft
#returns the estimated position of the aircraft in (lat, lon, alt) geoid-corrected WGS84.
#let's make it take a list of tuples so we can sort by them
def mlat(replies, altitude):
@@ -127,70 +127,76 @@ def mlat(replies, altitude):
stations = [sorted_reply[0] for sorted_reply in sorted_replies]
timestamps = [sorted_reply[1] for sorted_reply in sorted_replies]
nearest_llh = stations[0]
stations_xyz = numpy.array([llh2geoid(station) for station in stations])
#the absolute time shouldn't matter at all except perhaps in
#maintaining floating-point precision; in other words you can
#add a constant here and it should fall out in the solver as time error
prange_obs = [[c*stamp] for stamp in timestamps]
me_llh = stations[0]
me = llh2geoid(stations[0])
#if no alt, use a reasonably large number (in meters)
#since if all your stations lie in a plane (they basically will),
#there's a reasonable solution at negative altitude as well
if altitude is None:
altitude = 20000
firstguess = numpy.array(llh2ecef((nearest_llh[0], nearest_llh[1], altitude)))
prange_obs = numpy.array(prange_obs)
xyzpos, time_offset = mlat_iter(stations_xyz, prange_obs, firstguess, maxrounds=100)
llhpos = ecef2llh(xyzpos)
return (llhpos, time_offset)
#list of stations in XYZ relative to me
rel_stations = [numpy.array(llh2geoid(station)) - numpy.array(me) for station in stations[1:]]
rel_stations.append([0,0,0] - numpy.array(me))
rel_stations = numpy.array(rel_stations) #convert list of arrays to 2d array
#tests the mlat_iter algorithm using sample data from Farrell & Barth (p.147)
def farrell_barth_test():
pranges = numpy.array([[22228206.42],
[24096139.11],
[21729070.63],
[21259581.09]])
svpos = numpy.array([[7766188.44, -21960535.34, 12522838.56],
[-25922679.66, -6629461.28, 31864.37],
[-5743774.02, -25828319.92, 1692757.72],
[-2786005.69, -15900725.80, 21302003.49]])
#differentiate the timestamps to get TDOA, multiply by c to get pseudorange
prange_obs = [[c*(stamp-timestamps[0])] for stamp in timestamps[1:]]
#this is the "correct" resolved position, not the real receiver position
known_pos = numpy.array( [-2430745.0959362, -4702345.11359277, 3546568.70599656])
#so here we calc the estimated pseudorange to the center of the earth, using station[0] as a reference point for the geoid
#in other words, we say "if the aircraft were directly overhead of station[0], this is the prange to the center of the earth"
#this is a necessary approximation since we don't know the location of the aircraft yet
#if the dang earth were actually round this wouldn't be an issue
prange_obs.append( [numpy.linalg.norm(llh2ecef((me_llh[0], me_llh[1], altitude)))] ) #use ECEF not geoid since alt is MSL not GPS
prange_obs = numpy.array(prange_obs)
#xguess = llh2ecef([37.617175,-122.400843, 8000])-numpy.array(me)
#xguess = [0,0,0]
#start our guess directly overhead, who cares
xguess = numpy.array(llh2ecef([me_llh[0], me_llh[1], altitude])) - numpy.array(me)
xyzpos = mlat_iter(rel_stations, prange_obs, xguess)
llhpos = ecef2llh(xyzpos+me)
#now, we could return llhpos right now and be done with it.
#but the assumption we made above, namely that the aircraft is directly above the
#nearest station, results in significant error due to the oblateness of the Earth's geometry.
#so now we solve AGAIN, but this time with the corrected pseudorange of the aircraft altitude
#this might not be really useful in practice but the sim shows >50m errors without it
#and <4cm errors with it, not that we'll get that close in reality but hey let's do it right
prange_obs[-1] = [numpy.linalg.norm(llh2ecef((llhpos[0], llhpos[1], altitude)))]
xyzpos_corr = mlat_iter(rel_stations, prange_obs, xyzpos) #start off with a really close guess
llhpos = ecef2llh(xyzpos_corr+me)
#and now, what the hell, let's try to get dilution of precision data
#avec is the unit vector of relative ranges to the aircraft from each of the stations
# for i in range(len(avec)):
# avec[i] = numpy.array(avec[i]) / numpy.linalg.norm(numpy.array(avec[i]))
# numpy.append(avec, [[-1],[-1],[-1],[-1]], 1) #must be # of stations
# doparray = numpy.linalg.inv(avec.T*avec)
#the diagonal elements of doparray will be the x, y, z DOPs.
return llhpos
pos, time_offset = mlat_iter(svpos, pranges)
print "Position: ", pos
print "LLH: ", ecef2llh(pos)
error = numpy.linalg.norm(pos - known_pos)
print "Error: ", error
if error < 1e-3:
print "Farrell & Barth test OK"
else:
raise Exception("ERROR: Failed Farrell & Barth test")
if __name__ == '__main__':
#check to see that you haven't screwed up mlat_iter
farrell_barth_test()
#construct simulated returns from these stations
#here's some test data to validate the algorithm
teststations = [[37.76225, -122.44254, 100], [37.680016,-121.772461, 100], [37.385844,-122.083082, 100], [37.701207,-122.309418, 100]]
testalt = 8000
testplane = numpy.array(llh2ecef([37.617175,-122.400843, testalt]))
tx_time = 10 #time offset to apply to timestamps
teststamps = [tx_time+numpy.linalg.norm(testplane-numpy.array(llh2geoid(station))) / c for station in teststations]
testme = llh2geoid(teststations[0])
teststamps = [10,
10 + numpy.linalg.norm(testplane-numpy.array(llh2geoid(teststations[1]))) / c,
10 + numpy.linalg.norm(testplane-numpy.array(llh2geoid(teststations[2]))) / c,
10 + numpy.linalg.norm(testplane-numpy.array(llh2geoid(teststations[3]))) / c,
]
print "Actual pranges: ", sorted([numpy.linalg.norm(testplane - numpy.array(llh2geoid(station))) for station in teststations])
print(teststamps)
replies = [(station, stamp) for station,stamp in zip(teststations, teststamps)]
ans, offset = mlat(replies, testalt)
replies = []
for i in range(0, len(teststations)):
replies.append((teststations[i], teststamps[i]))
ans = mlat(replies, testalt)
error = numpy.linalg.norm(numpy.array(llh2ecef(ans))-numpy.array(testplane))
rng = numpy.linalg.norm(llh2geoid(ans)-numpy.array(llh2geoid(teststations[0])))
print "Resolved lat/lon/alt: ", ans
print "Error: %.2fm" % (error)
print "Range: %.2fkm (from first station in list)" % (rng/1000)
print "Aircraft-local transmit time: %.8fs" % (offset)
range = numpy.linalg.norm(llh2geoid(ans)-numpy.array(testme))
print(testplane-testme)
print(ans)
print("Error: %.2fm" % (error))
print("Range: %.2fkm (from first station in list)" % (range/1000))

87
python/mlat_client.py Normal file
View File

@@ -0,0 +1,87 @@
#!/usr/bin/env python
#
# Copyright 2012 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.
#
#multilateration client
#outputs stamps to server, receives multilaterated outputs back
import socket, pickle, time, sys
import air_modes
from gnuradio import gr
pickle_prot = 0
#pickle_prot = pickle.HIGHEST_PROTOCOL
class client_info:
def __init__(self):
self.name = ""
self.position = []
self.offset_secs = 0
self.offset_frac_secs = 0.0
self.time_source = None
class mlat_client:
def __init__(self, queue, position, server_addr, time_source):
self._queue = queue
self._pos = position
self._name = socket.gethostname()
#connect to server
self._sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._sock.setblocking(1)
self._sock.connect((server_addr, 19005))
info = client_info()
info.name = self._name
info.position = self._pos
info.time_source = time_source #"gpsdo" or None
self._sock.send(pickle.dumps(info))
reply = self._sock.recv(1024)
if reply != "HELO": #i know, shut up
raise Exception("Invalid reply from server: %s" % reply)
self._sock.setblocking(0)
self._remnant = None
def __del__(self):
self._sock.close()
#send a stamped report to the server
def output(self, message):
self._sock.send(message+"\n")
#this is called from the update() method list of the main app thread
def get_mlat_positions(self):
msg = None
try:
msg = self._sock.recv(1024)
except socket.error:
pass
if msg:
for line in msg.splitlines(True):
if line.endswith("\n"):
if self._remnant:
line = self._remnant + line
self._remnant = None
self._queue.insert_tail(gr.message_from_string(line))
else:
if self._remnant is not None:
raise Exception("Malformed data: " + line)
else:
self._remnant = line

107
python/modes_types.py Normal file
View File

@@ -0,0 +1,107 @@
#
# Copyright 2013 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.
#
from collections import namedtuple
#this is a timestamp that preserves precision when used with UTC timestamps.
#ordinary double-precision timestamps lose significant fractional precision
#when the exponent is as large as necessary for UTC.
class stamp:
def __init__(self, secs, frac_secs):
self.secs = secs
self.frac_secs = frac_secs
self.secs += int(self.frac_secs)
self.frac_secs -= int(self.frac_secs)
def __lt__(self, other):
if isinstance(other, self.__class__):
if self.secs == other.secs:
return self.frac_secs < other.frac_secs
else:
return self.secs < other.secs
elif isinstance(other, float):
return float(self) > other
else:
raise TypeError
def __gt__(self, other):
if type(other) is type(self):
if self.secs == other.secs:
return self.frac_secs > other.frac_secs
else:
return self.secs > other.secs
elif type(other) is type(float):
return float(self) > other
else:
raise TypeError
def __eq__(self, other):
if isinstance(other, self.__class__):
return self.secs == other.secs and self.frac_secs == other.frac_secs
elif isinstance(other, float):
return float(self) == other
else:
raise TypeError
def __ne__(self, other):
return not (self == other)
def __le__(self, other):
return (self == other) or (self < other)
def __ge__(self, other):
return (self == other) or (self > other)
def __add__(self, other):
if isinstance(other, self.__class__):
ipart = self.secs + other.secs
fpart = self.frac_secs + other.frac_secs
return stamp(ipart, fpart)
elif isinstance(other, float):
return self + stamp(0, other)
elif isinstance(other, int):
return self + stamp(other, 0)
else:
raise TypeError
def __sub__(self, other):
if isinstance(other, self.__class__):
ipart = self.secs - other.secs
fpart = self.frac_secs - other.frac_secs
return stamp(ipart, fpart)
elif isinstance(other, float):
return self - stamp(0, other)
elif isinstance(other, int):
return self - stamp(other, 0)
else:
raise TypeError
#to ensure we don't hash by stamp
#TODO fixme with a reasonable hash in case you feel like you'd hash by stamp
__hash__ = None
#good to within ms for comparison
def __float__(self):
return self.secs + self.frac_secs
def __str__(self):
return "%f" % float(self)
#a Mode S report including the modes_reply data object
modes_report = namedtuple('modes_report', ['data', 'ecc', 'rssi', 'timestamp'])
#lat, lon, alt
#TODO: a position class internally represented as ECEF XYZ which can easily be used for multilateration and distance calculation
llh = namedtuple('llh', ['lat', 'lon', 'alt'])
mlat_report = namedtuple('mlat_report', ['data', 'nreps', 'timestamp', 'llh', 'hdop', 'vdop'])

Some files were not shown because too many files have changed in this diff Show More