├── CMakeLists.txt ├── LICENSE ├── README.md ├── apps ├── CMakeLists.txt ├── ask_demod ├── fsk_demod ├── tpms_burst_ping └── tpms_rx ├── cmake ├── Modules │ ├── CMakeParseArgumentsCopy.cmake │ ├── FindCppUnit.cmake │ ├── FindGnuradioRuntime.cmake │ ├── GrMiscUtils.cmake │ ├── GrPlatform.cmake │ ├── GrPython.cmake │ ├── GrSwig.cmake │ ├── GrTest.cmake │ └── tpmsConfig.cmake └── cmake_uninstall.cmake.in ├── docs ├── CMakeLists.txt ├── README.tpms └── doxygen │ ├── CMakeLists.txt │ ├── Doxyfile.in │ ├── Doxyfile.swig_doc.in │ ├── doxyxml │ ├── __init__.py │ ├── base.py │ ├── doxyindex.py │ ├── generated │ │ ├── __init__.py │ │ ├── compound.py │ │ ├── compoundsuper.py │ │ ├── index.py │ │ └── indexsuper.py │ └── text.py │ ├── other │ ├── group_defs.dox │ └── main_page.dox │ └── swig_doc.py ├── examples └── README ├── grc ├── CMakeLists.txt ├── tpms_ask_env.xml ├── tpms_burst_detector.xml └── tpms_fixed_length_frame_sink.xml ├── include └── tpms │ ├── CMakeLists.txt │ ├── api.h │ ├── ask_env.h │ ├── burst_detector.h │ └── fixed_length_frame_sink.h ├── lib ├── CMakeLists.txt ├── ask_env_impl.cc ├── ask_env_impl.h ├── burst_detector_impl.cc ├── burst_detector_impl.h ├── fixed_length_frame_sink_impl.cc ├── fixed_length_frame_sink_impl.h ├── qa_ask_env.cc ├── qa_ask_env.h ├── qa_fixed_length_frame_sink.cc ├── qa_fixed_length_frame_sink.h ├── qa_tpms.cc ├── qa_tpms.h └── test_tpms.cc ├── python ├── CMakeLists.txt ├── __init__.py ├── ask.py ├── bit_coding.py ├── build_utils.py ├── build_utils_codes.py ├── decode.py ├── fsk.py ├── packet_check.py ├── qa_ask_env.py ├── qa_burst_detector.py ├── qa_fixed_length_frame_sink.py └── source.py └── swig ├── CMakeLists.txt └── tpms_swig.i /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Jared Boone . 2 | # 3 | # This is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 3, or (at your option) 6 | # any later version. 7 | # 8 | # This software is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this software; see the file COPYING. If not, write to 15 | # the Free Software Foundation, Inc., 51 Franklin Street, 16 | # Boston, MA 02110-1301, USA. 17 | 18 | ######################################################################## 19 | # Project setup 20 | ######################################################################## 21 | cmake_minimum_required(VERSION 2.6) 22 | project(gr-tpms CXX C) 23 | enable_testing() 24 | 25 | #select the release build type by default to get optimization flags 26 | if(NOT CMAKE_BUILD_TYPE) 27 | set(CMAKE_BUILD_TYPE "Release") 28 | message(STATUS "Build type not specified: defaulting to release.") 29 | endif(NOT CMAKE_BUILD_TYPE) 30 | set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") 31 | 32 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) 33 | 34 | ######################################################################## 35 | # Compiler specific setup 36 | ######################################################################## 37 | if(CMAKE_COMPILER_IS_GNUCXX AND NOT WIN32) 38 | #http://gcc.gnu.org/wiki/Visibility 39 | add_definitions(-fvisibility=hidden) 40 | endif() 41 | 42 | ######################################################################## 43 | # Find boost 44 | ######################################################################## 45 | if(UNIX AND EXISTS "/usr/lib64") 46 | list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix 47 | endif(UNIX AND EXISTS "/usr/lib64") 48 | set(Boost_ADDITIONAL_VERSIONS 49 | "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" 50 | "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" 51 | "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" 52 | "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" 53 | "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" 54 | "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" 55 | "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" 56 | ) 57 | find_package(Boost "1.35" COMPONENTS filesystem system) 58 | 59 | if(NOT Boost_FOUND) 60 | message(FATAL_ERROR "Boost required to compile tpms") 61 | endif() 62 | 63 | ######################################################################## 64 | # Install directories 65 | ######################################################################## 66 | include(GrPlatform) #define LIB_SUFFIX 67 | set(GR_RUNTIME_DIR bin) 68 | set(GR_LIBRARY_DIR lib${LIB_SUFFIX}) 69 | set(GR_INCLUDE_DIR include/tpms) 70 | set(GR_DATA_DIR share) 71 | set(GR_PKG_DATA_DIR ${GR_DATA_DIR}/${CMAKE_PROJECT_NAME}) 72 | set(GR_DOC_DIR ${GR_DATA_DIR}/doc) 73 | set(GR_PKG_DOC_DIR ${GR_DOC_DIR}/${CMAKE_PROJECT_NAME}) 74 | set(GR_CONF_DIR etc) 75 | set(GR_PKG_CONF_DIR ${GR_CONF_DIR}/${CMAKE_PROJECT_NAME}/conf.d) 76 | set(GR_LIBEXEC_DIR libexec) 77 | set(GR_PKG_LIBEXEC_DIR ${GR_LIBEXEC_DIR}/${CMAKE_PROJECT_NAME}) 78 | set(GRC_BLOCKS_DIR ${GR_PKG_DATA_DIR}/grc/blocks) 79 | 80 | ######################################################################## 81 | # Find gnuradio build dependencies 82 | ######################################################################## 83 | find_package(GnuradioRuntime) 84 | find_package(CppUnit) 85 | 86 | # To run a more advanced search for GNU Radio and it's components and 87 | # versions, use the following. Add any components required to the list 88 | # of GR_REQUIRED_COMPONENTS (in all caps) and change "version" to the 89 | # minimum API compatible version required. 90 | # 91 | set(GR_REQUIRED_COMPONENTS RUNTIME BLOCKS FILTER PMT VOLK) 92 | find_package(Gnuradio "3.7.2" REQUIRED) 93 | 94 | if(NOT GNURADIO_RUNTIME_FOUND) 95 | message(FATAL_ERROR "GnuRadio Runtime required to compile tpms") 96 | endif() 97 | if(NOT CPPUNIT_FOUND) 98 | message(FATAL_ERROR "CppUnit required to compile tpms") 99 | endif() 100 | 101 | ######################################################################## 102 | # Setup the include and linker paths 103 | ######################################################################## 104 | include_directories( 105 | ${CMAKE_SOURCE_DIR}/lib 106 | ${CMAKE_SOURCE_DIR}/include 107 | ${CMAKE_BINARY_DIR}/lib 108 | ${CMAKE_BINARY_DIR}/include 109 | ${Boost_INCLUDE_DIRS} 110 | ${CPPUNIT_INCLUDE_DIRS} 111 | ${GNURADIO_RUNTIME_INCLUDE_DIRS} 112 | ${GNURADIO_ALL_INCLUDE_DIRS} 113 | ) 114 | 115 | link_directories( 116 | ${Boost_LIBRARY_DIRS} 117 | ${CPPUNIT_LIBRARY_DIRS} 118 | ${GNURADIO_RUNTIME_LIBRARY_DIRS} 119 | ) 120 | 121 | # Set component parameters 122 | set(GR_TPMS_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE INTERNAL "" FORCE) 123 | set(GR_TPMS_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/swig CACHE INTERNAL "" FORCE) 124 | 125 | ######################################################################## 126 | # Create uninstall target 127 | ######################################################################## 128 | configure_file( 129 | ${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in 130 | ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake 131 | @ONLY) 132 | 133 | add_custom_target(uninstall 134 | ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake 135 | ) 136 | 137 | ######################################################################## 138 | # Add subdirectories 139 | ######################################################################## 140 | add_subdirectory(include/tpms) 141 | add_subdirectory(lib) 142 | add_subdirectory(swig) 143 | add_subdirectory(python) 144 | add_subdirectory(grc) 145 | add_subdirectory(apps) 146 | add_subdirectory(docs) 147 | 148 | ######################################################################## 149 | # Install cmake search helper for this library 150 | ######################################################################## 151 | install(FILES cmake/Modules/tpmsConfig.cmake 152 | DESTINATION lib/cmake/tpms 153 | ) 154 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gr-tpms 2 | 3 | Software for capturing, demodulating, decoding, and assessing data from 4 | automotive tire pressure monitors using GNU Radio. 5 | 6 | Tire pressure monitoring systems (TPMS) are becoming common on automobiles, 7 | and in certain countries, are required as a condition of sale. Most TPMS 8 | devices use simple wireless communication techniques such as: 9 | 10 | * ASK or FSK modulation 11 | * Manchester bit coding 12 | * Small CRCs or checksums 13 | * Unique device identifiers 14 | 15 | # Background 16 | 17 | For more background on this project, please watch Jared Boone's talk from ToorCon 15: 18 | 19 | "Dude, Where's My Car?: Reversing Tire Pressure Monitors with a Software Defined Radio" [[video](http://www.youtube.com/watch?v=bKqiq2Y43Wg)] [[slides](http://sharebrained.com/downloads/toorcon/dude_wheres_my_car_toorcon_sd_2013.pdf)] 20 | 21 | ...or this interview with Darren Kitchen of Hak5: 22 | 23 | "Hak5 1511 – Tracking Cars Wirelessly And Intercepting Femtocell Traffic" [[video](http://hak5.org/episodes/hak5-1511)] 24 | 25 | # Software 26 | 27 | Someone has contributed [a Docker container](https://registry.hub.docker.com/u/nobis99/gr-tpms/) as a quick way to bring up gr-tpms on your computer. 28 | 29 | This software was developed for and tested with: 30 | 31 | * [Python](http://python.org) 2.7 32 | * [FFTW](http://www.fftw.org) Fastest FFT in the West. 33 | * [GNU Radio](http://gnuradio.org) 3.7.3 (should work with earlier 3.7 releasese) 34 | * [rtl-sdr](http://sdr.osmocom.org/trac/wiki/rtl-sdr) 35 | * [gr-osmosdr](http://sdr.osmocom.org/trac/wiki/GrOsmoSDR) 36 | * [crcmod](http://crcmod.sourceforge.net), CRC library for Python. 37 | 38 | Optional dependencies (for tpms_burst_ping): 39 | 40 | * [Watchdog](http://packages.python.org/watchdog/) 0.6.0. Filesystem monitoring library for Python. 41 | * [portaudio](http://www.portaudio.com/) v19. Audio I/O abstraction library. 42 | * [pyaudio](http://people.csail.mit.edu/hubert/pyaudio/) 0.2.7. Python wrapper for portaudio. 43 | * [numpy](http://www.numpy.org) 1.8.0. Numerical Python library. 44 | 45 | # Hardware 46 | 47 | I used a variety of hardware for receiving tire pressure monitors. If you don't already 48 | have a software-defined radio receiver, a $50 US investment is all you need to get started. 49 | 50 | ### Quick Shopping List for The Impatient 51 | 52 | Aside from a modern and fairly fast computer capable of running GNU Radio, here's what you'll need: 53 | 54 | * [NooElec TV28T v2 DVB-T USB Stick (R820T) w/ Antenna and Remote Control](http://www.nooelec.com/store/software-defined-radio/tv28tv2.html) or [Hacker Warehouse DVB-T USB 2.0](http://hackerwarehouse.com/product/dvb-t-usb2-0/) 55 | * [NooElec Male MCX to Female SMA Adapter](http://www.nooelec.com/store/software-defined-radio/male-mcx-to-female-sma-adapter.html) 56 | * [Linx Technologies ANT-315-CW-RH-SMA 315MHz 51mm (2") helical whip antenna, SMA](http://mouser.com/Search/Refine.aspx?Keyword=ANT-315-CW-RH-SMA) or [Linx Technologies ANT-433-CW-RH-SMA 433MHz 51mm (2") helical whip antenna, SMA](http://mouser.com/Search/Refine.aspx?Keyword=ANT-433-CW-RH-SMA) 57 | * [Johnson / Emerson Connectivity Solutions 415-0031-024 SMA male to SMA female cable, 61cm (24")](http://mouser.com/Search/Refine.aspx?Keyword=415-0031-024), (Optional, if you don't want your antenna sticking straight out of your USB receiver dongle.) 58 | 59 | ### Receiver 60 | 61 | If you're just getting started with SDR, I highly recommend getting a DVB-T USB dongle, 62 | supported by the [rtl-sdr](http://sdr.osmocom.org/trac/wiki/rtl-sdr) project. They cost 63 | $25 US, typically. 64 | 65 | Recommended DVB-T dongle vendors include: 66 | 67 | * [Hacker Warehouse](http://hackerwarehouse.com/product/dvb-t-usb2-0/) 68 | * [NooElec](http://www.nooelec.com/store/software-defined-radio.html) 69 | 70 | If you're looking to do active attacks on TPMS (a topic I haven't explored), I recommend 71 | the [HackRF](https://github.com/mossmann/hackrf/). 72 | 73 | ### Antenna 74 | 75 | The antenna that comes with your DVB-T dongle will work well, but you'll get more signal 76 | and less noise with a band-specific antenna. 77 | 78 | For 315MHz: 79 | * Linx Technologies [ANT-315-CW-RH-SMA](http://mouser.com/Search/Refine.aspx?Keyword=ANT-315-CW-RH-SMA) 315MHz 51mm (2") helical whip antenna, SMA. 80 | * Linx Technologies [ANT-315-CW-RH](http://mouser.com/Search/Refine.aspx?Keyword=ANT-315-CW-RH) 315MHz 51mm (2") helical whip antenna, RP-SMA. 81 | * Linx Technologies [ANT-315-CW-HWR-SMA](http://mouser.com/Search/Refine.aspx?Keyword=ANT-315-CW-HWR-SMA) 315MHz 142mm (5.6") tilt/swivel whip antenna, SMA. 82 | * Linx Technologies [ANT-315-CW-HWR-RPS](http://mouser.com/Search/Refine.aspx?Keyword=ANT-315-CW-HWR-RPS) 315MHz 142mm (5.6") tilt/swivel whip antenna, RP-SMA. 83 | 84 | For 433MHz: 85 | * Linx Technologies [ANT-433-CW-RH-SMA](http://mouser.com/Search/Refine.aspx?Keyword=ANT-433-CW-RH-SMA) 433MHz 51mm (2") helical whip antenna, SMA. 86 | * Linx Technologies [ANT-433-CW-RH](http://mouser.com/Search/Refine.aspx?Keyword=ANT-433-CW-RH) 433MHz 51mm (2") helical whip antenna, RP-SMA. 87 | * Linx Technologies [ANT-433-CW-HWR-SMA](http://mouser.com/Search/Refine.aspx?Keyword=ANT-433-CW-HWR-SMA) 433MHz 142mm (5.6") tilt/swivel whip antenna, SMA. 88 | * Linx Technologies [ANT-433-CW-HWR-RPS](http://mouser.com/Search/Refine.aspx?Keyword=ANT-433-CW-HWR-RPS) 433MHz 142mm (5.6") tilt/swivel whip antenna, RP-SMA. 89 | 90 | I'm using the Linx Technologies ANT-315-CW-RH-SMA and ANT-433-CW-RH-SMA with good 91 | results, but you may prefer bigger antennas, or RP-SMA connectors. 92 | 93 | Ideally, I'd build a [Yagi-Uda antenna](http://en.wikipedia.org/wiki/Yagi-Uda_antenna). :-) 94 | 95 | ### Cabling 96 | 97 | You'll need a cable to connect the antenna to the DVB-T dongle. The DVB-T dongles 98 | from Hacker Warehouse and NooElec have a female MCX connector. The SMA antennas I 99 | use have a male SMA connector. So you'll want a 50 Ohm cable with a male MCX 100 | connector on one side, and a female SMA connector on the other. 101 | 102 | ### Filtering 103 | 104 | I like to use a SAW filter between the antenna and receiver to further cut noise 105 | and interference. It's certainly not necessary (and likely overkill). The SAW 106 | filter I use is built from a PCB I designed. 107 | 108 | # Building 109 | 110 | Assuming you have the above prerequisites installed, clone this repo and do the 111 | following: 112 | 113 | cd gr-tpms 114 | mkdir build 115 | cd build 116 | cmake .. 117 | make 118 | sudo make install 119 | sudo ldconfig # only if you're using Linux 120 | 121 | If you have trouble building or running gr-tpms, check that you have all the 122 | dependencies installed. Also, review your CMake configuration by running 123 | "ccmake .." instead of "cmake .." and reviewing the "advanced mode" options. 124 | For example, using MacPorts on Mac OS X 10.9, I've had to adjust 125 | PYTHON_INCLUDE_DIR and PYTHON_LIBRARY to start with "/opt/local" instead of the 126 | detected path. 127 | 128 | If you're using Linux, and run into a SWIG exception thrown by Python, 129 | metioning _tpms_swig, swig_import_helper, load_module, etc., you may have 130 | missed running ldconfig. 131 | 132 | # Using 133 | 134 | Once gr-tpms is installed, you may use it in two modes, a live capture mode, or 135 | decoding from a file. To run live using an RTL-SDR dongle as a signal source: 136 | 137 | tpms_rx --source rtlsdr --if-rate 400000 --tuned-frequency 315000000 138 | 139 | Or using a HackRF: 140 | 141 | tpms_rx --source hackrf --if-rate 400000 --tuned-frequency 315000000 142 | 143 | To detect and decode from a file: 144 | 145 | tpms_rx --if-rate 400000 --file file sampled at 400kHz> 146 | 147 | Optional arguments to tpms_rx include: 148 | 149 | --bursts: Output a complex file of each burst of significant energy. 150 | --raw: Output undecoded bits of detected packets. 151 | 152 | While gr-tpms is running, you should see a line printed for each TPMS transmission 153 | that gr-tpms can successfully detect, demodulate, and decode. There are several 154 | modulation and encoding schemes that gr-tpms can handle, but certainly many 155 | remain to be observed and reversed. The burst option is handy for capturing raw 156 | baseband data for packets that gr-tpms doesn't recognize. 157 | 158 | The output of gr-tpms is the recovered contents (bits) of the received packets. 159 | At the moment, you're on your own as far as figuring out which bits represent a 160 | sensor's unique identifier, tire pressure or temperature, and checksum or CRC field. 161 | As you accumulate more raw packets, you can observe statistics which indicate 162 | the nature of bits in the many different types of packets. For more information on 163 | how this is done, see my ToorCon talk (linked above) and check out my sister 164 | project, [tpms](https://github.com/jboone/tpms), which contains some visualization 165 | and testing tools and techniques. 166 | 167 | If you want to drive around, listening for TPMS signals, I recommend using the 168 | optional tpms_burst_ping utility. It takes a single, optional argument that 169 | specifies a directory to watch for --burst files to appear. When a file appears 170 | (a burst occurs), tpms_burst_ping will emit a "ping" through your audio output. 171 | 172 | tpms_burst_ping . 173 | 174 | # License 175 | 176 | The associated software is provided under a GPLv2 license: 177 | 178 | Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc. 179 | 180 | This program is free software; you can redistribute it and/or 181 | modify it under the terms of the GNU General Public License 182 | as published by the Free Software Foundation; either version 2 183 | of the License, or (at your option) any later version. 184 | 185 | This program is distributed in the hope that it will be useful, 186 | but WITHOUT ANY WARRANTY; without even the implied warranty of 187 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 188 | GNU General Public License for more details. 189 | 190 | You should have received a copy of the GNU General Public License 191 | along with this program; if not, write to the Free Software 192 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 193 | 02110-1301, USA. 194 | 195 | # Contact 196 | 197 | Jared Boone 198 | 199 | ShareBrained Technology, Inc. 200 | 201 | 202 | 203 | 204 | The latest version of this repository can be found at 205 | https://github.com/jboone/gr-tpms 206 | -------------------------------------------------------------------------------- /apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Jared Boone . 2 | # 3 | # This is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 3, or (at your option) 6 | # any later version. 7 | # 8 | # This software is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this software; see the file COPYING. If not, write to 15 | # the Free Software Foundation, Inc., 51 Franklin Street, 16 | # Boston, MA 02110-1301, USA. 17 | 18 | include(GrPython) 19 | 20 | GR_PYTHON_INSTALL( 21 | PROGRAMS 22 | ask_demod 23 | fsk_demod 24 | tpms_burst_ping 25 | tpms_rx 26 | DESTINATION bin 27 | ) 28 | -------------------------------------------------------------------------------- /apps/ask_demod: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2014 Jared Boone 4 | # 5 | # This file is part of gr-tpms. 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; see the file COPYING. If not, write to 19 | # the Free Software Foundation, Inc., 51 Franklin Street, 20 | # Boston, MA 02110-1301, USA. 21 | # 22 | 23 | import math 24 | 25 | from gnuradio import blocks 26 | from gnuradio import digital 27 | from gnuradio import gr 28 | 29 | from gnuradio import filter 30 | from gnuradio.filter import firdes 31 | 32 | import tpms 33 | 34 | from tpms.source import source_file 35 | from tpms.ask import ask_channel_filter 36 | from tpms.decode import clock_recovery, tag_print 37 | 38 | class top_block(gr.top_block): 39 | def __init__(self, args): 40 | super(top_block, self).__init__( 41 | "top_block" 42 | ) 43 | 44 | sample_rate = args.sample_rate 45 | offset = args.offset 46 | channel_bandwidth = args.channel_bandwidth 47 | symbol_rate = args.symbol_rate 48 | access_code = args.access_code 49 | 50 | nyquist = sample_rate / 2.0 51 | 52 | self.source = source_file(args.file) 53 | 54 | # Temporary work-around for poor structuring of TPMS library code 55 | # Moving the channel filter *before* conversion to magnitude. 56 | filter_attenuation_db = 40 57 | filter_cutoff = channel_bandwidth / 2.0 58 | filter_transition = filter_cutoff * 0.2 59 | if (filter_cutoff + filter_transition) > nyquist: 60 | raise RuntimeError('ASK channel filter exceeds Nyquist frequency') 61 | 62 | filter_taps = firdes.low_pass_2(1.0, sample_rate, filter_cutoff, filter_transition, filter_attenuation_db) 63 | print(len(filter_taps)) 64 | #self.filter = filter.freq_xlating_fir_filter_ccf(1, (filter_taps), offset, sample_rate) 65 | #self.connect((self.source, 0), (self.filter, 0)) 66 | self.rotator = blocks.rotator_cc(float(-offset) / sample_rate * 2.0 * math.pi) 67 | self.connect((self.source, 0), (self.rotator, 0)) 68 | self.filter = filter.fft_filter_ccf(1, (filter_taps)) 69 | self.connect((self.rotator, 0), (self.filter, 0)) 70 | 71 | self.complex_to_mag = blocks.complex_to_mag(1) 72 | self.connect((self.filter, 0), (self.complex_to_mag, 0)) 73 | 74 | self.envelope = tpms.ask_env(alpha=0.02) 75 | self.connect((self.complex_to_mag, 0), (self.envelope, 0)) 76 | 77 | self.clock_recovery = clock_recovery(sample_rate, symbol_rate) 78 | self.connect((self.envelope, 0), (self.clock_recovery, 0)) 79 | 80 | self.correlator = digital.correlate_access_code_tag_bb(access_code, 0, "preamble") 81 | self.connect((self.clock_recovery, 0), (self.correlator, 0)) 82 | 83 | self.tag_print = tag_print(args.symbol_rate, args.length) 84 | self.connect((self.correlator, 0), (self.tag_print, 0)) 85 | 86 | def main(): 87 | from argparse import ArgumentParser 88 | 89 | parser = ArgumentParser() 90 | parser.add_argument('-f', '--file', type=str, default=None, help="Input file path") 91 | parser.add_argument('-r', '--sample-rate', type=float, default=None, help="Sample rate") 92 | parser.add_argument('-o', '--offset', type=float, default=0, help="Carrier offset from 0 Hz") 93 | parser.add_argument('-c', '--channel-bandwidth', type=float, default=0, help="Channel filter bandwidth") 94 | parser.add_argument('-s', '--symbol-rate', type=float, default=None, help="Symbol rate") 95 | parser.add_argument('-a', '--access-code', type=str, default=None, help="Access code") 96 | parser.add_argument('-l', '--length', type=int, default=None, help="Number of bits to dump after detected access code") 97 | args = parser.parse_args() 98 | 99 | tb = top_block(args) 100 | tb.start() 101 | 102 | try: 103 | tb.wait() 104 | except KeyboardInterrupt: 105 | tb.stop() 106 | 107 | if __name__ == '__main__': 108 | main() 109 | -------------------------------------------------------------------------------- /apps/fsk_demod: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2014 Jared Boone 4 | # 5 | # This file is part of gr-tpms. 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; see the file COPYING. If not, write to 19 | # the Free Software Foundation, Inc., 51 Franklin Street, 20 | # Boston, MA 02110-1301, USA. 21 | # 22 | 23 | import numpy 24 | 25 | from gnuradio import blocks 26 | from gnuradio import digital 27 | from gnuradio import gr 28 | 29 | from tpms.source import source_file 30 | from tpms.fsk import fsk_demodulator 31 | from tpms.decode import clock_recovery, tag_print 32 | 33 | class top_block(gr.top_block): 34 | def __init__(self, args): 35 | super(top_block, self).__init__( 36 | "top_block" 37 | ) 38 | 39 | self.source = source_file(args.file) 40 | 41 | self.demodulator = fsk_demodulator(args.sample_rate, args.offset, args.deviation, 1, args.symbol_rate) 42 | self.connect((self.source, 0), (self.demodulator, 0)) 43 | 44 | self.clock_recovery = clock_recovery(args.sample_rate, args.symbol_rate) 45 | self.connect((self.demodulator, 0), (self.clock_recovery, 0)) 46 | 47 | self.correlator = digital.correlate_access_code_tag_bb(args.access_code, 0, "preamble") 48 | self.connect((self.clock_recovery, 0), (self.correlator, 0)) 49 | 50 | self.tag_print = tag_print(args.symbol_rate, args.length) 51 | self.connect((self.correlator, 0), (self.tag_print, 0)) 52 | 53 | def main(): 54 | from argparse import ArgumentParser 55 | 56 | parser = ArgumentParser() 57 | parser.add_argument('-f', '--file', type=str, default=None, help="Input file path") 58 | parser.add_argument('-r', '--sample-rate', type=float, default=None, help="Sample rate") 59 | parser.add_argument('-o', '--offset', type=float, default=0, help="Carrier offset from 0 Hz") 60 | parser.add_argument('-d', '--deviation', type=float, default=None, help="Deviation above/below carrier") 61 | parser.add_argument('-s', '--symbol-rate', type=float, default=None, help="Symbol rate") 62 | parser.add_argument('-a', '--access-code', type=str, default=None, help="Access code") 63 | parser.add_argument('-l', '--length', type=int, default=None, help="Number of bits to dump after detected access code") 64 | args = parser.parse_args() 65 | 66 | tb = top_block(args) 67 | tb.start() 68 | 69 | try: 70 | tb.wait() 71 | except KeyboardInterrupt: 72 | tb.stop() 73 | 74 | if __name__ == '__main__': 75 | main() 76 | -------------------------------------------------------------------------------- /apps/tpms_burst_ping: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2014 Jared Boone 4 | # 5 | # This file is part of gr-tpms. 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; see the file COPYING. If not, write to 19 | # the Free Software Foundation, Inc., 51 Franklin Street, 20 | # Boston, MA 02110-1301, USA. 21 | # 22 | 23 | import sys 24 | import time 25 | import numpy 26 | 27 | import pyaudio 28 | 29 | from watchdog.observers import Observer 30 | from watchdog.events import FileSystemEventHandler, EVENT_TYPE_CREATED 31 | 32 | audio_sampling_rate = 48000 33 | ping_hz = 4000 34 | ping_length = 0.2 35 | t = numpy.arange(0, ping_length * audio_sampling_rate) / audio_sampling_rate 36 | w = t * ping_hz * numpy.pi / 2.0 37 | ping_sound = numpy.sin(w) 38 | ping_envelope = numpy.power(10, ((t / ping_length) * -100) / 20.0) 39 | ping_sound *= ping_envelope 40 | ping_sound = numpy.array(ping_sound, dtype=numpy.float32) 41 | 42 | audio_out = pyaudio.PyAudio() 43 | stream = audio_out.open(format=pyaudio.paFloat32, channels=1, rate=48000, output=True) 44 | 45 | class PlayBurstEventHandler(FileSystemEventHandler): 46 | def on_created(self, event): 47 | if event.event_type == EVENT_TYPE_CREATED and \ 48 | event.is_directory == False and \ 49 | event.src_path.endswith(".dat"): 50 | print(event.src_path) 51 | stream.write(ping_sound) 52 | 53 | event_handler = PlayBurstEventHandler() 54 | 55 | if len(sys.argv) > 1: 56 | watch_path = sys.argv[1] 57 | else: 58 | watch_path = '.' 59 | 60 | observer = Observer() 61 | observer.schedule(event_handler, watch_path, recursive=True) 62 | observer.start() 63 | 64 | try: 65 | while True: 66 | time.sleep(1) 67 | except KeyboardInterrupt: 68 | observer.stop() 69 | observer.join() 70 | -------------------------------------------------------------------------------- /apps/tpms_rx: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2014 Jared Boone 4 | # 5 | # This file is part of gr-tpms. 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; see the file COPYING. If not, write to 19 | # the Free Software Foundation, Inc., 51 Franklin Street, 20 | # Boston, MA 02110-1301, USA. 21 | # 22 | 23 | import time 24 | 25 | from math import floor, ceil, sqrt 26 | 27 | from argparse import ArgumentParser 28 | 29 | from gnuradio import analog 30 | from gnuradio import blocks 31 | from gnuradio import digital 32 | from gnuradio import gr 33 | from gnuradio import filter 34 | from gnuradio.filter import firdes 35 | import pmt 36 | 37 | import tpms 38 | 39 | from tpms.packet_check import packet_decode 40 | 41 | from tpms.source import source_hackrf, source_rtlsdr, source_file 42 | from tpms.ask import ask_channel_filter 43 | from tpms.fsk import fsk_center_tracking, fsk_demodulator 44 | from tpms.decode import clock_recovery 45 | 46 | class packet_sink(gr.basic_block): 47 | def __init__(self, output_raw=False): 48 | super(packet_sink, self).__init__( 49 | "packet_handler", 50 | in_sig=None, 51 | out_sig=None, 52 | ) 53 | self.output_raw = output_raw 54 | 55 | port = pmt.intern("packet_sink") 56 | self.message_port_register_in(port) 57 | 58 | self.set_msg_handler(port, self._handle) 59 | 60 | def _handle(self, message): 61 | d = pmt.to_python(message) 62 | data_ref = pmt.dict_ref(message, pmt.intern("data"), pmt.PMT_NIL) 63 | data = pmt.u8vector_elements(data_ref) 64 | data_s = ''.join([str(c) for c in data]) 65 | d['data'] = data_s 66 | packet_decode(d, output_raw=self.output_raw) 67 | 68 | def queue_handler(queue): 69 | def queue_handler_fn(): 70 | while True: 71 | message = queue.delete_head() 72 | print('message') 73 | return queue_handler_fn 74 | 75 | class demodulate_ask(gr.hier_block2): 76 | def __init__(self, if_sampling_rate, packet_sink): 77 | super(demodulate_ask, self).__init__( 78 | "demodulate_ask", 79 | gr.io_signature(1, 1, gr.sizeof_gr_complex*1), 80 | gr.io_signature(0, 0, 0), 81 | ) 82 | 83 | self.complex_to_mag = blocks.complex_to_mag(1) 84 | self.connect((self, 0), (self.complex_to_mag, 0)) 85 | 86 | filter_output = False 87 | bits_output = False 88 | cor_output = False 89 | 90 | configuration = ( 91 | ( 92 | 4667, 40, ( 93 | (4040, ('110011001100110011001011',)), 94 | (4667, ('10101010101010101010101010101001',)), 95 | ), 96 | ), ( 97 | 8400, 20, ( 98 | (8157, ('111101010101010101010101010101011110',)), 99 | (8400, ('1010101010101010101010101010110',)), 100 | ), 101 | ), ( 102 | 10000, 16, ( 103 | (10000, ('11001111101010101001',)), 104 | ), 105 | ), 106 | ) 107 | 108 | for filter_rate, decimation, symbol_rates in configuration: 109 | input_rate = float(if_sampling_rate) / decimation 110 | 111 | channel_filter = ask_channel_filter(if_sampling_rate, decimation=decimation, symbol_rate=filter_rate) 112 | self.connect((self.complex_to_mag, 0), (channel_filter, 0)) 113 | 114 | if filter_output: 115 | input_rate_s = ('%.3f' % (input_rate / 1e3)).replace('.', 'k') 116 | filename = 'lpf_ask_%s.i16' % (input_rate_s,) 117 | float_to_short = blocks.float_to_short(1, 32767 * sqrt(2) / 2 / 2) 118 | self.connect((channel_filter, 0), (float_to_short, 0)) 119 | file_sink_filter = blocks.file_sink(gr.sizeof_short*1, filename, False) 120 | self.connect((float_to_short, 0), (file_sink_filter, 0)) 121 | 122 | for symbol_rate, access_codes in symbol_rates: 123 | if not isinstance(access_codes, tuple): 124 | raise RuntimeError('Access code formatting error') 125 | 126 | reclock = clock_recovery(input_rate, symbol_rate) 127 | self.connect((channel_filter, 0), (reclock, 0)) 128 | 129 | if bits_output: 130 | input_rate_s = ('%.3f' % (input_rate / 1e3)).replace('.', 'k') 131 | symbol_rate_s = ('%.3f' % (symbol_rate / 1e3)).replace('.', 'k') 132 | filename = 'bit_ask_%s_%s.i8' % (input_rate_s, symbol_rate_s) 133 | file_sink_bits = blocks.file_sink(gr.sizeof_char*1, filename, False) 134 | self.connect((reclock, 0), (file_sink_bits, 0)) 135 | 136 | for access_code in access_codes: 137 | correlator = digital.correlate_access_code_bb(access_code, 0) 138 | self.connect((reclock, 0), (correlator, 0)) 139 | 140 | attributes = pmt.make_dict() 141 | attributes = pmt.dict_add(attributes, pmt.intern("modulation"), pmt.intern("ask")) 142 | attributes = pmt.dict_add(attributes, pmt.intern("access_code"), pmt.intern(access_code)) 143 | attributes = pmt.dict_add(attributes, pmt.intern("symbol_rate"), pmt.from_long(int(round(symbol_rate)))) 144 | 145 | frame_sink = tpms.fixed_length_frame_sink(256, attributes) 146 | self.connect((correlator, 0), (frame_sink, 0)) 147 | 148 | self.msg_connect(frame_sink, "packet_source", packet_sink, "packet_sink") 149 | 150 | if cor_output: 151 | input_rate_s = ('%.3f' % (input_rate / 1e3)).replace('.', 'k') 152 | symbol_rate_s = ('%.3f' % (symbol_rate / 1e3)).replace('.', 'k') 153 | filename = 'cor_ask_%s_%s_%s.i8' % (input_rate_s, symbol_rate_s, access_code) 154 | file_sink_correlator = blocks.file_sink(gr.sizeof_char*1, filename, False) 155 | self.connect((correlator, 0), (file_sink_correlator, 0)) 156 | 157 | class demodulate_fsk(gr.hier_block2): 158 | def __init__(self, if_sampling_rate, packet_sink): 159 | super(demodulate_fsk, self).__init__( 160 | "demodulate_fsk", 161 | gr.io_signature(1, 1, gr.sizeof_gr_complex*1), 162 | gr.io_signature(0, 0, 0), 163 | ) 164 | 165 | self.center_tracking = fsk_center_tracking(if_sampling_rate) 166 | self.connect((self, 0), (self.center_tracking, 0)) 167 | 168 | configuration = ( 169 | (38400, 10, 19200, ( 170 | (19220, ('01010101010101010101010101010110',)), 171 | )), 172 | (35000, 10, 20000, ( 173 | (19440, ('01010101010101010101010101010110',)), 174 | )), 175 | (30000, 10, 20000, ( 176 | (19250, ('00110011001100110011001100110011010',)), 177 | (19950, ('110110101110001',)), 178 | (20480, ('110110101110001',)), 179 | )), 180 | (20000, 10, 20000, ( 181 | (19224, ('1010101010101010101010101010100110',)), 182 | )), 183 | (40000, 10, 10000, ( 184 | (9910, ('00111111001',)), 185 | )), 186 | (25000, 8, 20000, ( 187 | (20040, ('010101010011110',)), 188 | )), 189 | ) 190 | 191 | for deviation, decimation, channel_rate, symbol_rates in configuration: 192 | input_rate = float(if_sampling_rate) / decimation 193 | 194 | demodulator = fsk_demodulator(if_sampling_rate, 0, deviation, decimation, channel_rate) 195 | self.connect((self.center_tracking, 0), (demodulator, 0)) 196 | 197 | for symbol_rate, access_codes in symbol_rates: 198 | if not isinstance(access_codes, tuple): 199 | raise RuntimeError('Access code formatting error') 200 | 201 | reclock = clock_recovery(input_rate, symbol_rate) 202 | self.connect((demodulator, 0), (reclock, 0)) 203 | 204 | for access_code in access_codes: 205 | correlator = digital.correlate_access_code_bb(access_code, 0) 206 | self.connect((reclock, 0), (correlator, 0)) 207 | 208 | attributes = pmt.make_dict() 209 | attributes = pmt.dict_add(attributes, pmt.intern("modulation"), pmt.intern("fsk")) 210 | attributes = pmt.dict_add(attributes, pmt.intern("deviation"), pmt.from_long(int(round(deviation)))) 211 | attributes = pmt.dict_add(attributes, pmt.intern("access_code"), pmt.intern(access_code)) 212 | attributes = pmt.dict_add(attributes, pmt.intern("symbol_rate"), pmt.from_long(int(round(symbol_rate)))) 213 | 214 | frame_sink = tpms.fixed_length_frame_sink(256, attributes) 215 | self.connect((correlator, 0), (frame_sink, 0)) 216 | 217 | self.msg_connect(frame_sink, "packet_source", packet_sink, "packet_sink") 218 | 219 | class top_block(gr.top_block): 220 | def __init__(self, source, args): 221 | super(top_block, self).__init__( 222 | "top_block" 223 | ) 224 | 225 | if source == 'hackrf': 226 | self.source = source_hackrf(args.tuned_frequency, args.if_rate) 227 | elif source == 'rtlsdr': 228 | self.source = source_rtlsdr(args.tuned_frequency, args.if_rate) 229 | elif source == 'file': 230 | self.source = source_file(args.file) 231 | else: 232 | raise RuntimeError('No source specified') 233 | 234 | if args.bursts: 235 | self.burst_detector = tpms.burst_detector() 236 | 237 | self.burst_file_sink = blocks.tagged_file_sink(gr.sizeof_gr_complex*1, args.if_rate) 238 | self.connect((self.source, 0), (self.burst_detector, 0)) 239 | self.connect((self.burst_detector, 0), (self.burst_file_sink, 0)) 240 | 241 | self.packet_sink = packet_sink(output_raw=args.raw) 242 | 243 | self.demodulate_ask = demodulate_ask(args.if_rate, self.packet_sink) 244 | self.connect((self.source, 0), (self.demodulate_ask, 0)) 245 | 246 | self.demodulate_fsk = demodulate_fsk(args.if_rate, self.packet_sink) 247 | self.connect((self.source, 0), (self.demodulate_fsk, 0)) 248 | 249 | def main(): 250 | parser = ArgumentParser() 251 | parser.add_argument('-f', '--file', type=str, default=None, help="Input file path for offline processing") 252 | parser.add_argument('-s', '--source', type=str, default=None, help="Source for live baseband data (hackrf, rtlsdr)") 253 | parser.add_argument('-b', '--bursts', action="store_true", help="Save bursts of significant energy to separate files") 254 | parser.add_argument('-r', '--raw', action="store_true", help="Include raw (undecoded) bits in output") 255 | parser.add_argument('-i', '--if-rate', type=float, default=None, help="IF sampling rate") 256 | parser.add_argument('-t', '--tuned-frequency', type=float, default=None, help="Receiver source center frequency") 257 | args = parser.parse_args() 258 | 259 | if args.file: 260 | source = 'file' 261 | else: 262 | source = args.source 263 | 264 | tb = top_block(source, args) 265 | tb.start() 266 | 267 | try: 268 | tb.wait() 269 | except KeyboardInterrupt: 270 | tb.stop() 271 | 272 | if __name__ == '__main__': 273 | main() 274 | -------------------------------------------------------------------------------- /cmake/Modules/CMakeParseArgumentsCopy.cmake: -------------------------------------------------------------------------------- 1 | # CMAKE_PARSE_ARGUMENTS( args...) 2 | # 3 | # CMAKE_PARSE_ARGUMENTS() is intended to be used in macros or functions for 4 | # parsing the arguments given to that macro or function. 5 | # It processes the arguments and defines a set of variables which hold the 6 | # values of the respective options. 7 | # 8 | # The argument contains all options for the respective macro, 9 | # i.e. keywords which can be used when calling the macro without any value 10 | # following, like e.g. the OPTIONAL keyword of the install() command. 11 | # 12 | # The argument contains all keywords for this macro 13 | # which are followed by one value, like e.g. DESTINATION keyword of the 14 | # install() command. 15 | # 16 | # The argument contains all keywords for this macro 17 | # which can be followed by more than one value, like e.g. the TARGETS or 18 | # FILES keywords of the install() command. 19 | # 20 | # When done, CMAKE_PARSE_ARGUMENTS() will have defined for each of the 21 | # keywords listed in , and 22 | # a variable composed of the given 23 | # followed by "_" and the name of the respective keyword. 24 | # These variables will then hold the respective value from the argument list. 25 | # For the keywords this will be TRUE or FALSE. 26 | # 27 | # All remaining arguments are collected in a variable 28 | # _UNPARSED_ARGUMENTS, this can be checked afterwards to see whether 29 | # your macro was called with unrecognized parameters. 30 | # 31 | # As an example here a my_install() macro, which takes similar arguments as the 32 | # real install() command: 33 | # 34 | # function(MY_INSTALL) 35 | # set(options OPTIONAL FAST) 36 | # set(oneValueArgs DESTINATION RENAME) 37 | # set(multiValueArgs TARGETS CONFIGURATIONS) 38 | # cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) 39 | # ... 40 | # 41 | # Assume my_install() has been called like this: 42 | # my_install(TARGETS foo bar DESTINATION bin OPTIONAL blub) 43 | # 44 | # After the cmake_parse_arguments() call the macro will have set the following 45 | # variables: 46 | # MY_INSTALL_OPTIONAL = TRUE 47 | # MY_INSTALL_FAST = FALSE (this option was not used when calling my_install() 48 | # MY_INSTALL_DESTINATION = "bin" 49 | # MY_INSTALL_RENAME = "" (was not used) 50 | # MY_INSTALL_TARGETS = "foo;bar" 51 | # MY_INSTALL_CONFIGURATIONS = "" (was not used) 52 | # MY_INSTALL_UNPARSED_ARGUMENTS = "blub" (no value expected after "OPTIONAL" 53 | # 54 | # You can the continue and process these variables. 55 | # 56 | # Keywords terminate lists of values, e.g. if directly after a one_value_keyword 57 | # another recognized keyword follows, this is interpreted as the beginning of 58 | # the new option. 59 | # E.g. my_install(TARGETS foo DESTINATION OPTIONAL) would result in 60 | # MY_INSTALL_DESTINATION set to "OPTIONAL", but MY_INSTALL_DESTINATION would 61 | # be empty and MY_INSTALL_OPTIONAL would be set to TRUE therefor. 62 | 63 | #============================================================================= 64 | # Copyright 2010 Alexander Neundorf 65 | # 66 | # Distributed under the OSI-approved BSD License (the "License"); 67 | # see accompanying file Copyright.txt for details. 68 | # 69 | # This software is distributed WITHOUT ANY WARRANTY; without even the 70 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 71 | # See the License for more information. 72 | #============================================================================= 73 | # (To distribute this file outside of CMake, substitute the full 74 | # License text for the above reference.) 75 | 76 | 77 | if(__CMAKE_PARSE_ARGUMENTS_INCLUDED) 78 | return() 79 | endif() 80 | set(__CMAKE_PARSE_ARGUMENTS_INCLUDED TRUE) 81 | 82 | 83 | function(CMAKE_PARSE_ARGUMENTS prefix _optionNames _singleArgNames _multiArgNames) 84 | # first set all result variables to empty/FALSE 85 | foreach(arg_name ${_singleArgNames} ${_multiArgNames}) 86 | set(${prefix}_${arg_name}) 87 | endforeach(arg_name) 88 | 89 | foreach(option ${_optionNames}) 90 | set(${prefix}_${option} FALSE) 91 | endforeach(option) 92 | 93 | set(${prefix}_UNPARSED_ARGUMENTS) 94 | 95 | set(insideValues FALSE) 96 | set(currentArgName) 97 | 98 | # now iterate over all arguments and fill the result variables 99 | foreach(currentArg ${ARGN}) 100 | list(FIND _optionNames "${currentArg}" optionIndex) # ... then this marks the end of the arguments belonging to this keyword 101 | list(FIND _singleArgNames "${currentArg}" singleArgIndex) # ... then this marks the end of the arguments belonging to this keyword 102 | list(FIND _multiArgNames "${currentArg}" multiArgIndex) # ... then this marks the end of the arguments belonging to this keyword 103 | 104 | if(${optionIndex} EQUAL -1 AND ${singleArgIndex} EQUAL -1 AND ${multiArgIndex} EQUAL -1) 105 | if(insideValues) 106 | if("${insideValues}" STREQUAL "SINGLE") 107 | set(${prefix}_${currentArgName} ${currentArg}) 108 | set(insideValues FALSE) 109 | elseif("${insideValues}" STREQUAL "MULTI") 110 | list(APPEND ${prefix}_${currentArgName} ${currentArg}) 111 | endif() 112 | else(insideValues) 113 | list(APPEND ${prefix}_UNPARSED_ARGUMENTS ${currentArg}) 114 | endif(insideValues) 115 | else() 116 | if(NOT ${optionIndex} EQUAL -1) 117 | set(${prefix}_${currentArg} TRUE) 118 | set(insideValues FALSE) 119 | elseif(NOT ${singleArgIndex} EQUAL -1) 120 | set(currentArgName ${currentArg}) 121 | set(${prefix}_${currentArgName}) 122 | set(insideValues "SINGLE") 123 | elseif(NOT ${multiArgIndex} EQUAL -1) 124 | set(currentArgName ${currentArg}) 125 | set(${prefix}_${currentArgName}) 126 | set(insideValues "MULTI") 127 | endif() 128 | endif() 129 | 130 | endforeach(currentArg) 131 | 132 | # propagate the result variables to the caller: 133 | foreach(arg_name ${_singleArgNames} ${_multiArgNames} ${_optionNames}) 134 | set(${prefix}_${arg_name} ${${prefix}_${arg_name}} PARENT_SCOPE) 135 | endforeach(arg_name) 136 | set(${prefix}_UNPARSED_ARGUMENTS ${${prefix}_UNPARSED_ARGUMENTS} PARENT_SCOPE) 137 | 138 | endfunction(CMAKE_PARSE_ARGUMENTS _options _singleArgs _multiArgs) 139 | -------------------------------------------------------------------------------- /cmake/Modules/FindCppUnit.cmake: -------------------------------------------------------------------------------- 1 | # http://www.cmake.org/pipermail/cmake/2006-October/011446.html 2 | # Modified to use pkg config and use standard var names 3 | 4 | # 5 | # Find the CppUnit includes and library 6 | # 7 | # This module defines 8 | # CPPUNIT_INCLUDE_DIR, where to find tiff.h, etc. 9 | # CPPUNIT_LIBRARIES, the libraries to link against to use CppUnit. 10 | # CPPUNIT_FOUND, If false, do not try to use CppUnit. 11 | 12 | INCLUDE(FindPkgConfig) 13 | PKG_CHECK_MODULES(PC_CPPUNIT "cppunit") 14 | 15 | FIND_PATH(CPPUNIT_INCLUDE_DIRS 16 | NAMES cppunit/TestCase.h 17 | HINTS ${PC_CPPUNIT_INCLUDE_DIR} 18 | PATHS 19 | /usr/local/include 20 | /usr/include 21 | ) 22 | 23 | FIND_LIBRARY(CPPUNIT_LIBRARIES 24 | NAMES cppunit 25 | HINTS ${PC_CPPUNIT_LIBDIR} 26 | PATHS 27 | ${CPPUNIT_INCLUDE_DIRS}/../lib 28 | /usr/local/lib 29 | /usr/lib 30 | ) 31 | 32 | LIST(APPEND CPPUNIT_LIBRARIES ${CMAKE_DL_LIBS}) 33 | 34 | INCLUDE(FindPackageHandleStandardArgs) 35 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(CPPUNIT DEFAULT_MSG CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS) 36 | MARK_AS_ADVANCED(CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS) 37 | -------------------------------------------------------------------------------- /cmake/Modules/FindGnuradioRuntime.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_GNURADIO_RUNTIME gnuradio-runtime) 3 | 4 | if(PC_GNURADIO_RUNTIME_FOUND) 5 | # look for include files 6 | FIND_PATH( 7 | GNURADIO_RUNTIME_INCLUDE_DIRS 8 | NAMES gnuradio/top_block.h 9 | HINTS $ENV{GNURADIO_RUNTIME_DIR}/include 10 | ${PC_GNURADIO_RUNTIME_INCLUDE_DIRS} 11 | ${CMAKE_INSTALL_PREFIX}/include 12 | PATHS /usr/local/include 13 | /usr/include 14 | ) 15 | 16 | # look for libs 17 | FIND_LIBRARY( 18 | GNURADIO_RUNTIME_LIBRARIES 19 | NAMES gnuradio-runtime 20 | HINTS $ENV{GNURADIO_RUNTIME_DIR}/lib 21 | ${PC_GNURADIO_RUNTIME_LIBDIR} 22 | ${CMAKE_INSTALL_PREFIX}/lib/ 23 | ${CMAKE_INSTALL_PREFIX}/lib64/ 24 | PATHS /usr/local/lib 25 | /usr/local/lib64 26 | /usr/lib 27 | /usr/lib64 28 | ) 29 | 30 | set(GNURADIO_RUNTIME_FOUND ${PC_GNURADIO_RUNTIME_FOUND}) 31 | endif(PC_GNURADIO_RUNTIME_FOUND) 32 | 33 | INCLUDE(FindPackageHandleStandardArgs) 34 | # do not check GNURADIO_RUNTIME_INCLUDE_DIRS, is not set when default include path us used. 35 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_RUNTIME DEFAULT_MSG GNURADIO_RUNTIME_LIBRARIES) 36 | MARK_AS_ADVANCED(GNURADIO_RUNTIME_LIBRARIES GNURADIO_RUNTIME_INCLUDE_DIRS) 37 | -------------------------------------------------------------------------------- /cmake/Modules/GrMiscUtils.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | if(DEFINED __INCLUDED_GR_MISC_UTILS_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_MISC_UTILS_CMAKE TRUE) 24 | 25 | ######################################################################## 26 | # Set global variable macro. 27 | # Used for subdirectories to export settings. 28 | # Example: include and library paths. 29 | ######################################################################## 30 | function(GR_SET_GLOBAL var) 31 | set(${var} ${ARGN} CACHE INTERNAL "" FORCE) 32 | endfunction(GR_SET_GLOBAL) 33 | 34 | ######################################################################## 35 | # Set the pre-processor definition if the condition is true. 36 | # - def the pre-processor definition to set and condition name 37 | ######################################################################## 38 | function(GR_ADD_COND_DEF def) 39 | if(${def}) 40 | add_definitions(-D${def}) 41 | endif(${def}) 42 | endfunction(GR_ADD_COND_DEF) 43 | 44 | ######################################################################## 45 | # Check for a header and conditionally set a compile define. 46 | # - hdr the relative path to the header file 47 | # - def the pre-processor definition to set 48 | ######################################################################## 49 | function(GR_CHECK_HDR_N_DEF hdr def) 50 | include(CheckIncludeFileCXX) 51 | CHECK_INCLUDE_FILE_CXX(${hdr} ${def}) 52 | GR_ADD_COND_DEF(${def}) 53 | endfunction(GR_CHECK_HDR_N_DEF) 54 | 55 | ######################################################################## 56 | # Include subdirectory macro. 57 | # Sets the CMake directory variables, 58 | # includes the subdirectory CMakeLists.txt, 59 | # resets the CMake directory variables. 60 | # 61 | # This macro includes subdirectories rather than adding them 62 | # so that the subdirectory can affect variables in the level above. 63 | # This provides a work-around for the lack of convenience libraries. 64 | # This way a subdirectory can append to the list of library sources. 65 | ######################################################################## 66 | macro(GR_INCLUDE_SUBDIRECTORY subdir) 67 | #insert the current directories on the front of the list 68 | list(INSERT _cmake_source_dirs 0 ${CMAKE_CURRENT_SOURCE_DIR}) 69 | list(INSERT _cmake_binary_dirs 0 ${CMAKE_CURRENT_BINARY_DIR}) 70 | 71 | #set the current directories to the names of the subdirs 72 | set(CMAKE_CURRENT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${subdir}) 73 | set(CMAKE_CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${subdir}) 74 | 75 | #include the subdirectory CMakeLists to run it 76 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 77 | include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) 78 | 79 | #reset the value of the current directories 80 | list(GET _cmake_source_dirs 0 CMAKE_CURRENT_SOURCE_DIR) 81 | list(GET _cmake_binary_dirs 0 CMAKE_CURRENT_BINARY_DIR) 82 | 83 | #pop the subdir names of the front of the list 84 | list(REMOVE_AT _cmake_source_dirs 0) 85 | list(REMOVE_AT _cmake_binary_dirs 0) 86 | endmacro(GR_INCLUDE_SUBDIRECTORY) 87 | 88 | ######################################################################## 89 | # Check if a compiler flag works and conditionally set a compile define. 90 | # - flag the compiler flag to check for 91 | # - have the variable to set with result 92 | ######################################################################## 93 | macro(GR_ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have) 94 | include(CheckCXXCompilerFlag) 95 | CHECK_CXX_COMPILER_FLAG(${flag} ${have}) 96 | if(${have}) 97 | add_definitions(${flag}) 98 | endif(${have}) 99 | endmacro(GR_ADD_CXX_COMPILER_FLAG_IF_AVAILABLE) 100 | 101 | ######################################################################## 102 | # Generates the .la libtool file 103 | # This appears to generate libtool files that cannot be used by auto*. 104 | # Usage GR_LIBTOOL(TARGET [target] DESTINATION [dest]) 105 | # Notice: there is not COMPONENT option, these will not get distributed. 106 | ######################################################################## 107 | function(GR_LIBTOOL) 108 | if(NOT DEFINED GENERATE_LIBTOOL) 109 | set(GENERATE_LIBTOOL OFF) #disabled by default 110 | endif() 111 | 112 | if(GENERATE_LIBTOOL) 113 | include(CMakeParseArgumentsCopy) 114 | CMAKE_PARSE_ARGUMENTS(GR_LIBTOOL "" "TARGET;DESTINATION" "" ${ARGN}) 115 | 116 | find_program(LIBTOOL libtool) 117 | if(LIBTOOL) 118 | include(CMakeMacroLibtoolFile) 119 | CREATE_LIBTOOL_FILE(${GR_LIBTOOL_TARGET} /${GR_LIBTOOL_DESTINATION}) 120 | endif(LIBTOOL) 121 | endif(GENERATE_LIBTOOL) 122 | 123 | endfunction(GR_LIBTOOL) 124 | 125 | ######################################################################## 126 | # Do standard things to the library target 127 | # - set target properties 128 | # - make install rules 129 | # Also handle gnuradio custom naming conventions w/ extras mode. 130 | ######################################################################## 131 | function(GR_LIBRARY_FOO target) 132 | #parse the arguments for component names 133 | include(CMakeParseArgumentsCopy) 134 | CMAKE_PARSE_ARGUMENTS(GR_LIBRARY "" "RUNTIME_COMPONENT;DEVEL_COMPONENT" "" ${ARGN}) 135 | 136 | #set additional target properties 137 | set_target_properties(${target} PROPERTIES SOVERSION ${LIBVER}) 138 | 139 | #install the generated files like so... 140 | install(TARGETS ${target} 141 | LIBRARY DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} # .so/.dylib file 142 | ARCHIVE DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_DEVEL_COMPONENT} # .lib file 143 | RUNTIME DESTINATION ${GR_RUNTIME_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} # .dll file 144 | ) 145 | 146 | #extras mode enabled automatically on linux 147 | if(NOT DEFINED LIBRARY_EXTRAS) 148 | set(LIBRARY_EXTRAS ${LINUX}) 149 | endif() 150 | 151 | #special extras mode to enable alternative naming conventions 152 | if(LIBRARY_EXTRAS) 153 | 154 | #create .la file before changing props 155 | GR_LIBTOOL(TARGET ${target} DESTINATION ${GR_LIBRARY_DIR}) 156 | 157 | #give the library a special name with ultra-zero soversion 158 | set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_NAME ${target}-${LIBVER} SOVERSION "0.0.0") 159 | set(target_name lib${target}-${LIBVER}.so.0.0.0) 160 | 161 | #custom command to generate symlinks 162 | add_custom_command( 163 | TARGET ${target} 164 | POST_BUILD 165 | COMMAND ${CMAKE_COMMAND} -E create_symlink ${target_name} ${CMAKE_CURRENT_BINARY_DIR}/lib${target}.so 166 | COMMAND ${CMAKE_COMMAND} -E create_symlink ${target_name} ${CMAKE_CURRENT_BINARY_DIR}/lib${target}-${LIBVER}.so.0 167 | COMMAND ${CMAKE_COMMAND} -E touch ${target_name} #so the symlinks point to something valid so cmake 2.6 will install 168 | ) 169 | 170 | #and install the extra symlinks 171 | install( 172 | FILES 173 | ${CMAKE_CURRENT_BINARY_DIR}/lib${target}.so 174 | ${CMAKE_CURRENT_BINARY_DIR}/lib${target}-${LIBVER}.so.0 175 | DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} 176 | ) 177 | 178 | endif(LIBRARY_EXTRAS) 179 | endfunction(GR_LIBRARY_FOO) 180 | 181 | ######################################################################## 182 | # Create a dummy custom command that depends on other targets. 183 | # Usage: 184 | # GR_GEN_TARGET_DEPS(unique_name target_deps ...) 185 | # ADD_CUSTOM_COMMAND( ${target_deps}) 186 | # 187 | # Custom command cant depend on targets, but can depend on executables, 188 | # and executables can depend on targets. So this is the process: 189 | ######################################################################## 190 | function(GR_GEN_TARGET_DEPS name var) 191 | file( 192 | WRITE ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp.in 193 | "int main(void){return 0;}\n" 194 | ) 195 | execute_process( 196 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 197 | ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp.in 198 | ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp 199 | ) 200 | add_executable(${name} ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp) 201 | if(ARGN) 202 | add_dependencies(${name} ${ARGN}) 203 | endif(ARGN) 204 | 205 | if(CMAKE_CROSSCOMPILING) 206 | set(${var} "DEPENDS;${name}" PARENT_SCOPE) #cant call command when cross 207 | else() 208 | set(${var} "DEPENDS;${name};COMMAND;${name}" PARENT_SCOPE) 209 | endif() 210 | endfunction(GR_GEN_TARGET_DEPS) 211 | -------------------------------------------------------------------------------- /cmake/Modules/GrPlatform.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | if(DEFINED __INCLUDED_GR_PLATFORM_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_PLATFORM_CMAKE TRUE) 24 | 25 | ######################################################################## 26 | # Setup additional defines for OS types 27 | ######################################################################## 28 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 29 | set(LINUX TRUE) 30 | endif() 31 | 32 | if(LINUX AND EXISTS "/etc/debian_version") 33 | set(DEBIAN TRUE) 34 | endif() 35 | 36 | if(LINUX AND EXISTS "/etc/redhat-release") 37 | set(REDHAT TRUE) 38 | endif() 39 | 40 | ######################################################################## 41 | # when the library suffix should be 64 (applies to redhat linux family) 42 | ######################################################################## 43 | if(NOT DEFINED LIB_SUFFIX AND REDHAT AND CMAKE_SYSTEM_PROCESSOR MATCHES "64$") 44 | set(LIB_SUFFIX 64) 45 | endif() 46 | set(LIB_SUFFIX ${LIB_SUFFIX} CACHE STRING "lib directory suffix") 47 | -------------------------------------------------------------------------------- /cmake/Modules/GrPython.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | if(DEFINED __INCLUDED_GR_PYTHON_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_PYTHON_CMAKE TRUE) 24 | 25 | ######################################################################## 26 | # Setup the python interpreter: 27 | # This allows the user to specify a specific interpreter, 28 | # or finds the interpreter via the built-in cmake module. 29 | ######################################################################## 30 | #this allows the user to override PYTHON_EXECUTABLE 31 | if(PYTHON_EXECUTABLE) 32 | 33 | set(PYTHONINTERP_FOUND TRUE) 34 | 35 | #otherwise if not set, try to automatically find it 36 | else(PYTHON_EXECUTABLE) 37 | 38 | #use the built-in find script 39 | find_package(PythonInterp 2) 40 | 41 | #and if that fails use the find program routine 42 | if(NOT PYTHONINTERP_FOUND) 43 | find_program(PYTHON_EXECUTABLE NAMES python python2 python2.7 python2.6 python2.5) 44 | if(PYTHON_EXECUTABLE) 45 | set(PYTHONINTERP_FOUND TRUE) 46 | endif(PYTHON_EXECUTABLE) 47 | endif(NOT PYTHONINTERP_FOUND) 48 | 49 | endif(PYTHON_EXECUTABLE) 50 | 51 | #make the path to the executable appear in the cmake gui 52 | set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} CACHE FILEPATH "python interpreter") 53 | 54 | #make sure we can use -B with python (introduced in 2.6) 55 | if(PYTHON_EXECUTABLE) 56 | execute_process( 57 | COMMAND ${PYTHON_EXECUTABLE} -B -c "" 58 | OUTPUT_QUIET ERROR_QUIET 59 | RESULT_VARIABLE PYTHON_HAS_DASH_B_RESULT 60 | ) 61 | if(PYTHON_HAS_DASH_B_RESULT EQUAL 0) 62 | set(PYTHON_DASH_B "-B") 63 | endif() 64 | endif(PYTHON_EXECUTABLE) 65 | 66 | ######################################################################## 67 | # Check for the existence of a python module: 68 | # - desc a string description of the check 69 | # - mod the name of the module to import 70 | # - cmd an additional command to run 71 | # - have the result variable to set 72 | ######################################################################## 73 | macro(GR_PYTHON_CHECK_MODULE desc mod cmd have) 74 | message(STATUS "") 75 | message(STATUS "Python checking for ${desc}") 76 | execute_process( 77 | COMMAND ${PYTHON_EXECUTABLE} -c " 78 | ######################################### 79 | try: import ${mod} 80 | except: exit(-1) 81 | try: assert ${cmd} 82 | except: exit(-1) 83 | #########################################" 84 | RESULT_VARIABLE ${have} 85 | ) 86 | if(${have} EQUAL 0) 87 | message(STATUS "Python checking for ${desc} - found") 88 | set(${have} TRUE) 89 | else(${have} EQUAL 0) 90 | message(STATUS "Python checking for ${desc} - not found") 91 | set(${have} FALSE) 92 | endif(${have} EQUAL 0) 93 | endmacro(GR_PYTHON_CHECK_MODULE) 94 | 95 | ######################################################################## 96 | # Sets the python installation directory GR_PYTHON_DIR 97 | ######################################################################## 98 | execute_process(COMMAND ${PYTHON_EXECUTABLE} -c " 99 | from distutils import sysconfig 100 | print sysconfig.get_python_lib(plat_specific=True, prefix='') 101 | " OUTPUT_VARIABLE GR_PYTHON_DIR OUTPUT_STRIP_TRAILING_WHITESPACE 102 | ) 103 | file(TO_CMAKE_PATH ${GR_PYTHON_DIR} GR_PYTHON_DIR) 104 | 105 | ######################################################################## 106 | # Create an always-built target with a unique name 107 | # Usage: GR_UNIQUE_TARGET( ) 108 | ######################################################################## 109 | function(GR_UNIQUE_TARGET desc) 110 | file(RELATIVE_PATH reldir ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) 111 | execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import re, hashlib 112 | unique = hashlib.md5('${reldir}${ARGN}').hexdigest()[:5] 113 | print(re.sub('\\W', '_', '${desc} ${reldir} ' + unique))" 114 | OUTPUT_VARIABLE _target OUTPUT_STRIP_TRAILING_WHITESPACE) 115 | add_custom_target(${_target} ALL DEPENDS ${ARGN}) 116 | endfunction(GR_UNIQUE_TARGET) 117 | 118 | ######################################################################## 119 | # Install python sources (also builds and installs byte-compiled python) 120 | ######################################################################## 121 | function(GR_PYTHON_INSTALL) 122 | include(CMakeParseArgumentsCopy) 123 | CMAKE_PARSE_ARGUMENTS(GR_PYTHON_INSTALL "" "DESTINATION;COMPONENT" "FILES;PROGRAMS" ${ARGN}) 124 | 125 | #################################################################### 126 | if(GR_PYTHON_INSTALL_FILES) 127 | #################################################################### 128 | install(${ARGN}) #installs regular python files 129 | 130 | #create a list of all generated files 131 | unset(pysrcfiles) 132 | unset(pycfiles) 133 | unset(pyofiles) 134 | foreach(pyfile ${GR_PYTHON_INSTALL_FILES}) 135 | get_filename_component(pyfile ${pyfile} ABSOLUTE) 136 | list(APPEND pysrcfiles ${pyfile}) 137 | 138 | #determine if this file is in the source or binary directory 139 | file(RELATIVE_PATH source_rel_path ${CMAKE_CURRENT_SOURCE_DIR} ${pyfile}) 140 | string(LENGTH "${source_rel_path}" source_rel_path_len) 141 | file(RELATIVE_PATH binary_rel_path ${CMAKE_CURRENT_BINARY_DIR} ${pyfile}) 142 | string(LENGTH "${binary_rel_path}" binary_rel_path_len) 143 | 144 | #and set the generated path appropriately 145 | if(${source_rel_path_len} GREATER ${binary_rel_path_len}) 146 | set(pygenfile ${CMAKE_CURRENT_BINARY_DIR}/${binary_rel_path}) 147 | else() 148 | set(pygenfile ${CMAKE_CURRENT_BINARY_DIR}/${source_rel_path}) 149 | endif() 150 | list(APPEND pycfiles ${pygenfile}c) 151 | list(APPEND pyofiles ${pygenfile}o) 152 | 153 | #ensure generation path exists 154 | get_filename_component(pygen_path ${pygenfile} PATH) 155 | file(MAKE_DIRECTORY ${pygen_path}) 156 | 157 | endforeach(pyfile) 158 | 159 | #the command to generate the pyc files 160 | add_custom_command( 161 | DEPENDS ${pysrcfiles} OUTPUT ${pycfiles} 162 | COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_BINARY_DIR}/python_compile_helper.py ${pysrcfiles} ${pycfiles} 163 | ) 164 | 165 | #the command to generate the pyo files 166 | add_custom_command( 167 | DEPENDS ${pysrcfiles} OUTPUT ${pyofiles} 168 | COMMAND ${PYTHON_EXECUTABLE} -O ${CMAKE_BINARY_DIR}/python_compile_helper.py ${pysrcfiles} ${pyofiles} 169 | ) 170 | 171 | #create install rule and add generated files to target list 172 | set(python_install_gen_targets ${pycfiles} ${pyofiles}) 173 | install(FILES ${python_install_gen_targets} 174 | DESTINATION ${GR_PYTHON_INSTALL_DESTINATION} 175 | COMPONENT ${GR_PYTHON_INSTALL_COMPONENT} 176 | ) 177 | 178 | 179 | #################################################################### 180 | elseif(GR_PYTHON_INSTALL_PROGRAMS) 181 | #################################################################### 182 | file(TO_NATIVE_PATH ${PYTHON_EXECUTABLE} pyexe_native) 183 | 184 | foreach(pyfile ${GR_PYTHON_INSTALL_PROGRAMS}) 185 | get_filename_component(pyfile_name ${pyfile} NAME) 186 | get_filename_component(pyfile ${pyfile} ABSOLUTE) 187 | string(REPLACE "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" pyexefile "${pyfile}.exe") 188 | list(APPEND python_install_gen_targets ${pyexefile}) 189 | 190 | get_filename_component(pyexefile_path ${pyexefile} PATH) 191 | file(MAKE_DIRECTORY ${pyexefile_path}) 192 | 193 | add_custom_command( 194 | OUTPUT ${pyexefile} DEPENDS ${pyfile} 195 | COMMAND ${PYTHON_EXECUTABLE} -c 196 | \"open('${pyexefile}', 'w').write('\#!${pyexe_native}\\n'+open('${pyfile}').read())\" 197 | COMMENT "Shebangin ${pyfile_name}" 198 | ) 199 | 200 | #on windows, python files need an extension to execute 201 | get_filename_component(pyfile_ext ${pyfile} EXT) 202 | if(WIN32 AND NOT pyfile_ext) 203 | set(pyfile_name "${pyfile_name}.py") 204 | endif() 205 | 206 | install(PROGRAMS ${pyexefile} RENAME ${pyfile_name} 207 | DESTINATION ${GR_PYTHON_INSTALL_DESTINATION} 208 | COMPONENT ${GR_PYTHON_INSTALL_COMPONENT} 209 | ) 210 | endforeach(pyfile) 211 | 212 | endif() 213 | 214 | GR_UNIQUE_TARGET("pygen" ${python_install_gen_targets}) 215 | 216 | endfunction(GR_PYTHON_INSTALL) 217 | 218 | ######################################################################## 219 | # Write the python helper script that generates byte code files 220 | ######################################################################## 221 | file(WRITE ${CMAKE_BINARY_DIR}/python_compile_helper.py " 222 | import sys, py_compile 223 | files = sys.argv[1:] 224 | srcs, gens = files[:len(files)/2], files[len(files)/2:] 225 | for src, gen in zip(srcs, gens): 226 | py_compile.compile(file=src, cfile=gen, doraise=True) 227 | ") 228 | -------------------------------------------------------------------------------- /cmake/Modules/GrSwig.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | if(DEFINED __INCLUDED_GR_SWIG_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_SWIG_CMAKE TRUE) 24 | 25 | include(GrPython) 26 | 27 | ######################################################################## 28 | # Builds a swig documentation file to be generated into python docstrings 29 | # Usage: GR_SWIG_MAKE_DOCS(output_file input_path input_path....) 30 | # 31 | # Set the following variable to specify extra dependent targets: 32 | # - GR_SWIG_DOCS_SOURCE_DEPS 33 | # - GR_SWIG_DOCS_TARGET_DEPS 34 | ######################################################################## 35 | function(GR_SWIG_MAKE_DOCS output_file) 36 | find_package(Doxygen) 37 | if(DOXYGEN_FOUND) 38 | 39 | #setup the input files variable list, quote formated 40 | set(input_files) 41 | unset(INPUT_PATHS) 42 | foreach(input_path ${ARGN}) 43 | if (IS_DIRECTORY ${input_path}) #when input path is a directory 44 | file(GLOB input_path_h_files ${input_path}/*.h) 45 | else() #otherwise its just a file, no glob 46 | set(input_path_h_files ${input_path}) 47 | endif() 48 | list(APPEND input_files ${input_path_h_files}) 49 | set(INPUT_PATHS "${INPUT_PATHS} \"${input_path}\"") 50 | endforeach(input_path) 51 | 52 | #determine the output directory 53 | get_filename_component(name ${output_file} NAME_WE) 54 | get_filename_component(OUTPUT_DIRECTORY ${output_file} PATH) 55 | set(OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}/${name}_swig_docs) 56 | make_directory(${OUTPUT_DIRECTORY}) 57 | 58 | #generate the Doxyfile used by doxygen 59 | configure_file( 60 | ${CMAKE_SOURCE_DIR}/docs/doxygen/Doxyfile.swig_doc.in 61 | ${OUTPUT_DIRECTORY}/Doxyfile 62 | @ONLY) 63 | 64 | #Create a dummy custom command that depends on other targets 65 | include(GrMiscUtils) 66 | GR_GEN_TARGET_DEPS(_${name}_tag tag_deps ${GR_SWIG_DOCS_TARGET_DEPS}) 67 | 68 | #call doxygen on the Doxyfile + input headers 69 | add_custom_command( 70 | OUTPUT ${OUTPUT_DIRECTORY}/xml/index.xml 71 | DEPENDS ${input_files} ${GR_SWIG_DOCS_SOURCE_DEPS} ${tag_deps} 72 | COMMAND ${DOXYGEN_EXECUTABLE} ${OUTPUT_DIRECTORY}/Doxyfile 73 | COMMENT "Generating doxygen xml for ${name} docs" 74 | ) 75 | 76 | #call the swig_doc script on the xml files 77 | add_custom_command( 78 | OUTPUT ${output_file} 79 | DEPENDS ${input_files} ${OUTPUT_DIRECTORY}/xml/index.xml 80 | COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} 81 | ${CMAKE_SOURCE_DIR}/docs/doxygen/swig_doc.py 82 | ${OUTPUT_DIRECTORY}/xml 83 | ${output_file} 84 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs/doxygen 85 | ) 86 | 87 | else(DOXYGEN_FOUND) 88 | file(WRITE ${output_file} "\n") #no doxygen -> empty file 89 | endif(DOXYGEN_FOUND) 90 | endfunction(GR_SWIG_MAKE_DOCS) 91 | 92 | ######################################################################## 93 | # Build a swig target for the common gnuradio use case. Usage: 94 | # GR_SWIG_MAKE(target ifile ifile ifile...) 95 | # 96 | # Set the following variables before calling: 97 | # - GR_SWIG_FLAGS 98 | # - GR_SWIG_INCLUDE_DIRS 99 | # - GR_SWIG_LIBRARIES 100 | # - GR_SWIG_SOURCE_DEPS 101 | # - GR_SWIG_TARGET_DEPS 102 | # - GR_SWIG_DOC_FILE 103 | # - GR_SWIG_DOC_DIRS 104 | ######################################################################## 105 | macro(GR_SWIG_MAKE name) 106 | set(ifiles ${ARGN}) 107 | 108 | #do swig doc generation if specified 109 | if (GR_SWIG_DOC_FILE) 110 | set(GR_SWIG_DOCS_SOURCE_DEPS ${GR_SWIG_SOURCE_DEPS}) 111 | set(GR_SWIG_DOCS_TAREGT_DEPS ${GR_SWIG_TARGET_DEPS}) 112 | GR_SWIG_MAKE_DOCS(${GR_SWIG_DOC_FILE} ${GR_SWIG_DOC_DIRS}) 113 | list(APPEND GR_SWIG_SOURCE_DEPS ${GR_SWIG_DOC_FILE}) 114 | endif() 115 | 116 | #append additional include directories 117 | find_package(PythonLibs 2) 118 | list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_PATH}) #deprecated name (now dirs) 119 | list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS}) 120 | list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) 121 | list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) 122 | 123 | #determine include dependencies for swig file 124 | execute_process( 125 | COMMAND ${PYTHON_EXECUTABLE} 126 | ${CMAKE_BINARY_DIR}/get_swig_deps.py 127 | "${ifiles}" "${GR_SWIG_INCLUDE_DIRS}" 128 | OUTPUT_STRIP_TRAILING_WHITESPACE 129 | OUTPUT_VARIABLE SWIG_MODULE_${name}_EXTRA_DEPS 130 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 131 | ) 132 | 133 | #Create a dummy custom command that depends on other targets 134 | include(GrMiscUtils) 135 | GR_GEN_TARGET_DEPS(_${name}_swig_tag tag_deps ${GR_SWIG_TARGET_DEPS}) 136 | set(tag_file ${CMAKE_CURRENT_BINARY_DIR}/${name}.tag) 137 | add_custom_command( 138 | OUTPUT ${tag_file} 139 | DEPENDS ${GR_SWIG_SOURCE_DEPS} ${tag_deps} 140 | COMMAND ${CMAKE_COMMAND} -E touch ${tag_file} 141 | ) 142 | 143 | #append the specified include directories 144 | include_directories(${GR_SWIG_INCLUDE_DIRS}) 145 | list(APPEND SWIG_MODULE_${name}_EXTRA_DEPS ${tag_file}) 146 | 147 | #setup the swig flags with flags and include directories 148 | set(CMAKE_SWIG_FLAGS -fvirtual -modern -keyword -w511 -module ${name} ${GR_SWIG_FLAGS}) 149 | foreach(dir ${GR_SWIG_INCLUDE_DIRS}) 150 | list(APPEND CMAKE_SWIG_FLAGS "-I${dir}") 151 | endforeach(dir) 152 | 153 | #set the C++ property on the swig .i file so it builds 154 | set_source_files_properties(${ifiles} PROPERTIES CPLUSPLUS ON) 155 | 156 | #setup the actual swig library target to be built 157 | include(UseSWIG) 158 | SWIG_ADD_MODULE(${name} python ${ifiles}) 159 | SWIG_LINK_LIBRARIES(${name} ${PYTHON_LIBRARIES} ${GR_SWIG_LIBRARIES}) 160 | 161 | endmacro(GR_SWIG_MAKE) 162 | 163 | ######################################################################## 164 | # Install swig targets generated by GR_SWIG_MAKE. Usage: 165 | # GR_SWIG_INSTALL( 166 | # TARGETS target target target... 167 | # [DESTINATION destination] 168 | # [COMPONENT component] 169 | # ) 170 | ######################################################################## 171 | macro(GR_SWIG_INSTALL) 172 | 173 | include(CMakeParseArgumentsCopy) 174 | CMAKE_PARSE_ARGUMENTS(GR_SWIG_INSTALL "" "DESTINATION;COMPONENT" "TARGETS" ${ARGN}) 175 | 176 | foreach(name ${GR_SWIG_INSTALL_TARGETS}) 177 | install(TARGETS ${SWIG_MODULE_${name}_REAL_NAME} 178 | DESTINATION ${GR_SWIG_INSTALL_DESTINATION} 179 | COMPONENT ${GR_SWIG_INSTALL_COMPONENT} 180 | ) 181 | 182 | include(GrPython) 183 | GR_PYTHON_INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${name}.py 184 | DESTINATION ${GR_SWIG_INSTALL_DESTINATION} 185 | COMPONENT ${GR_SWIG_INSTALL_COMPONENT} 186 | ) 187 | 188 | GR_LIBTOOL( 189 | TARGET ${SWIG_MODULE_${name}_REAL_NAME} 190 | DESTINATION ${GR_SWIG_INSTALL_DESTINATION} 191 | ) 192 | 193 | endforeach(name) 194 | 195 | endmacro(GR_SWIG_INSTALL) 196 | 197 | ######################################################################## 198 | # Generate a python file that can determine swig dependencies. 199 | # Used by the make macro above to determine extra dependencies. 200 | # When you build C++, CMake figures out the header dependencies. 201 | # This code essentially performs that logic for swig includes. 202 | ######################################################################## 203 | file(WRITE ${CMAKE_BINARY_DIR}/get_swig_deps.py " 204 | 205 | import os, sys, re 206 | 207 | include_matcher = re.compile('[#|%]include\\s*[<|\"](.*)[>|\"]') 208 | include_dirs = sys.argv[2].split(';') 209 | 210 | def get_swig_incs(file_path): 211 | file_contents = open(file_path, 'r').read() 212 | return include_matcher.findall(file_contents, re.MULTILINE) 213 | 214 | def get_swig_deps(file_path, level): 215 | deps = [file_path] 216 | if level == 0: return deps 217 | for inc_file in get_swig_incs(file_path): 218 | for inc_dir in include_dirs: 219 | inc_path = os.path.join(inc_dir, inc_file) 220 | if not os.path.exists(inc_path): continue 221 | deps.extend(get_swig_deps(inc_path, level-1)) 222 | return deps 223 | 224 | if __name__ == '__main__': 225 | ifiles = sys.argv[1].split(';') 226 | deps = sum([get_swig_deps(ifile, 3) for ifile in ifiles], []) 227 | #sys.stderr.write(';'.join(set(deps)) + '\\n\\n') 228 | print(';'.join(set(deps))) 229 | ") 230 | -------------------------------------------------------------------------------- /cmake/Modules/GrTest.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | if(DEFINED __INCLUDED_GR_TEST_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_TEST_CMAKE TRUE) 24 | 25 | ######################################################################## 26 | # Add a unit test and setup the environment for a unit test. 27 | # Takes the same arguments as the ADD_TEST function. 28 | # 29 | # Before calling set the following variables: 30 | # GR_TEST_TARGET_DEPS - built targets for the library path 31 | # GR_TEST_LIBRARY_DIRS - directories for the library path 32 | # GR_TEST_PYTHON_DIRS - directories for the python path 33 | ######################################################################## 34 | function(GR_ADD_TEST test_name) 35 | 36 | if(WIN32) 37 | #Ensure that the build exe also appears in the PATH. 38 | list(APPEND GR_TEST_TARGET_DEPS ${ARGN}) 39 | 40 | #In the land of windows, all libraries must be in the PATH. 41 | #Since the dependent libraries are not yet installed, 42 | #we must manually set them in the PATH to run tests. 43 | #The following appends the path of a target dependency. 44 | foreach(target ${GR_TEST_TARGET_DEPS}) 45 | get_target_property(location ${target} LOCATION) 46 | if(location) 47 | get_filename_component(path ${location} PATH) 48 | string(REGEX REPLACE "\\$\\(.*\\)" ${CMAKE_BUILD_TYPE} path ${path}) 49 | list(APPEND GR_TEST_LIBRARY_DIRS ${path}) 50 | endif(location) 51 | endforeach(target) 52 | 53 | #SWIG generates the python library files into a subdirectory. 54 | #Therefore, we must append this subdirectory into PYTHONPATH. 55 | #Only do this for the python directories matching the following: 56 | foreach(pydir ${GR_TEST_PYTHON_DIRS}) 57 | get_filename_component(name ${pydir} NAME) 58 | if(name MATCHES "^(swig|lib|src)$") 59 | list(APPEND GR_TEST_PYTHON_DIRS ${pydir}/${CMAKE_BUILD_TYPE}) 60 | endif() 61 | endforeach(pydir) 62 | endif(WIN32) 63 | 64 | file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR} srcdir) 65 | file(TO_NATIVE_PATH "${GR_TEST_LIBRARY_DIRS}" libpath) #ok to use on dir list? 66 | file(TO_NATIVE_PATH "${GR_TEST_PYTHON_DIRS}" pypath) #ok to use on dir list? 67 | 68 | set(environs "GR_DONT_LOAD_PREFS=1" "srcdir=${srcdir}") 69 | 70 | #http://www.cmake.org/pipermail/cmake/2009-May/029464.html 71 | #Replaced this add test + set environs code with the shell script generation. 72 | #Its nicer to be able to manually run the shell script to diagnose problems. 73 | #ADD_TEST(${ARGV}) 74 | #SET_TESTS_PROPERTIES(${test_name} PROPERTIES ENVIRONMENT "${environs}") 75 | 76 | if(UNIX) 77 | set(binpath "${CMAKE_CURRENT_BINARY_DIR}:$PATH") 78 | #set both LD and DYLD paths to cover multiple UNIX OS library paths 79 | list(APPEND libpath "$LD_LIBRARY_PATH" "$DYLD_LIBRARY_PATH") 80 | list(APPEND pypath "$PYTHONPATH") 81 | 82 | #replace list separator with the path separator 83 | string(REPLACE ";" ":" libpath "${libpath}") 84 | string(REPLACE ";" ":" pypath "${pypath}") 85 | list(APPEND environs "PATH=${binpath}" "LD_LIBRARY_PATH=${libpath}" "DYLD_LIBRARY_PATH=${libpath}" "PYTHONPATH=${pypath}") 86 | 87 | #generate a bat file that sets the environment and runs the test 88 | find_program(SHELL sh) 89 | set(sh_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.sh) 90 | file(WRITE ${sh_file} "#!${SHELL}\n") 91 | #each line sets an environment variable 92 | foreach(environ ${environs}) 93 | file(APPEND ${sh_file} "export ${environ}\n") 94 | endforeach(environ) 95 | #load the command to run with its arguments 96 | foreach(arg ${ARGN}) 97 | file(APPEND ${sh_file} "${arg} ") 98 | endforeach(arg) 99 | file(APPEND ${sh_file} "\n") 100 | 101 | #make the shell file executable 102 | execute_process(COMMAND chmod +x ${sh_file}) 103 | 104 | add_test(${test_name} ${SHELL} ${sh_file}) 105 | 106 | endif(UNIX) 107 | 108 | if(WIN32) 109 | list(APPEND libpath ${DLL_PATHS} "%PATH%") 110 | list(APPEND pypath "%PYTHONPATH%") 111 | 112 | #replace list separator with the path separator (escaped) 113 | string(REPLACE ";" "\\;" libpath "${libpath}") 114 | string(REPLACE ";" "\\;" pypath "${pypath}") 115 | list(APPEND environs "PATH=${libpath}" "PYTHONPATH=${pypath}") 116 | 117 | #generate a bat file that sets the environment and runs the test 118 | set(bat_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.bat) 119 | file(WRITE ${bat_file} "@echo off\n") 120 | #each line sets an environment variable 121 | foreach(environ ${environs}) 122 | file(APPEND ${bat_file} "SET ${environ}\n") 123 | endforeach(environ) 124 | #load the command to run with its arguments 125 | foreach(arg ${ARGN}) 126 | file(APPEND ${bat_file} "${arg} ") 127 | endforeach(arg) 128 | file(APPEND ${bat_file} "\n") 129 | 130 | add_test(${test_name} ${bat_file}) 131 | endif(WIN32) 132 | 133 | endfunction(GR_ADD_TEST) 134 | -------------------------------------------------------------------------------- /cmake/Modules/tpmsConfig.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_TPMS tpms) 3 | 4 | FIND_PATH( 5 | TPMS_INCLUDE_DIRS 6 | NAMES tpms/api.h 7 | HINTS $ENV{TPMS_DIR}/include 8 | ${PC_TPMS_INCLUDEDIR} 9 | PATHS ${CMAKE_INSTALL_PREEFIX}/include 10 | /usr/local/include 11 | /usr/include 12 | ) 13 | 14 | FIND_LIBRARY( 15 | TPMS_LIBRARIES 16 | NAMES gnuradio-tpms 17 | HINTS $ENV{TPMS_DIR}/lib 18 | ${PC_TPMS_LIBDIR} 19 | PATHS ${CMAKE_INSTALL_PREFIX}/lib 20 | ${CMAKE_INSTALL_PREFIX}/lib64 21 | /usr/local/lib 22 | /usr/local/lib64 23 | /usr/lib 24 | /usr/lib64 25 | ) 26 | 27 | INCLUDE(FindPackageHandleStandardArgs) 28 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(TPMS DEFAULT_MSG TPMS_LIBRARIES TPMS_INCLUDE_DIRS) 29 | MARK_AS_ADVANCED(TPMS_LIBRARIES TPMS_INCLUDE_DIRS) 30 | 31 | -------------------------------------------------------------------------------- /cmake/cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | # http://www.vtk.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F 2 | 3 | IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 4 | MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") 5 | ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 6 | 7 | FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 8 | STRING(REGEX REPLACE "\n" ";" files "${files}") 9 | FOREACH(file ${files}) 10 | MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") 11 | IF(EXISTS "$ENV{DESTDIR}${file}") 12 | EXEC_PROGRAM( 13 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 14 | OUTPUT_VARIABLE rm_out 15 | RETURN_VALUE rm_retval 16 | ) 17 | IF(NOT "${rm_retval}" STREQUAL 0) 18 | MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") 19 | ENDIF(NOT "${rm_retval}" STREQUAL 0) 20 | ELSEIF(IS_SYMLINK "$ENV{DESTDIR}${file}") 21 | EXEC_PROGRAM( 22 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 23 | OUTPUT_VARIABLE rm_out 24 | RETURN_VALUE rm_retval 25 | ) 26 | IF(NOT "${rm_retval}" STREQUAL 0) 27 | MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") 28 | ENDIF(NOT "${rm_retval}" STREQUAL 0) 29 | ELSE(EXISTS "$ENV{DESTDIR}${file}") 30 | MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") 31 | ENDIF(EXISTS "$ENV{DESTDIR}${file}") 32 | ENDFOREACH(file) 33 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Jared Boone . 2 | # 3 | # This is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 3, or (at your option) 6 | # any later version. 7 | # 8 | # This software is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this software; see the file COPYING. If not, write to 15 | # the Free Software Foundation, Inc., 51 Franklin Street, 16 | # Boston, MA 02110-1301, USA. 17 | 18 | ######################################################################## 19 | # Setup dependencies 20 | ######################################################################## 21 | find_package(Doxygen) 22 | 23 | ######################################################################## 24 | # Begin conditional configuration 25 | ######################################################################## 26 | if(ENABLE_DOXYGEN) 27 | 28 | ######################################################################## 29 | # Add subdirectories 30 | ######################################################################## 31 | add_subdirectory(doxygen) 32 | 33 | endif(ENABLE_DOXYGEN) 34 | -------------------------------------------------------------------------------- /docs/README.tpms: -------------------------------------------------------------------------------- 1 | This is the tpms-write-a-block package meant as a guide to building 2 | out-of-tree packages. To use the tpms blocks, the Python namespaces 3 | is in 'tpms', which is imported as: 4 | 5 | import tpms 6 | 7 | See the Doxygen documentation for details about the blocks available 8 | in this package. A quick listing of the details can be found in Python 9 | after importing by using: 10 | 11 | help(tpms) 12 | -------------------------------------------------------------------------------- /docs/doxygen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Jared Boone . 2 | # 3 | # This is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 3, or (at your option) 6 | # any later version. 7 | # 8 | # This software is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this software; see the file COPYING. If not, write to 15 | # the Free Software Foundation, Inc., 51 Franklin Street, 16 | # Boston, MA 02110-1301, USA. 17 | 18 | ######################################################################## 19 | # Create the doxygen configuration file 20 | ######################################################################## 21 | file(TO_NATIVE_PATH ${CMAKE_SOURCE_DIR} top_srcdir) 22 | file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR} top_builddir) 23 | file(TO_NATIVE_PATH ${CMAKE_SOURCE_DIR} abs_top_srcdir) 24 | file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR} abs_top_builddir) 25 | 26 | set(HAVE_DOT ${DOXYGEN_DOT_FOUND}) 27 | set(enable_html_docs YES) 28 | set(enable_latex_docs NO) 29 | set(enable_xml_docs YES) 30 | 31 | configure_file( 32 | ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in 33 | ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile 34 | @ONLY) 35 | 36 | set(BUILT_DIRS ${CMAKE_CURRENT_BINARY_DIR}/xml ${CMAKE_CURRENT_BINARY_DIR}/html) 37 | 38 | ######################################################################## 39 | # Make and install doxygen docs 40 | ######################################################################## 41 | add_custom_command( 42 | OUTPUT ${BUILT_DIRS} 43 | COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile 44 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 45 | COMMENT "Generating documentation with doxygen" 46 | ) 47 | 48 | add_custom_target(doxygen_target ALL DEPENDS ${BUILT_DIRS}) 49 | 50 | install(DIRECTORY ${BUILT_DIRS} DESTINATION ${GR_PKG_DOC_DIR}) 51 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2010 Free Software Foundation, Inc. 3 | # 4 | # This file is part of GNU Radio 5 | # 6 | # GNU Radio is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3, or (at your option) 9 | # any later version. 10 | # 11 | # GNU Radio is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with GNU Radio; see the file COPYING. If not, write to 18 | # the Free Software Foundation, Inc., 51 Franklin Street, 19 | # Boston, MA 02110-1301, USA. 20 | # 21 | """ 22 | Python interface to contents of doxygen xml documentation. 23 | 24 | Example use: 25 | See the contents of the example folder for the C++ and 26 | doxygen-generated xml used in this example. 27 | 28 | >>> # Parse the doxygen docs. 29 | >>> import os 30 | >>> this_dir = os.path.dirname(globals()['__file__']) 31 | >>> xml_path = this_dir + "/example/xml/" 32 | >>> di = DoxyIndex(xml_path) 33 | 34 | Get a list of all top-level objects. 35 | 36 | >>> print([mem.name() for mem in di.members()]) 37 | [u'Aadvark', u'aadvarky_enough', u'main'] 38 | 39 | Get all functions. 40 | 41 | >>> print([mem.name() for mem in di.in_category(DoxyFunction)]) 42 | [u'aadvarky_enough', u'main'] 43 | 44 | Check if an object is present. 45 | 46 | >>> di.has_member(u'Aadvark') 47 | True 48 | >>> di.has_member(u'Fish') 49 | False 50 | 51 | Get an item by name and check its properties. 52 | 53 | >>> aad = di.get_member(u'Aadvark') 54 | >>> print(aad.brief_description) 55 | Models the mammal Aadvark. 56 | >>> print(aad.detailed_description) 57 | Sadly the model is incomplete and cannot capture all aspects of an aadvark yet. 58 | 59 | This line is uninformative and is only to test line breaks in the comments. 60 | >>> [mem.name() for mem in aad.members()] 61 | [u'aadvarkness', u'print', u'Aadvark', u'get_aadvarkness'] 62 | >>> aad.get_member(u'print').brief_description 63 | u'Outputs the vital aadvark statistics.' 64 | 65 | """ 66 | 67 | from doxyindex import DoxyIndex, DoxyFunction, DoxyParam, DoxyClass, DoxyFile, DoxyNamespace, DoxyGroup, DoxyFriend, DoxyOther 68 | 69 | def _test(): 70 | import os 71 | this_dir = os.path.dirname(globals()['__file__']) 72 | xml_path = this_dir + "/example/xml/" 73 | di = DoxyIndex(xml_path) 74 | # Get the Aadvark class 75 | aad = di.get_member('Aadvark') 76 | aad.brief_description 77 | import doctest 78 | return doctest.testmod() 79 | 80 | if __name__ == "__main__": 81 | _test() 82 | 83 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/base.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2010 Free Software Foundation, Inc. 3 | # 4 | # This file is part of GNU Radio 5 | # 6 | # GNU Radio is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3, or (at your option) 9 | # any later version. 10 | # 11 | # GNU Radio is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with GNU Radio; see the file COPYING. If not, write to 18 | # the Free Software Foundation, Inc., 51 Franklin Street, 19 | # Boston, MA 02110-1301, USA. 20 | # 21 | """ 22 | A base class is created. 23 | 24 | Classes based upon this are used to make more user-friendly interfaces 25 | to the doxygen xml docs than the generated classes provide. 26 | """ 27 | 28 | import os 29 | import pdb 30 | 31 | from xml.parsers.expat import ExpatError 32 | 33 | from generated import compound 34 | 35 | 36 | class Base(object): 37 | 38 | class Duplicate(StandardError): 39 | pass 40 | 41 | class NoSuchMember(StandardError): 42 | pass 43 | 44 | class ParsingError(StandardError): 45 | pass 46 | 47 | def __init__(self, parse_data, top=None): 48 | self._parsed = False 49 | self._error = False 50 | self._parse_data = parse_data 51 | self._members = [] 52 | self._dict_members = {} 53 | self._in_category = {} 54 | self._data = {} 55 | if top is not None: 56 | self._xml_path = top._xml_path 57 | # Set up holder of references 58 | else: 59 | top = self 60 | self._refs = {} 61 | self._xml_path = parse_data 62 | self.top = top 63 | 64 | @classmethod 65 | def from_refid(cls, refid, top=None): 66 | """ Instantiate class from a refid rather than parsing object. """ 67 | # First check to see if its already been instantiated. 68 | if top is not None and refid in top._refs: 69 | return top._refs[refid] 70 | # Otherwise create a new instance and set refid. 71 | inst = cls(None, top=top) 72 | inst.refid = refid 73 | inst.add_ref(inst) 74 | return inst 75 | 76 | @classmethod 77 | def from_parse_data(cls, parse_data, top=None): 78 | refid = getattr(parse_data, 'refid', None) 79 | if refid is not None and top is not None and refid in top._refs: 80 | return top._refs[refid] 81 | inst = cls(parse_data, top=top) 82 | if refid is not None: 83 | inst.refid = refid 84 | inst.add_ref(inst) 85 | return inst 86 | 87 | def add_ref(self, obj): 88 | if hasattr(obj, 'refid'): 89 | self.top._refs[obj.refid] = obj 90 | 91 | mem_classes = [] 92 | 93 | def get_cls(self, mem): 94 | for cls in self.mem_classes: 95 | if cls.can_parse(mem): 96 | return cls 97 | raise StandardError(("Did not find a class for object '%s'." \ 98 | % (mem.get_name()))) 99 | 100 | def convert_mem(self, mem): 101 | try: 102 | cls = self.get_cls(mem) 103 | converted = cls.from_parse_data(mem, self.top) 104 | if converted is None: 105 | raise StandardError('No class matched this object.') 106 | self.add_ref(converted) 107 | return converted 108 | except StandardError, e: 109 | print e 110 | 111 | @classmethod 112 | def includes(cls, inst): 113 | return isinstance(inst, cls) 114 | 115 | @classmethod 116 | def can_parse(cls, obj): 117 | return False 118 | 119 | def _parse(self): 120 | self._parsed = True 121 | 122 | def _get_dict_members(self, cat=None): 123 | """ 124 | For given category a dictionary is returned mapping member names to 125 | members of that category. For names that are duplicated the name is 126 | mapped to None. 127 | """ 128 | self.confirm_no_error() 129 | if cat not in self._dict_members: 130 | new_dict = {} 131 | for mem in self.in_category(cat): 132 | if mem.name() not in new_dict: 133 | new_dict[mem.name()] = mem 134 | else: 135 | new_dict[mem.name()] = self.Duplicate 136 | self._dict_members[cat] = new_dict 137 | return self._dict_members[cat] 138 | 139 | def in_category(self, cat): 140 | self.confirm_no_error() 141 | if cat is None: 142 | return self._members 143 | if cat not in self._in_category: 144 | self._in_category[cat] = [mem for mem in self._members 145 | if cat.includes(mem)] 146 | return self._in_category[cat] 147 | 148 | def get_member(self, name, cat=None): 149 | self.confirm_no_error() 150 | # Check if it's in a namespace or class. 151 | bits = name.split('::') 152 | first = bits[0] 153 | rest = '::'.join(bits[1:]) 154 | member = self._get_dict_members(cat).get(first, self.NoSuchMember) 155 | # Raise any errors that are returned. 156 | if member in set([self.NoSuchMember, self.Duplicate]): 157 | raise member() 158 | if rest: 159 | return member.get_member(rest, cat=cat) 160 | return member 161 | 162 | def has_member(self, name, cat=None): 163 | try: 164 | mem = self.get_member(name, cat=cat) 165 | return True 166 | except self.NoSuchMember: 167 | return False 168 | 169 | def data(self): 170 | self.confirm_no_error() 171 | return self._data 172 | 173 | def members(self): 174 | self.confirm_no_error() 175 | return self._members 176 | 177 | def process_memberdefs(self): 178 | mdtss = [] 179 | for sec in self._retrieved_data.compounddef.sectiondef: 180 | mdtss += sec.memberdef 181 | # At the moment we lose all information associated with sections. 182 | # Sometimes a memberdef is in several sectiondef. 183 | # We make sure we don't get duplicates here. 184 | uniques = set([]) 185 | for mem in mdtss: 186 | converted = self.convert_mem(mem) 187 | pair = (mem.name, mem.__class__) 188 | if pair not in uniques: 189 | uniques.add(pair) 190 | self._members.append(converted) 191 | 192 | def retrieve_data(self): 193 | filename = os.path.join(self._xml_path, self.refid + '.xml') 194 | try: 195 | self._retrieved_data = compound.parse(filename) 196 | except ExpatError: 197 | print('Error in xml in file %s' % filename) 198 | self._error = True 199 | self._retrieved_data = None 200 | 201 | def check_parsed(self): 202 | if not self._parsed: 203 | self._parse() 204 | 205 | def confirm_no_error(self): 206 | self.check_parsed() 207 | if self._error: 208 | raise self.ParsingError() 209 | 210 | def error(self): 211 | self.check_parsed() 212 | return self._error 213 | 214 | def name(self): 215 | # first see if we can do it without processing. 216 | if self._parse_data is not None: 217 | return self._parse_data.name 218 | self.check_parsed() 219 | return self._retrieved_data.compounddef.name 220 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/doxyindex.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2010 Free Software Foundation, Inc. 3 | # 4 | # This file is part of GNU Radio 5 | # 6 | # GNU Radio is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3, or (at your option) 9 | # any later version. 10 | # 11 | # GNU Radio is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with GNU Radio; see the file COPYING. If not, write to 18 | # the Free Software Foundation, Inc., 51 Franklin Street, 19 | # Boston, MA 02110-1301, USA. 20 | # 21 | """ 22 | Classes providing more user-friendly interfaces to the doxygen xml 23 | docs than the generated classes provide. 24 | """ 25 | 26 | import os 27 | 28 | from generated import index 29 | from base import Base 30 | from text import description 31 | 32 | class DoxyIndex(Base): 33 | """ 34 | Parses a doxygen xml directory. 35 | """ 36 | 37 | __module__ = "gnuradio.utils.doxyxml" 38 | 39 | def _parse(self): 40 | if self._parsed: 41 | return 42 | super(DoxyIndex, self)._parse() 43 | self._root = index.parse(os.path.join(self._xml_path, 'index.xml')) 44 | for mem in self._root.compound: 45 | converted = self.convert_mem(mem) 46 | # For files we want the contents to be accessible directly 47 | # from the parent rather than having to go through the file 48 | # object. 49 | if self.get_cls(mem) == DoxyFile: 50 | if mem.name.endswith('.h'): 51 | self._members += converted.members() 52 | self._members.append(converted) 53 | else: 54 | self._members.append(converted) 55 | 56 | 57 | def generate_swig_doc_i(self): 58 | """ 59 | %feature("docstring") gr_make_align_on_samplenumbers_ss::align_state " 60 | Wraps the C++: gr_align_on_samplenumbers_ss::align_state"; 61 | """ 62 | pass 63 | 64 | 65 | class DoxyCompMem(Base): 66 | 67 | 68 | kind = None 69 | 70 | def __init__(self, *args, **kwargs): 71 | super(DoxyCompMem, self).__init__(*args, **kwargs) 72 | 73 | @classmethod 74 | def can_parse(cls, obj): 75 | return obj.kind == cls.kind 76 | 77 | def set_descriptions(self, parse_data): 78 | bd = description(getattr(parse_data, 'briefdescription', None)) 79 | dd = description(getattr(parse_data, 'detaileddescription', None)) 80 | self._data['brief_description'] = bd 81 | self._data['detailed_description'] = dd 82 | 83 | class DoxyCompound(DoxyCompMem): 84 | pass 85 | 86 | class DoxyMember(DoxyCompMem): 87 | pass 88 | 89 | 90 | class DoxyFunction(DoxyMember): 91 | 92 | __module__ = "gnuradio.utils.doxyxml" 93 | 94 | kind = 'function' 95 | 96 | def _parse(self): 97 | if self._parsed: 98 | return 99 | super(DoxyFunction, self)._parse() 100 | self.set_descriptions(self._parse_data) 101 | self._data['params'] = [] 102 | prms = self._parse_data.param 103 | for prm in prms: 104 | self._data['params'].append(DoxyParam(prm)) 105 | 106 | brief_description = property(lambda self: self.data()['brief_description']) 107 | detailed_description = property(lambda self: self.data()['detailed_description']) 108 | params = property(lambda self: self.data()['params']) 109 | 110 | Base.mem_classes.append(DoxyFunction) 111 | 112 | 113 | class DoxyParam(DoxyMember): 114 | 115 | __module__ = "gnuradio.utils.doxyxml" 116 | 117 | def _parse(self): 118 | if self._parsed: 119 | return 120 | super(DoxyParam, self)._parse() 121 | self.set_descriptions(self._parse_data) 122 | self._data['declname'] = self._parse_data.declname 123 | 124 | brief_description = property(lambda self: self.data()['brief_description']) 125 | detailed_description = property(lambda self: self.data()['detailed_description']) 126 | declname = property(lambda self: self.data()['declname']) 127 | 128 | class DoxyClass(DoxyCompound): 129 | 130 | __module__ = "gnuradio.utils.doxyxml" 131 | 132 | kind = 'class' 133 | 134 | def _parse(self): 135 | if self._parsed: 136 | return 137 | super(DoxyClass, self)._parse() 138 | self.retrieve_data() 139 | if self._error: 140 | return 141 | self.set_descriptions(self._retrieved_data.compounddef) 142 | # Sectiondef.kind tells about whether private or public. 143 | # We just ignore this for now. 144 | self.process_memberdefs() 145 | 146 | brief_description = property(lambda self: self.data()['brief_description']) 147 | detailed_description = property(lambda self: self.data()['detailed_description']) 148 | 149 | Base.mem_classes.append(DoxyClass) 150 | 151 | 152 | class DoxyFile(DoxyCompound): 153 | 154 | __module__ = "gnuradio.utils.doxyxml" 155 | 156 | kind = 'file' 157 | 158 | def _parse(self): 159 | if self._parsed: 160 | return 161 | super(DoxyFile, self)._parse() 162 | self.retrieve_data() 163 | self.set_descriptions(self._retrieved_data.compounddef) 164 | if self._error: 165 | return 166 | self.process_memberdefs() 167 | 168 | brief_description = property(lambda self: self.data()['brief_description']) 169 | detailed_description = property(lambda self: self.data()['detailed_description']) 170 | 171 | Base.mem_classes.append(DoxyFile) 172 | 173 | 174 | class DoxyNamespace(DoxyCompound): 175 | 176 | __module__ = "gnuradio.utils.doxyxml" 177 | 178 | kind = 'namespace' 179 | 180 | Base.mem_classes.append(DoxyNamespace) 181 | 182 | 183 | class DoxyGroup(DoxyCompound): 184 | 185 | __module__ = "gnuradio.utils.doxyxml" 186 | 187 | kind = 'group' 188 | 189 | def _parse(self): 190 | if self._parsed: 191 | return 192 | super(DoxyGroup, self)._parse() 193 | self.retrieve_data() 194 | if self._error: 195 | return 196 | cdef = self._retrieved_data.compounddef 197 | self._data['title'] = description(cdef.title) 198 | # Process inner groups 199 | grps = cdef.innergroup 200 | for grp in grps: 201 | converted = DoxyGroup.from_refid(grp.refid, top=self.top) 202 | self._members.append(converted) 203 | # Process inner classes 204 | klasses = cdef.innerclass 205 | for kls in klasses: 206 | converted = DoxyClass.from_refid(kls.refid, top=self.top) 207 | self._members.append(converted) 208 | # Process normal members 209 | self.process_memberdefs() 210 | 211 | title = property(lambda self: self.data()['title']) 212 | 213 | 214 | Base.mem_classes.append(DoxyGroup) 215 | 216 | 217 | class DoxyFriend(DoxyMember): 218 | 219 | __module__ = "gnuradio.utils.doxyxml" 220 | 221 | kind = 'friend' 222 | 223 | Base.mem_classes.append(DoxyFriend) 224 | 225 | 226 | class DoxyOther(Base): 227 | 228 | __module__ = "gnuradio.utils.doxyxml" 229 | 230 | kinds = set(['variable', 'struct', 'union', 'define', 'typedef', 'enum', 'dir', 'page']) 231 | 232 | @classmethod 233 | def can_parse(cls, obj): 234 | return obj.kind in cls.kinds 235 | 236 | Base.mem_classes.append(DoxyOther) 237 | 238 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/generated/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Contains generated files produced by generateDS.py. 3 | 4 | These do the real work of parsing the doxygen xml files but the 5 | resultant classes are not very friendly to navigate so the rest of the 6 | doxyxml module processes them further. 7 | """ 8 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/generated/index.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | Generated Mon Feb 9 19:08:05 2009 by generateDS.py. 5 | """ 6 | 7 | from xml.dom import minidom 8 | 9 | import os 10 | import sys 11 | import compound 12 | 13 | import indexsuper as supermod 14 | 15 | class DoxygenTypeSub(supermod.DoxygenType): 16 | def __init__(self, version=None, compound=None): 17 | supermod.DoxygenType.__init__(self, version, compound) 18 | 19 | def find_compounds_and_members(self, details): 20 | """ 21 | Returns a list of all compounds and their members which match details 22 | """ 23 | 24 | results = [] 25 | for compound in self.compound: 26 | members = compound.find_members(details) 27 | if members: 28 | results.append([compound, members]) 29 | else: 30 | if details.match(compound): 31 | results.append([compound, []]) 32 | 33 | return results 34 | 35 | supermod.DoxygenType.subclass = DoxygenTypeSub 36 | # end class DoxygenTypeSub 37 | 38 | 39 | class CompoundTypeSub(supermod.CompoundType): 40 | def __init__(self, kind=None, refid=None, name='', member=None): 41 | supermod.CompoundType.__init__(self, kind, refid, name, member) 42 | 43 | def find_members(self, details): 44 | """ 45 | Returns a list of all members which match details 46 | """ 47 | 48 | results = [] 49 | 50 | for member in self.member: 51 | if details.match(member): 52 | results.append(member) 53 | 54 | return results 55 | 56 | supermod.CompoundType.subclass = CompoundTypeSub 57 | # end class CompoundTypeSub 58 | 59 | 60 | class MemberTypeSub(supermod.MemberType): 61 | 62 | def __init__(self, kind=None, refid=None, name=''): 63 | supermod.MemberType.__init__(self, kind, refid, name) 64 | 65 | supermod.MemberType.subclass = MemberTypeSub 66 | # end class MemberTypeSub 67 | 68 | 69 | def parse(inFilename): 70 | 71 | doc = minidom.parse(inFilename) 72 | rootNode = doc.documentElement 73 | rootObj = supermod.DoxygenType.factory() 74 | rootObj.build(rootNode) 75 | 76 | return rootObj 77 | 78 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/text.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2010 Free Software Foundation, Inc. 3 | # 4 | # This file is part of GNU Radio 5 | # 6 | # GNU Radio is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3, or (at your option) 9 | # any later version. 10 | # 11 | # GNU Radio is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with GNU Radio; see the file COPYING. If not, write to 18 | # the Free Software Foundation, Inc., 51 Franklin Street, 19 | # Boston, MA 02110-1301, USA. 20 | # 21 | """ 22 | Utilities for extracting text from generated classes. 23 | """ 24 | 25 | def is_string(txt): 26 | if isinstance(txt, str): 27 | return True 28 | try: 29 | if isinstance(txt, unicode): 30 | return True 31 | except NameError: 32 | pass 33 | return False 34 | 35 | def description(obj): 36 | if obj is None: 37 | return None 38 | return description_bit(obj).strip() 39 | 40 | def description_bit(obj): 41 | if hasattr(obj, 'content'): 42 | contents = [description_bit(item) for item in obj.content] 43 | result = ''.join(contents) 44 | elif hasattr(obj, 'content_'): 45 | contents = [description_bit(item) for item in obj.content_] 46 | result = ''.join(contents) 47 | elif hasattr(obj, 'value'): 48 | result = description_bit(obj.value) 49 | elif is_string(obj): 50 | return obj 51 | else: 52 | raise StandardError('Expecting a string or something with content, content_ or value attribute') 53 | # If this bit is a paragraph then add one some line breaks. 54 | if hasattr(obj, 'name') and obj.name == 'para': 55 | result += "\n\n" 56 | return result 57 | -------------------------------------------------------------------------------- /docs/doxygen/other/group_defs.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | * \defgroup block GNU Radio TPMS C++ Signal Processing Blocks 3 | * \brief All C++ blocks that can be used from the TPMS GNU Radio 4 | * module are listed here or in the subcategories below. 5 | * 6 | */ 7 | 8 | -------------------------------------------------------------------------------- /docs/doxygen/other/main_page.dox: -------------------------------------------------------------------------------- 1 | /*! \mainpage 2 | 3 | Welcome to the GNU Radio TPMS Block 4 | 5 | This is the intro page for the Doxygen manual generated for the TPMS 6 | block (docs/doxygen/other/main_page.dox). Edit it to add more detailed 7 | documentation about the new GNU Radio modules contained in this 8 | project. 9 | 10 | */ 11 | -------------------------------------------------------------------------------- /docs/doxygen/swig_doc.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2010,2011 Free Software Foundation, Inc. 3 | # 4 | # This file is part of GNU Radio 5 | # 6 | # GNU Radio is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3, or (at your option) 9 | # any later version. 10 | # 11 | # GNU Radio is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with GNU Radio; see the file COPYING. If not, write to 18 | # the Free Software Foundation, Inc., 51 Franklin Street, 19 | # Boston, MA 02110-1301, USA. 20 | # 21 | """ 22 | Creates the swig_doc.i SWIG interface file. 23 | Execute using: python swig_doc.py xml_path outputfilename 24 | 25 | The file instructs SWIG to transfer the doxygen comments into the 26 | python docstrings. 27 | 28 | """ 29 | 30 | import sys 31 | 32 | try: 33 | from doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base 34 | except ImportError: 35 | from gnuradio.doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base 36 | 37 | 38 | def py_name(name): 39 | bits = name.split('_') 40 | return '_'.join(bits[1:]) 41 | 42 | def make_name(name): 43 | bits = name.split('_') 44 | return bits[0] + '_make_' + '_'.join(bits[1:]) 45 | 46 | 47 | class Block(object): 48 | """ 49 | Checks if doxyxml produced objects correspond to a gnuradio block. 50 | """ 51 | 52 | @classmethod 53 | def includes(cls, item): 54 | if not isinstance(item, DoxyClass): 55 | return False 56 | # Check for a parsing error. 57 | if item.error(): 58 | return False 59 | return item.has_member(make_name(item.name()), DoxyFriend) 60 | 61 | 62 | def utoascii(text): 63 | """ 64 | Convert unicode text into ascii and escape quotes. 65 | """ 66 | if text is None: 67 | return '' 68 | out = text.encode('ascii', 'replace') 69 | out = out.replace('"', '\\"') 70 | return out 71 | 72 | 73 | def combine_descriptions(obj): 74 | """ 75 | Combines the brief and detailed descriptions of an object together. 76 | """ 77 | description = [] 78 | bd = obj.brief_description.strip() 79 | dd = obj.detailed_description.strip() 80 | if bd: 81 | description.append(bd) 82 | if dd: 83 | description.append(dd) 84 | return utoascii('\n\n'.join(description)).strip() 85 | 86 | 87 | entry_templ = '%feature("docstring") {name} "{docstring}"' 88 | def make_entry(obj, name=None, templ="{description}", description=None): 89 | """ 90 | Create a docstring entry for a swig interface file. 91 | 92 | obj - a doxyxml object from which documentation will be extracted. 93 | name - the name of the C object (defaults to obj.name()) 94 | templ - an optional template for the docstring containing only one 95 | variable named 'description'. 96 | description - if this optional variable is set then it's value is 97 | used as the description instead of extracting it from obj. 98 | """ 99 | if name is None: 100 | name=obj.name() 101 | if "operator " in name: 102 | return '' 103 | if description is None: 104 | description = combine_descriptions(obj) 105 | docstring = templ.format(description=description) 106 | if not docstring: 107 | return '' 108 | return entry_templ.format( 109 | name=name, 110 | docstring=docstring, 111 | ) 112 | 113 | 114 | def make_func_entry(func, name=None, description=None, params=None): 115 | """ 116 | Create a function docstring entry for a swig interface file. 117 | 118 | func - a doxyxml object from which documentation will be extracted. 119 | name - the name of the C object (defaults to func.name()) 120 | description - if this optional variable is set then it's value is 121 | used as the description instead of extracting it from func. 122 | params - a parameter list that overrides using func.params. 123 | """ 124 | if params is None: 125 | params = func.params 126 | params = [prm.declname for prm in params] 127 | if params: 128 | sig = "Params: (%s)" % ", ".join(params) 129 | else: 130 | sig = "Params: (NONE)" 131 | templ = "{description}\n\n" + sig 132 | return make_entry(func, name=name, templ=utoascii(templ), 133 | description=description) 134 | 135 | 136 | def make_class_entry(klass, description=None): 137 | """ 138 | Create a class docstring for a swig interface file. 139 | """ 140 | output = [] 141 | output.append(make_entry(klass, description=description)) 142 | for func in klass.in_category(DoxyFunction): 143 | name = klass.name() + '::' + func.name() 144 | output.append(make_func_entry(func, name=name)) 145 | return "\n\n".join(output) 146 | 147 | 148 | def make_block_entry(di, block): 149 | """ 150 | Create class and function docstrings of a gnuradio block for a 151 | swig interface file. 152 | """ 153 | descriptions = [] 154 | # Get the documentation associated with the class. 155 | class_desc = combine_descriptions(block) 156 | if class_desc: 157 | descriptions.append(class_desc) 158 | # Get the documentation associated with the make function 159 | make_func = di.get_member(make_name(block.name()), DoxyFunction) 160 | make_func_desc = combine_descriptions(make_func) 161 | if make_func_desc: 162 | descriptions.append(make_func_desc) 163 | # Get the documentation associated with the file 164 | try: 165 | block_file = di.get_member(block.name() + ".h", DoxyFile) 166 | file_desc = combine_descriptions(block_file) 167 | if file_desc: 168 | descriptions.append(file_desc) 169 | except base.Base.NoSuchMember: 170 | # Don't worry if we can't find a matching file. 171 | pass 172 | # And join them all together to make a super duper description. 173 | super_description = "\n\n".join(descriptions) 174 | # Associate the combined description with the class and 175 | # the make function. 176 | output = [] 177 | output.append(make_class_entry(block, description=super_description)) 178 | creator = block.get_member(block.name(), DoxyFunction) 179 | output.append(make_func_entry(make_func, description=super_description, 180 | params=creator.params)) 181 | return "\n\n".join(output) 182 | 183 | 184 | def make_swig_interface_file(di, swigdocfilename, custom_output=None): 185 | 186 | output = [""" 187 | /* 188 | * This file was automatically generated using swig_doc.py. 189 | * 190 | * Any changes to it will be lost next time it is regenerated. 191 | */ 192 | """] 193 | 194 | if custom_output is not None: 195 | output.append(custom_output) 196 | 197 | # Create docstrings for the blocks. 198 | blocks = di.in_category(Block) 199 | make_funcs = set([]) 200 | for block in blocks: 201 | try: 202 | make_func = di.get_member(make_name(block.name()), DoxyFunction) 203 | make_funcs.add(make_func.name()) 204 | output.append(make_block_entry(di, block)) 205 | except block.ParsingError: 206 | print('Parsing error for block %s' % block.name()) 207 | 208 | # Create docstrings for functions 209 | # Don't include the make functions since they have already been dealt with. 210 | funcs = [f for f in di.in_category(DoxyFunction) if f.name() not in make_funcs] 211 | for f in funcs: 212 | try: 213 | output.append(make_func_entry(f)) 214 | except f.ParsingError: 215 | print('Parsing error for function %s' % f.name()) 216 | 217 | # Create docstrings for classes 218 | block_names = [block.name() for block in blocks] 219 | klasses = [k for k in di.in_category(DoxyClass) if k.name() not in block_names] 220 | for k in klasses: 221 | try: 222 | output.append(make_class_entry(k)) 223 | except k.ParsingError: 224 | print('Parsing error for class %s' % k.name()) 225 | 226 | # Docstrings are not created for anything that is not a function or a class. 227 | # If this excludes anything important please add it here. 228 | 229 | output = "\n\n".join(output) 230 | 231 | swig_doc = file(swigdocfilename, 'w') 232 | swig_doc.write(output) 233 | swig_doc.close() 234 | 235 | if __name__ == "__main__": 236 | # Parse command line options and set up doxyxml. 237 | err_msg = "Execute using: python swig_doc.py xml_path outputfilename" 238 | if len(sys.argv) != 3: 239 | raise StandardError(err_msg) 240 | xml_path = sys.argv[1] 241 | swigdocfilename = sys.argv[2] 242 | di = DoxyIndex(xml_path) 243 | 244 | # gnuradio.gr.msq_queue.insert_tail and delete_head create errors unless docstrings are defined! 245 | # This is presumably a bug in SWIG. 246 | #msg_q = di.get_member(u'gr_msg_queue', DoxyClass) 247 | #insert_tail = msg_q.get_member(u'insert_tail', DoxyFunction) 248 | #delete_head = msg_q.get_member(u'delete_head', DoxyFunction) 249 | output = [] 250 | #output.append(make_func_entry(insert_tail, name='gr_py_msg_queue__insert_tail')) 251 | #output.append(make_func_entry(delete_head, name='gr_py_msg_queue__delete_head')) 252 | custom_output = "\n\n".join(output) 253 | 254 | # Generate the docstrings interface file. 255 | make_swig_interface_file(di, swigdocfilename, custom_output=custom_output) 256 | -------------------------------------------------------------------------------- /examples/README: -------------------------------------------------------------------------------- 1 | It is considered good practice to add examples in here to demonstrate the 2 | functionality of your OOT module. Python scripts, GRC flow graphs or other 3 | code can go here. 4 | 5 | -------------------------------------------------------------------------------- /grc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Jared Boone . 2 | # 3 | # This is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 3, or (at your option) 6 | # any later version. 7 | # 8 | # This software is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this software; see the file COPYING. If not, write to 15 | # the Free Software Foundation, Inc., 51 Franklin Street, 16 | # Boston, MA 02110-1301, USA. 17 | 18 | install(FILES 19 | tpms_ask_env.xml 20 | tpms_fixed_length_frame_sink.xml 21 | tpms_burst_detector.xml DESTINATION share/gnuradio/grc/blocks 22 | ) 23 | -------------------------------------------------------------------------------- /grc/tpms_ask_env.xml: -------------------------------------------------------------------------------- 1 | 2 | Ask env 3 | tpms_ask_env 4 | TPMS 5 | import tpms 6 | tpms.ask_env( 7 | alpha=$alpha 8 | ) 9 | set_alpha($alpha) 10 | 11 | Alpha 12 | alpha 13 | float 14 | 15 | 16 | in 17 | float 18 | 19 | 20 | out 21 | float 22 | 23 | 24 | -------------------------------------------------------------------------------- /grc/tpms_burst_detector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | burst_detector 4 | tpms_burst_detector 5 | tpms 6 | import tpms 7 | tpms.burst_detector() 8 | 13 | 14 | ... 15 | ... 16 | ... 17 | 18 | 19 | 24 | 25 | in 26 | 27 | 28 | 29 | 34 | 35 | out 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /grc/tpms_fixed_length_frame_sink.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | fixed_length_frame_sink 4 | tpms_fixed_length_frame_sink 5 | tpms 6 | import tpms 7 | tpms.fixed_length_frame_sink($frame_length, $target_queue) 8 | 13 | 14 | ... 15 | ... 16 | ... 17 | 18 | 19 | 24 | 25 | in 26 | 27 | 28 | 29 | 34 | 35 | out 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /include/tpms/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Jared Boone . 2 | # 3 | # This is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 3, or (at your option) 6 | # any later version. 7 | # 8 | # This software is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this software; see the file COPYING. If not, write to 15 | # the Free Software Foundation, Inc., 51 Franklin Street, 16 | # Boston, MA 02110-1301, USA. 17 | 18 | ######################################################################## 19 | # Install public header files 20 | ######################################################################## 21 | install(FILES 22 | api.h 23 | ask_env.h 24 | fixed_length_frame_sink.h 25 | burst_detector.h DESTINATION include/tpms 26 | ) 27 | -------------------------------------------------------------------------------- /include/tpms/api.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifndef INCLUDED_TPMS_API_H 22 | #define INCLUDED_TPMS_API_H 23 | 24 | #include 25 | 26 | #ifdef gnuradio_tpms_EXPORTS 27 | # define TPMS_API __GR_ATTR_EXPORT 28 | #else 29 | # define TPMS_API __GR_ATTR_IMPORT 30 | #endif 31 | 32 | #endif /* INCLUDED_TPMS_API_H */ 33 | -------------------------------------------------------------------------------- /include/tpms/ask_env.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifndef INCLUDED_TPMS_ASK_ENV_H 22 | #define INCLUDED_TPMS_ASK_ENV_H 23 | 24 | #include 25 | #include 26 | 27 | namespace gr { 28 | namespace tpms { 29 | 30 | /*! 31 | * \brief Tire Pressure Monitoring System 32 | * \ingroup tpms 33 | * 34 | */ 35 | class TPMS_API ask_env : virtual public gr::sync_block 36 | { 37 | public: 38 | typedef boost::shared_ptr sptr; 39 | 40 | /*! 41 | * \class ask_env 42 | * \brief Bipolar envelope detection and scaling to +/- 1.0. 43 | * \param alpha Multiplicative ate at which envelope peaks decay. 44 | */ 45 | static sptr make(float alpha); 46 | 47 | virtual void set_alpha(float var) = 0; 48 | virtual float alpha() = 0; 49 | }; 50 | 51 | } // namespace tpms 52 | } // namespace gr 53 | 54 | #endif /* INCLUDED_TPMS_ASK_ENV_H */ 55 | 56 | -------------------------------------------------------------------------------- /include/tpms/burst_detector.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifndef INCLUDED_TPMS_BURST_DETECTOR_H 22 | #define INCLUDED_TPMS_BURST_DETECTOR_H 23 | 24 | #include 25 | #include 26 | 27 | namespace gr { 28 | namespace tpms { 29 | 30 | /*! 31 | * \brief Identify and tag stream ranges that contain significant bursts of energy. 32 | * \ingroup tpms 33 | * 34 | */ 35 | class TPMS_API burst_detector : virtual public gr::block 36 | { 37 | public: 38 | typedef boost::shared_ptr sptr; 39 | 40 | /*! 41 | * \brief Return a shared_ptr to a new instance of tpms::burst_detector. 42 | * 43 | * To avoid accidental use of raw pointers, tpms::burst_detector's 44 | * constructor is in a private implementation 45 | * class. tpms::burst_detector::make is the public interface for 46 | * creating new instances. 47 | */ 48 | static sptr make(); 49 | }; 50 | 51 | } // namespace tpms 52 | } // namespace gr 53 | 54 | #endif /* INCLUDED_TPMS_BURST_DETECTOR_H */ 55 | 56 | -------------------------------------------------------------------------------- /include/tpms/fixed_length_frame_sink.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifndef INCLUDED_TPMS_FIXED_LENGTH_FRAME_SINK_H 22 | #define INCLUDED_TPMS_FIXED_LENGTH_FRAME_SINK_H 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | namespace gr { 29 | namespace tpms { 30 | 31 | /*! 32 | * \brief <+description of block+> 33 | * \ingroup tpms 34 | * 35 | */ 36 | class TPMS_API fixed_length_frame_sink : virtual public gr::sync_block 37 | { 38 | public: 39 | typedef boost::shared_ptr sptr; 40 | 41 | /*! 42 | * \brief Return a shared_ptr to a new instance of tpms::fixed_length_frame_sink. 43 | * 44 | * To avoid accidental use of raw pointers, tpms::fixed_length_frame_sink's 45 | * constructor is in a private implementation 46 | * class. tpms::fixed_length_frame_sink::make is the public interface for 47 | * creating new instances. 48 | */ 49 | static sptr make(int frame_length, pmt::pmt_t attributes); 50 | }; 51 | 52 | } // namespace tpms 53 | } // namespace gr 54 | 55 | #endif /* INCLUDED_TPMS_FIXED_LENGTH_FRAME_SINK_H */ 56 | 57 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Jared Boone . 2 | # 3 | # This is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 3, or (at your option) 6 | # any later version. 7 | # 8 | # This software is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this software; see the file COPYING. If not, write to 15 | # the Free Software Foundation, Inc., 51 Franklin Street, 16 | # Boston, MA 02110-1301, USA. 17 | 18 | ######################################################################## 19 | # Setup library 20 | ######################################################################## 21 | include(GrPlatform) #define LIB_SUFFIX 22 | 23 | include_directories(${Boost_INCLUDE_DIR}) 24 | link_directories(${Boost_LIBRARY_DIRS} ${GNURADIO_RUNTIME_LIBRARY_DIRS}) 25 | 26 | list(APPEND tpms_sources 27 | ask_env_impl.cc 28 | fixed_length_frame_sink_impl.cc 29 | burst_detector_impl.cc 30 | ) 31 | 32 | add_library(gnuradio-tpms SHARED ${tpms_sources}) 33 | target_link_libraries(gnuradio-tpms ${Boost_LIBRARIES} ${GNURADIO_RUNTIME_LIBRARIES} ${GNURADIO_ALL_LIBRARIES} fftw3f) 34 | set_target_properties(gnuradio-tpms PROPERTIES DEFINE_SYMBOL "gnuradio_tpms_EXPORTS") 35 | 36 | ######################################################################## 37 | # Install built library files 38 | ######################################################################## 39 | install(TARGETS gnuradio-tpms 40 | LIBRARY DESTINATION lib${LIB_SUFFIX} # .so/.dylib file 41 | ARCHIVE DESTINATION lib${LIB_SUFFIX} # .lib file 42 | RUNTIME DESTINATION bin # .dll file 43 | ) 44 | 45 | ######################################################################## 46 | # Build and register unit test 47 | ######################################################################## 48 | include(GrTest) 49 | 50 | include_directories(${CPPUNIT_INCLUDE_DIRS}) 51 | 52 | list(APPEND test_tpms_sources 53 | ${CMAKE_CURRENT_SOURCE_DIR}/test_tpms.cc 54 | ${CMAKE_CURRENT_SOURCE_DIR}/qa_tpms.cc 55 | ${CMAKE_CURRENT_SOURCE_DIR}/qa_fixed_length_frame_sink.cc 56 | ${CMAKE_CURRENT_SOURCE_DIR}/qa_ask_env.cc 57 | ) 58 | 59 | add_executable(test-tpms ${test_tpms_sources}) 60 | 61 | target_link_libraries( 62 | test-tpms 63 | ${GNURADIO_RUNTIME_LIBRARIES} 64 | ${Boost_LIBRARIES} 65 | ${CPPUNIT_LIBRARIES} 66 | gnuradio-tpms 67 | ) 68 | 69 | GR_ADD_TEST(test_tpms test-tpms) 70 | -------------------------------------------------------------------------------- /lib/ask_env_impl.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include 26 | #include "ask_env_impl.h" 27 | 28 | namespace gr { 29 | namespace tpms { 30 | 31 | ask_env::sptr 32 | ask_env::make(float alpha) 33 | { 34 | return gnuradio::get_initial_sptr 35 | (new ask_env_impl(alpha)); 36 | } 37 | 38 | /* 39 | * The private constructor 40 | */ 41 | ask_env_impl::ask_env_impl(float alpha) 42 | : gr::sync_block("ask_env", 43 | gr::io_signature::make(1, 1, sizeof(float)), 44 | gr::io_signature::make(1, 1, sizeof(float))), 45 | d_max(0), 46 | d_min(0) 47 | { 48 | set_alpha(alpha); 49 | } 50 | 51 | /* 52 | * Our virtual destructor. 53 | */ 54 | ask_env_impl::~ask_env_impl() 55 | { 56 | } 57 | 58 | float 59 | ask_env_impl::alpha() { 60 | return d_alpha; 61 | } 62 | 63 | void 64 | ask_env_impl::set_alpha(float alpha) { 65 | d_alpha = alpha; 66 | } 67 | 68 | int 69 | ask_env_impl::work(int noutput_items, 70 | gr_vector_const_void_star &input_items, 71 | gr_vector_void_star &output_items) 72 | { 73 | const float *in = (const float*)input_items[0]; 74 | float *out = (float*)output_items[0]; 75 | 76 | for(int i=0; i d_max ) { 78 | d_max = in[i]; 79 | } 80 | 81 | if( in[i] < d_min ) { 82 | d_min = in[i]; 83 | } 84 | 85 | const float diff = d_max - d_min; 86 | if( diff > 0.0f ) { 87 | out[i] = (in[i] - d_min) / (diff * 0.5f) - 1.0f; 88 | } else { 89 | out[i] = 0.0f; 90 | } 91 | 92 | d_max -= diff * d_alpha; 93 | d_min += diff * d_alpha; 94 | } 95 | 96 | // Tell runtime system how many output items we produced. 97 | return noutput_items; 98 | } 99 | 100 | } /* namespace tpms */ 101 | } /* namespace gr */ 102 | 103 | -------------------------------------------------------------------------------- /lib/ask_env_impl.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifndef INCLUDED_TPMS_ASK_ENV_IMPL_H 22 | #define INCLUDED_TPMS_ASK_ENV_IMPL_H 23 | 24 | #include 25 | 26 | namespace gr { 27 | namespace tpms { 28 | 29 | class ask_env_impl : public ask_env 30 | { 31 | private: 32 | float d_max; 33 | float d_min; 34 | float d_alpha; 35 | 36 | public: 37 | ask_env_impl(float alpha); 38 | ~ask_env_impl(); 39 | 40 | void set_alpha(float alpha); 41 | float alpha(); 42 | 43 | int work(int noutput_items, 44 | gr_vector_const_void_star &input_items, 45 | gr_vector_void_star &output_items); 46 | }; 47 | 48 | } // namespace tpms 49 | } // namespace gr 50 | 51 | #endif /* INCLUDED_TPMS_ASK_ENV_IMPL_H */ 52 | 53 | -------------------------------------------------------------------------------- /lib/burst_detector_impl.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include "burst_detector_impl.h" 30 | 31 | /* Good reference: 32 | * http://gnuradio.squarespace.com/storage/tutorial/gr_scheduler_overview.pdf 33 | */ 34 | 35 | /* TODO: Terrible hack for volk_malloc not being present before 36 | * GNU Radio 3.7.2.1-something. Remove this and require GR>=3.7.3? */ 37 | #if !defined(volk_malloc) 38 | #define volk_malloc(x, y) (fftwf_malloc(x)) 39 | #endif 40 | #if !defined(volk_free) 41 | #define volk_free(x) (fftwf_free(x)) 42 | #endif 43 | 44 | namespace gr { 45 | namespace tpms { 46 | 47 | burst_detector::sptr 48 | burst_detector::make() 49 | { 50 | return gnuradio::get_initial_sptr 51 | (new burst_detector_impl()); 52 | } 53 | 54 | /* 55 | * The private constructor 56 | */ 57 | burst_detector_impl::burst_detector_impl() 58 | : gr::block("burst_detector", 59 | gr::io_signature::make(1, 1, sizeof(gr_complex)), 60 | gr::io_signature::make(1, 1, sizeof(gr_complex))), 61 | d_block_size(1024), 62 | d_advance(d_block_size / 2), 63 | d_readahead_items(d_advance) 64 | { 65 | d_burst = false; 66 | d_tag_burst = pmt::mp("burst"); 67 | 68 | d_hysteresis_timeout = 2; 69 | d_hysteresis_count = 0; 70 | 71 | d_fft_window = (float*)volk_malloc(d_block_size * sizeof(float), 256); 72 | assert((d_fft_window & 0xff) == 0); 73 | 74 | std::vector window = fft::window::hann(d_block_size); 75 | std::copy(window.begin(), window.end(), d_fft_window); 76 | 77 | d_temp_f = (float*)volk_malloc(d_block_size * sizeof(float), 256); 78 | assert((d_temp_f * 0xff) == 0); 79 | 80 | d_fft_in = (gr_complex*)fftwf_malloc(sizeof(gr_complex) * d_block_size); 81 | d_fft_out = (gr_complex*)fftwf_malloc(sizeof(gr_complex) * d_block_size); 82 | d_fft_plan = fftwf_plan_dft_1d(d_block_size, reinterpret_cast(d_fft_in), reinterpret_cast(d_fft_out), FFTW_FORWARD, FFTW_PATIENT | FFTW_DESTROY_INPUT); 83 | 84 | set_history(d_readahead_items + 1); 85 | set_output_multiple(d_advance); 86 | } 87 | 88 | /* 89 | * Our virtual destructor. 90 | */ 91 | burst_detector_impl::~burst_detector_impl() 92 | { 93 | fftwf_destroy_plan(d_fft_plan); 94 | fftwf_free(d_fft_out); 95 | fftwf_free(d_fft_in); 96 | 97 | volk_free(d_temp_f); 98 | volk_free(d_fft_window); 99 | } 100 | 101 | void 102 | burst_detector_impl::forecast(int noutput_items, 103 | gr_vector_int &ninput_items_required) 104 | { 105 | size_t block_count = ceilf(float(noutput_items) / d_advance); 106 | ninput_items_required[0] = block_count * d_advance + history() - 1; 107 | } 108 | 109 | int 110 | burst_detector_impl::general_work(int noutput_items, 111 | gr_vector_int &ninput_items, 112 | gr_vector_const_void_star &input_items, 113 | gr_vector_void_star &output_items) 114 | { 115 | const gr_complex *in = (const gr_complex*)input_items[0]; 116 | gr_complex *out = (gr_complex*)output_items[0]; 117 | 118 | const size_t block_count = std::min(ninput_items[0], noutput_items) / d_advance; 119 | 120 | const uint64_t _nitems_written = nitems_written(0); 121 | for(size_t block_n=0; block_n mean ) { 132 | d_hysteresis_count = d_hysteresis_timeout; 133 | } else { 134 | d_hysteresis_count = d_hysteresis_count ? (d_hysteresis_count - 1) : 0; 135 | } 136 | 137 | if( d_hysteresis_count ) { 138 | if( d_burst == false ) { 139 | add_item_tag(0, _nitems_written + index_start, d_tag_burst, pmt::PMT_T); 140 | d_burst = true; 141 | } 142 | } else { 143 | if( d_burst == true ) { 144 | add_item_tag(0, _nitems_written + index_start + d_block_size - 1, d_tag_burst, pmt::PMT_F); 145 | d_burst = false; 146 | } 147 | } 148 | } 149 | 150 | //noutput_items = block_count * d_block_size; 151 | // Effective delay of d_advance samples. 152 | memcpy(out, in, noutput_items * sizeof(gr_complex)); 153 | 154 | consume_each(noutput_items); 155 | 156 | return noutput_items; 157 | } 158 | 159 | } /* namespace tpms */ 160 | } /* namespace gr */ 161 | 162 | -------------------------------------------------------------------------------- /lib/burst_detector_impl.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifndef INCLUDED_TPMS_BURST_DETECTOR_IMPL_H 22 | #define INCLUDED_TPMS_BURST_DETECTOR_IMPL_H 23 | 24 | #include 25 | 26 | #include 27 | 28 | namespace gr { 29 | namespace tpms { 30 | 31 | class burst_detector_impl : public burst_detector 32 | { 33 | private: 34 | unsigned int d_hysteresis_timeout; 35 | unsigned int d_hysteresis_count; 36 | bool d_burst; 37 | pmt::pmt_t d_tag_burst; 38 | 39 | size_t d_block_size; 40 | size_t d_advance; 41 | size_t d_readahead_items; 42 | 43 | float* d_fft_window; 44 | float* d_temp_f; 45 | 46 | gr_complex *d_fft_in; 47 | gr_complex *d_fft_out; 48 | fftwf_plan d_fft_plan; 49 | 50 | public: 51 | burst_detector_impl(); 52 | ~burst_detector_impl(); 53 | 54 | void forecast(int noutput_items, 55 | gr_vector_int &ninput_items_required); 56 | 57 | int general_work(int noutput_items, 58 | gr_vector_int &ninput_items, 59 | gr_vector_const_void_star &input_items, 60 | gr_vector_void_star &output_items); 61 | }; 62 | 63 | } // namespace tpms 64 | } // namespace gr 65 | 66 | #endif /* INCLUDED_TPMS_BURST_DETECTOR_IMPL_H */ 67 | 68 | -------------------------------------------------------------------------------- /lib/fixed_length_frame_sink_impl.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include 26 | #include "fixed_length_frame_sink_impl.h" 27 | 28 | #include 29 | 30 | namespace gr { 31 | namespace tpms { 32 | fixed_length_frame_sink::sptr 33 | fixed_length_frame_sink::make(int frame_length, pmt::pmt_t attributes) 34 | { 35 | return gnuradio::get_initial_sptr 36 | //(new fixed_length_frame_sink_impl(frame_length, target_queue)); 37 | (new fixed_length_frame_sink_impl(frame_length, attributes)); 38 | } 39 | 40 | /* 41 | * The private constructor 42 | */ 43 | fixed_length_frame_sink_impl::fixed_length_frame_sink_impl(int frame_length, pmt::pmt_t attributes) 44 | : gr::sync_block("fixed_length_frame_sink", 45 | gr::io_signature::make(1, 1, sizeof(unsigned char)), 46 | gr::io_signature::make(0, 0, 0)), 47 | d_frame_length(frame_length), 48 | d_message_port(pmt::mp("packet_source")), 49 | d_attributes(attributes), 50 | d_packets() 51 | { 52 | message_port_register_out(d_message_port); 53 | } 54 | 55 | /* 56 | * Our virtual destructor. 57 | */ 58 | fixed_length_frame_sink_impl::~fixed_length_frame_sink_impl() 59 | { 60 | } 61 | 62 | int 63 | fixed_length_frame_sink_impl::work(int noutput_items, 64 | gr_vector_const_void_star &input_items, 65 | gr_vector_void_star &output_items) 66 | { 67 | const unsigned char *in = (const unsigned char *)input_items[0]; 68 | int count = 0; 69 | 70 | while(count < noutput_items) { 71 | const bool start = (in[count] & 0x2) != 0; 72 | if( start ) { 73 | d_packets.push_back(bits_t()); 74 | } 75 | 76 | if( !d_packets.empty() ) { 77 | const uint8_t bit = in[count] & 0x1; 78 | for(packets_t::iterator it = d_packets.begin(); it != d_packets.end(); ++it) { 79 | (*it).push_back(bit); 80 | } 81 | 82 | if( d_packets.front().size() == d_frame_length ) { 83 | pmt::pmt_t data_vector = pmt::init_u8vector(d_packets.front().size(), &(d_packets.front().front())); 84 | pmt::pmt_t attributes = pmt::dict_add(d_attributes, pmt::mp("data"), data_vector); 85 | message_port_pub(d_message_port, attributes); 86 | 87 | d_packets.pop_front(); 88 | } 89 | } 90 | 91 | count++; 92 | } 93 | 94 | return noutput_items; 95 | } 96 | 97 | } /* namespace tpms */ 98 | } /* namespace gr */ 99 | 100 | -------------------------------------------------------------------------------- /lib/fixed_length_frame_sink_impl.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifndef INCLUDED_TPMS_FIXED_LENGTH_FRAME_SINK_IMPL_H 22 | #define INCLUDED_TPMS_FIXED_LENGTH_FRAME_SINK_IMPL_H 23 | 24 | #include 25 | 26 | namespace gr { 27 | namespace tpms { 28 | 29 | class fixed_length_frame_sink_impl : public fixed_length_frame_sink 30 | { 31 | private: 32 | enum state_t { STATE_SYNC_SEARCH, STATE_HAVE_SYNC }; 33 | 34 | static const int MAX_PKT_LEN = 4096; 35 | 36 | //msg_queue::sptr d_target_queue; 37 | pmt::pmt_t d_message_port; 38 | pmt::pmt_t d_attributes; 39 | state_t d_state; 40 | /* 41 | unsigned char d_packet[MAX_PKT_LEN]; 42 | unsigned char d_packet_byte; 43 | int d_packet_byte_index; 44 | int d_packetlen; 45 | int d_packetlen_cnt; 46 | */ 47 | typedef std::vector bits_t; 48 | typedef std::list packets_t; 49 | 50 | packets_t d_packets; 51 | 52 | int d_frame_length; 53 | 54 | protected: 55 | void enter_search(); 56 | void enter_have_sync(); 57 | 58 | public: 59 | //fixed_length_frame_sink_impl(int frame_length, msg_queue::sptr target_queue); 60 | fixed_length_frame_sink_impl(int frame_length, pmt::pmt_t attributes); 61 | ~fixed_length_frame_sink_impl(); 62 | 63 | // Where all the action really happens 64 | int work(int noutput_items, 65 | gr_vector_const_void_star &input_items, 66 | gr_vector_void_star &output_items); 67 | }; 68 | 69 | } // namespace tpms 70 | } // namespace gr 71 | 72 | #endif /* INCLUDED_TPMS_FIXED_LENGTH_FRAME_SINK_IMPL_H */ 73 | 74 | -------------------------------------------------------------------------------- /lib/qa_ask_env.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | 22 | #include 23 | #include 24 | #include "qa_ask_env.h" 25 | #include 26 | 27 | namespace gr { 28 | namespace tpms { 29 | 30 | void 31 | qa_ask_env::t1() 32 | { 33 | // Put test here 34 | } 35 | 36 | } /* namespace tpms */ 37 | } /* namespace gr */ 38 | 39 | -------------------------------------------------------------------------------- /lib/qa_ask_env.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | 22 | #ifndef _QA_ASK_ENV_H_ 23 | #define _QA_ASK_ENV_H_ 24 | 25 | #include 26 | #include 27 | 28 | namespace gr { 29 | namespace tpms { 30 | 31 | class qa_ask_env : public CppUnit::TestCase 32 | { 33 | public: 34 | CPPUNIT_TEST_SUITE(qa_ask_env); 35 | CPPUNIT_TEST(t1); 36 | CPPUNIT_TEST_SUITE_END(); 37 | 38 | private: 39 | void t1(); 40 | }; 41 | 42 | } /* namespace tpms */ 43 | } /* namespace gr */ 44 | 45 | #endif /* _QA_ASK_ENV_H_ */ 46 | 47 | -------------------------------------------------------------------------------- /lib/qa_fixed_length_frame_sink.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | 22 | #include 23 | #include 24 | #include "qa_fixed_length_frame_sink.h" 25 | #include 26 | 27 | namespace gr { 28 | namespace tpms { 29 | 30 | void 31 | qa_fixed_length_frame_sink::t1() 32 | { 33 | // Put test here 34 | } 35 | 36 | } /* namespace tpms */ 37 | } /* namespace gr */ 38 | 39 | -------------------------------------------------------------------------------- /lib/qa_fixed_length_frame_sink.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | 22 | #ifndef _QA_FIXED_LENGTH_FRAME_SINK_H_ 23 | #define _QA_FIXED_LENGTH_FRAME_SINK_H_ 24 | 25 | #include 26 | #include 27 | 28 | namespace gr { 29 | namespace tpms { 30 | 31 | class qa_fixed_length_frame_sink : public CppUnit::TestCase 32 | { 33 | public: 34 | CPPUNIT_TEST_SUITE(qa_fixed_length_frame_sink); 35 | CPPUNIT_TEST(t1); 36 | CPPUNIT_TEST_SUITE_END(); 37 | 38 | private: 39 | void t1(); 40 | }; 41 | 42 | } /* namespace tpms */ 43 | } /* namespace gr */ 44 | 45 | #endif /* _QA_FIXED_LENGTH_FRAME_SINK_H_ */ 46 | 47 | -------------------------------------------------------------------------------- /lib/qa_tpms.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | /* 22 | * This class gathers together all the test cases for the gr-filter 23 | * directory into a single test suite. As you create new test cases, 24 | * add them here. 25 | */ 26 | 27 | #include "qa_tpms.h" 28 | #include "qa_ask_env.h" 29 | #include "qa_fixed_length_frame_sink.h" 30 | 31 | CppUnit::TestSuite * 32 | qa_tpms::suite() 33 | { 34 | CppUnit::TestSuite *s = new CppUnit::TestSuite("tpms"); 35 | s->addTest(gr::tpms::qa_ask_env::suite()); 36 | s->addTest(gr::tpms::qa_fixed_length_frame_sink::suite()); 37 | 38 | return s; 39 | } 40 | -------------------------------------------------------------------------------- /lib/qa_tpms.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifndef _QA_TPMS_H_ 22 | #define _QA_TPMS_H_ 23 | 24 | #include 25 | #include 26 | 27 | //! collect all the tests for the gr-filter directory 28 | 29 | class __GR_ATTR_EXPORT qa_tpms 30 | { 31 | public: 32 | //! return suite of tests for all of gr-filter directory 33 | static CppUnit::TestSuite *suite(); 34 | }; 35 | 36 | #endif /* _QA_TPMS_H_ */ 37 | -------------------------------------------------------------------------------- /lib/test_tpms.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2014 Jared Boone . 4 | * 5 | * This is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3, or (at your option) 8 | * any later version. 9 | * 10 | * This software is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this software; see the file COPYING. If not, write to 17 | * the Free Software Foundation, Inc., 51 Franklin Street, 18 | * Boston, MA 02110-1301, USA. 19 | */ 20 | 21 | #ifdef HAVE_CONFIG_H 22 | #include "config.h" 23 | #endif 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | #include "qa_tpms.h" 30 | #include 31 | 32 | int 33 | main (int argc, char **argv) 34 | { 35 | CppUnit::TextTestRunner runner; 36 | std::ofstream xmlfile(get_unittest_path("tpms.xml").c_str()); 37 | CppUnit::XmlOutputter *xmlout = new CppUnit::XmlOutputter(&runner.result(), xmlfile); 38 | 39 | runner.addTest(qa_tpms::suite()); 40 | runner.setOutputter(xmlout); 41 | 42 | bool was_successful = runner.run("", false); 43 | 44 | return was_successful ? 0 : 1; 45 | } 46 | -------------------------------------------------------------------------------- /python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Jared Boone . 2 | # 3 | # This is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 3, or (at your option) 6 | # any later version. 7 | # 8 | # This software is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this software; see the file COPYING. If not, write to 15 | # the Free Software Foundation, Inc., 51 Franklin Street, 16 | # Boston, MA 02110-1301, USA. 17 | 18 | ######################################################################## 19 | # Include python install macros 20 | ######################################################################## 21 | include(GrPython) 22 | if(NOT PYTHONINTERP_FOUND) 23 | return() 24 | endif() 25 | 26 | ######################################################################## 27 | # Install python sources 28 | ######################################################################## 29 | GR_PYTHON_INSTALL( 30 | FILES 31 | __init__.py 32 | ask.py 33 | bit_coding.py 34 | decode.py 35 | fsk.py 36 | packet_check.py 37 | source.py 38 | DESTINATION ${GR_PYTHON_DIR}/tpms 39 | ) 40 | 41 | ######################################################################## 42 | # Handle the unit tests 43 | ######################################################################## 44 | include(GrTest) 45 | 46 | set(GR_TEST_TARGET_DEPS gnuradio-tpms) 47 | set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig) 48 | GR_ADD_TEST(qa_ask_env ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_ask_env.py) 49 | GR_ADD_TEST(qa_fixed_length_frame_sink ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_fixed_length_frame_sink.py) 50 | GR_ADD_TEST(qa_burst_detector ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_burst_detector.py) 51 | -------------------------------------------------------------------------------- /python/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2008,2009 Free Software Foundation, Inc. 3 | # 4 | # This application is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3, or (at your option) 7 | # any later version. 8 | # 9 | # This application is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | # 18 | 19 | # The presence of this file turns this directory into a Python package 20 | 21 | ''' 22 | GNU Radio TPMS module Python utility libraries. 23 | ''' 24 | 25 | # ---------------------------------------------------------------- 26 | # Temporary workaround for ticket:181 (swig+python problem) 27 | import sys 28 | _RTLD_GLOBAL = 0 29 | try: 30 | from dl import RTLD_GLOBAL as _RTLD_GLOBAL 31 | except ImportError: 32 | try: 33 | from DLFCN import RTLD_GLOBAL as _RTLD_GLOBAL 34 | except ImportError: 35 | pass 36 | 37 | if _RTLD_GLOBAL != 0: 38 | _dlopenflags = sys.getdlopenflags() 39 | sys.setdlopenflags(_dlopenflags|_RTLD_GLOBAL) 40 | # ---------------------------------------------------------------- 41 | 42 | 43 | # import swig generated symbols into the tpms namespace 44 | from tpms_swig import * 45 | 46 | # import any pure python here 47 | # 48 | 49 | # ---------------------------------------------------------------- 50 | # Tail of workaround 51 | if _RTLD_GLOBAL != 0: 52 | sys.setdlopenflags(_dlopenflags) # Restore original flags 53 | # ---------------------------------------------------------------- 54 | -------------------------------------------------------------------------------- /python/ask.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2014 Jared Boone 4 | # 5 | # This file is part of gr-tpms. 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; see the file COPYING. If not, write to 19 | # the Free Software Foundation, Inc., 51 Franklin Street, 20 | # Boston, MA 02110-1301, USA. 21 | # 22 | 23 | from gnuradio import gr 24 | from gnuradio import filter 25 | from gnuradio.filter import firdes 26 | 27 | import tpms 28 | 29 | class ask_channel_filter(gr.hier_block2): 30 | def __init__(self, sample_rate, decimation, symbol_rate): 31 | super(ask_channel_filter, self).__init__( 32 | "ask_channel_filter", 33 | gr.io_signature(1, 1, gr.sizeof_float*1), 34 | gr.io_signature(1, 1, gr.sizeof_float*1), 35 | ) 36 | 37 | output_sampling_rate = float(sample_rate) / decimation 38 | output_nyquist = output_sampling_rate / 2.0 39 | 40 | filter_attenuation_db = 40 41 | filter_cutoff = symbol_rate * 1.4 * 0.5 42 | filter_transition = symbol_rate * 1.4 * 0.2 43 | if (filter_cutoff + filter_transition) > output_nyquist: 44 | raise RuntimeError('ASK channel filter exceeds Nyquist frequency') 45 | 46 | filter_taps = firdes.low_pass_2(1.0, sample_rate, filter_cutoff, filter_transition, filter_attenuation_db) 47 | self.filter = filter.fir_filter_fff(decimation, (filter_taps)) 48 | self.connect((self, 0), (self.filter, 0)) 49 | 50 | self.envelope = tpms.ask_env(alpha=0.02) 51 | self.connect((self.filter, 0), (self.envelope, 0)) 52 | 53 | self.connect((self.envelope, 0), (self, 0)) 54 | -------------------------------------------------------------------------------- /python/bit_coding.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2013 Jared Boone 4 | # 5 | # This file is part of gr-tpms. 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; see the file COPYING. If not, write to 19 | # the Free Software Foundation, Inc., 51 Franklin Street, 20 | # Boston, MA 02110-1301, USA. 21 | # 22 | 23 | def string_to_symbols(s, symbol_length): 24 | return [s[n:n+symbol_length] for n in range(0, len(s), symbol_length)] 25 | 26 | def differential_manchester_decode(s): 27 | symbols = string_to_symbols(s, 2) 28 | last_bit = '0' 29 | result = [] 30 | for symbol in symbols: 31 | if len(symbol) == 2: 32 | if symbol[0] == symbol[1]: 33 | result.append('X') 34 | elif last_bit != symbol[0]: 35 | result.append('0') 36 | else: 37 | result.append('1') 38 | last_bit = symbol[1] 39 | else: 40 | result.append('X') 41 | return ''.join(result) 42 | 43 | def manchester_decode(s): 44 | symbols = string_to_symbols(s, 2) 45 | result = [] 46 | for symbol in symbols: 47 | if len(symbol) == 2: 48 | if symbol[0] == symbol[1]: 49 | result.append('X') 50 | else: 51 | result.append(symbol[1]) 52 | else: 53 | result.append('X') 54 | return ''.join(result) 55 | -------------------------------------------------------------------------------- /python/build_utils.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2004,2009,2012 Free Software Foundation, Inc. 3 | # 4 | # This file is part of GNU Radio 5 | # 6 | # GNU Radio is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3, or (at your option) 9 | # any later version. 10 | # 11 | # GNU Radio is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with GNU Radio; see the file COPYING. If not, write to 18 | # the Free Software Foundation, Inc., 51 Franklin Street, 19 | # Boston, MA 02110-1301, USA. 20 | # 21 | 22 | """Misc utilities used at build time 23 | """ 24 | 25 | import re, os, os.path 26 | from build_utils_codes import * 27 | 28 | 29 | # set srcdir to the directory that contains Makefile.am 30 | try: 31 | srcdir = os.environ['srcdir'] 32 | except KeyError, e: 33 | srcdir = "." 34 | srcdir = srcdir + '/' 35 | 36 | # set do_makefile to either true or false dependeing on the environment 37 | try: 38 | if os.environ['do_makefile'] == '0': 39 | do_makefile = False 40 | else: 41 | do_makefile = True 42 | except KeyError, e: 43 | do_makefile = False 44 | 45 | # set do_sources to either true or false dependeing on the environment 46 | try: 47 | if os.environ['do_sources'] == '0': 48 | do_sources = False 49 | else: 50 | do_sources = True 51 | except KeyError, e: 52 | do_sources = True 53 | 54 | name_dict = {} 55 | 56 | def log_output_name (name): 57 | (base, ext) = os.path.splitext (name) 58 | ext = ext[1:] # drop the leading '.' 59 | 60 | entry = name_dict.setdefault (ext, []) 61 | entry.append (name) 62 | 63 | def open_and_log_name (name, dir): 64 | global do_sources 65 | if do_sources: 66 | f = open (name, dir) 67 | else: 68 | f = None 69 | log_output_name (name) 70 | return f 71 | 72 | def expand_template (d, template_filename, extra = ""): 73 | '''Given a dictionary D and a TEMPLATE_FILENAME, expand template into output file 74 | ''' 75 | global do_sources 76 | output_extension = extract_extension (template_filename) 77 | template = open_src (template_filename, 'r') 78 | output_name = d['NAME'] + extra + '.' + output_extension 79 | log_output_name (output_name) 80 | if do_sources: 81 | output = open (output_name, 'w') 82 | do_substitution (d, template, output) 83 | output.close () 84 | template.close () 85 | 86 | def output_glue (dirname): 87 | output_makefile_fragment () 88 | output_ifile_include (dirname) 89 | 90 | def output_makefile_fragment (): 91 | global do_makefile 92 | if not do_makefile: 93 | return 94 | # overwrite the source, which must be writable; this should have been 95 | # checked for beforehand in the top-level Makefile.gen.gen . 96 | f = open (os.path.join (os.environ.get('gendir', os.environ.get('srcdir', '.')), 'Makefile.gen'), 'w') 97 | f.write ('#\n# This file is machine generated. All edits will be overwritten\n#\n') 98 | output_subfrag (f, 'h') 99 | output_subfrag (f, 'i') 100 | output_subfrag (f, 'cc') 101 | f.close () 102 | 103 | def output_ifile_include (dirname): 104 | global do_sources 105 | if do_sources: 106 | f = open ('%s_generated.i' % (dirname,), 'w') 107 | f.write ('//\n// This file is machine generated. All edits will be overwritten\n//\n') 108 | files = name_dict.setdefault ('i', []) 109 | files.sort () 110 | f.write ('%{\n') 111 | for file in files: 112 | f.write ('#include <%s>\n' % (file[0:-1] + 'h',)) 113 | f.write ('%}\n\n') 114 | for file in files: 115 | f.write ('%%include <%s>\n' % (file,)) 116 | 117 | def output_subfrag (f, ext): 118 | files = name_dict.setdefault (ext, []) 119 | files.sort () 120 | f.write ("GENERATED_%s =" % (ext.upper ())) 121 | for file in files: 122 | f.write (" \\\n\t%s" % (file,)) 123 | f.write ("\n\n") 124 | 125 | def extract_extension (template_name): 126 | # template name is something like: GrFIRfilterXXX.h.t 127 | # we return everything between the penultimate . and .t 128 | mo = re.search (r'\.([a-z]+)\.t$', template_name) 129 | if not mo: 130 | raise ValueError, "Incorrectly formed template_name '%s'" % (template_name,) 131 | return mo.group (1) 132 | 133 | def open_src (name, mode): 134 | global srcdir 135 | return open (os.path.join (srcdir, name), mode) 136 | 137 | def do_substitution (d, in_file, out_file): 138 | def repl (match_obj): 139 | key = match_obj.group (1) 140 | # print key 141 | return d[key] 142 | 143 | inp = in_file.read () 144 | out = re.sub (r"@([a-zA-Z0-9_]+)@", repl, inp) 145 | out_file.write (out) 146 | 147 | 148 | 149 | copyright = '''/* -*- c++ -*- */ 150 | /* 151 | * Copyright 2003,2004 Free Software Foundation, Inc. 152 | * 153 | * This file is part of GNU Radio 154 | * 155 | * GNU Radio is free software; you can redistribute it and/or modify 156 | * it under the terms of the GNU General Public License as published by 157 | * the Free Software Foundation; either version 3, or (at your option) 158 | * any later version. 159 | * 160 | * GNU Radio is distributed in the hope that it will be useful, 161 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 162 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 163 | * GNU General Public License for more details. 164 | * 165 | * You should have received a copy of the GNU General Public License 166 | * along with GNU Radio; see the file COPYING. If not, write to 167 | * the Free Software Foundation, Inc., 51 Franklin Street, 168 | * Boston, MA 02110-1301, USA. 169 | */ 170 | ''' 171 | 172 | def is_complex (code3): 173 | if i_code (code3) == 'c' or o_code (code3) == 'c': 174 | return '1' 175 | else: 176 | return '0' 177 | 178 | 179 | def standard_dict (name, code3, package='gr'): 180 | d = {} 181 | d['NAME'] = name 182 | d['NAME_IMPL'] = name+'_impl' 183 | d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper()) 184 | d['GUARD_NAME_IMPL'] = 'INCLUDED_%s_%s_IMPL_H' % (package.upper(), name.upper()) 185 | d['BASE_NAME'] = re.sub ('^' + package + '_', '', name) 186 | d['SPTR_NAME'] = '%s_sptr' % name 187 | d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten' 188 | d['COPYRIGHT'] = copyright 189 | d['TYPE'] = i_type (code3) 190 | d['I_TYPE'] = i_type (code3) 191 | d['O_TYPE'] = o_type (code3) 192 | d['TAP_TYPE'] = tap_type (code3) 193 | d['IS_COMPLEX'] = is_complex (code3) 194 | return d 195 | 196 | 197 | def standard_dict2 (name, code3, package): 198 | d = {} 199 | d['NAME'] = name 200 | d['BASE_NAME'] = name 201 | d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper()) 202 | d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten' 203 | d['COPYRIGHT'] = copyright 204 | d['TYPE'] = i_type (code3) 205 | d['I_TYPE'] = i_type (code3) 206 | d['O_TYPE'] = o_type (code3) 207 | d['TAP_TYPE'] = tap_type (code3) 208 | d['IS_COMPLEX'] = is_complex (code3) 209 | return d 210 | 211 | def standard_impl_dict2 (name, code3, package): 212 | d = {} 213 | d['NAME'] = name 214 | d['IMPL_NAME'] = name 215 | d['BASE_NAME'] = name.rstrip("impl").rstrip("_") 216 | d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper()) 217 | d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten' 218 | d['COPYRIGHT'] = copyright 219 | d['FIR_TYPE'] = "fir_filter_" + code3 220 | d['CFIR_TYPE'] = "fir_filter_" + code3[0:2] + 'c' 221 | d['TYPE'] = i_type (code3) 222 | d['I_TYPE'] = i_type (code3) 223 | d['O_TYPE'] = o_type (code3) 224 | d['TAP_TYPE'] = tap_type (code3) 225 | d['IS_COMPLEX'] = is_complex (code3) 226 | return d 227 | -------------------------------------------------------------------------------- /python/build_utils_codes.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2004 Free Software Foundation, Inc. 3 | # 4 | # This file is part of GNU Radio 5 | # 6 | # GNU Radio is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3, or (at your option) 9 | # any later version. 10 | # 11 | # GNU Radio is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with GNU Radio; see the file COPYING. If not, write to 18 | # the Free Software Foundation, Inc., 51 Franklin Street, 19 | # Boston, MA 02110-1301, USA. 20 | # 21 | 22 | def i_code (code3): 23 | return code3[0] 24 | 25 | def o_code (code3): 26 | if len (code3) >= 2: 27 | return code3[1] 28 | else: 29 | return code3[0] 30 | 31 | def tap_code (code3): 32 | if len (code3) >= 3: 33 | return code3[2] 34 | else: 35 | return code3[0] 36 | 37 | def i_type (code3): 38 | return char_to_type[i_code (code3)] 39 | 40 | def o_type (code3): 41 | return char_to_type[o_code (code3)] 42 | 43 | def tap_type (code3): 44 | return char_to_type[tap_code (code3)] 45 | 46 | 47 | char_to_type = {} 48 | char_to_type['s'] = 'short' 49 | char_to_type['i'] = 'int' 50 | char_to_type['f'] = 'float' 51 | char_to_type['c'] = 'gr_complex' 52 | char_to_type['b'] = 'unsigned char' 53 | -------------------------------------------------------------------------------- /python/decode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2014 Jared Boone 4 | # 5 | # This file is part of gr-tpms. 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; see the file COPYING. If not, write to 19 | # the Free Software Foundation, Inc., 51 Franklin Street, 20 | # Boston, MA 02110-1301, USA. 21 | # 22 | 23 | import numpy 24 | 25 | from gnuradio import digital 26 | from gnuradio import gr 27 | 28 | class clock_recovery(gr.hier_block2): 29 | def __init__(self, input_rate, symbol_rate): 30 | super(clock_recovery, self).__init__( 31 | "clock_recovery", 32 | gr.io_signature(1, 1, gr.sizeof_float*1), 33 | gr.io_signature(1, 1, gr.sizeof_char*1), 34 | ) 35 | 36 | samples_per_symbol = float(input_rate) / symbol_rate 37 | omega_relative_limit = 0.02 38 | gain_mu = 0.4 / samples_per_symbol 39 | 40 | self.clock_recovery = digital.clock_recovery_mm_ff(samples_per_symbol*(1+0.00), 0.25*gain_mu*gain_mu, 0.5, gain_mu, omega_relative_limit) 41 | self.slicer = digital.binary_slicer_fb() 42 | 43 | self.connect((self, 0), (self.clock_recovery, 0)) 44 | self.connect((self.clock_recovery, 0), (self.slicer, 0)) 45 | self.connect((self.slicer, 0), (self, 0)) 46 | 47 | class tag_print(gr.sync_block): 48 | def __init__(self, symbol_rate, bit_count): 49 | super(tag_print, self).__init__( 50 | "tag_print", 51 | [numpy.uint8], 52 | None 53 | ) 54 | 55 | self.symbol_rate = symbol_rate 56 | self.bit_count = bit_count 57 | 58 | self._packets = {} 59 | 60 | def work(self, input_items, output_items): 61 | nread = self.nitems_read(0) 62 | ninput_items = len(input_items[0]) 63 | tags = self.get_tags_in_range(0, nread, nread + ninput_items) 64 | 65 | for offset, packet in self._packets.items(): 66 | items_needed = self.bit_count - len(packet) 67 | if items_needed > 0: 68 | new_packet = numpy.concatenate((packet, input_items[0][:items_needed])) 69 | self._packets[offset] = new_packet 70 | if len(self._packets[offset]) >= self.bit_count: 71 | time_seconds = float(offset) / self.symbol_rate 72 | bits = ''.join(map(str, self._packets[offset])) 73 | print('%s %12.6f' % (bits, time_seconds)) 74 | del self._packets[offset] 75 | 76 | for tag in tags: 77 | local_start = tag.offset - nread 78 | self._packets[tag.offset] = input_items[0][local_start:local_start + self.bit_count] 79 | 80 | return ninput_items 81 | -------------------------------------------------------------------------------- /python/fsk.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2014 Jared Boone 4 | # 5 | # This file is part of gr-tpms. 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; see the file COPYING. If not, write to 19 | # the Free Software Foundation, Inc., 51 Franklin Street, 20 | # Boston, MA 02110-1301, USA. 21 | # 22 | 23 | from math import floor 24 | 25 | from gnuradio import analog 26 | from gnuradio import blocks 27 | from gnuradio import gr 28 | from gnuradio import filter 29 | from gnuradio.filter import firdes 30 | 31 | class fsk_center_tracking(gr.hier_block2): 32 | def __init__(self, sample_rate): 33 | super(fsk_center_tracking, self).__init__( 34 | "fsk_center_tracking", 35 | gr.io_signature(1, 1, gr.sizeof_gr_complex*1), 36 | gr.io_signature(1, 1, gr.sizeof_gr_complex*1), 37 | ) 38 | 39 | # Arbitrary averaging values that seem to work OK. 40 | window_symbols = 20 41 | symbol_rate = 19200 42 | average_window = int(floor(sample_rate * window_symbols / symbol_rate)) 43 | 44 | self.delay = blocks.delay(gr.sizeof_gr_complex*1, average_window) 45 | 46 | self.demod = analog.quadrature_demod_cf(1) 47 | self.moving_average = blocks.moving_average_ff(average_window, 1.0 / average_window, 4000) 48 | self.vco = blocks.vco_c(sample_rate, -sample_rate, 1) 49 | 50 | self.multiply = blocks.multiply_vcc(1) 51 | 52 | self.connect((self, 0), (self.delay, 0)) 53 | self.connect((self.delay, 0), (self.multiply, 0)) 54 | 55 | self.connect((self, 0), (self.demod, 0)) 56 | self.connect((self.demod, 0), (self.moving_average, 0)) 57 | self.connect((self.moving_average, 0), (self.vco, 0)) 58 | self.connect((self.vco, 0), (self.multiply, 1)) 59 | 60 | self.connect((self.multiply, 0), (self, 0)) 61 | 62 | class fsk_demodulator(gr.hier_block2): 63 | def __init__(self, sample_rate, offset, deviation, decimation, symbol_rate): 64 | super(fsk_demodulator, self).__init__( 65 | "fsk_demodulator", 66 | gr.io_signature(1, 1, gr.sizeof_gr_complex*1), 67 | gr.io_signature(1, 1, gr.sizeof_float*1), 68 | ) 69 | 70 | symbol_taps_length = int(floor(float(sample_rate) / symbol_rate)) 71 | symbol_taps = (1,) * symbol_taps_length 72 | 73 | self.symbol_filter_h = filter.freq_xlating_fir_filter_ccf(1, (symbol_taps), offset + deviation, sample_rate) 74 | self.symbol_filter_l = filter.freq_xlating_fir_filter_ccf(1, (symbol_taps), offset - deviation, sample_rate) 75 | 76 | self.mag_h = blocks.complex_to_mag(1) 77 | self.mag_l = blocks.complex_to_mag(1) 78 | self.sub = blocks.sub_ff(1) 79 | 80 | output_filter_cutoff = symbol_rate * 0.75 81 | output_filter_transition = symbol_rate * 0.25 82 | output_filter_attenuation = 40 83 | output_filter_taps = firdes.low_pass_2(1.0, sample_rate, output_filter_cutoff, output_filter_transition, output_filter_attenuation) 84 | self.output_filter = filter.fir_filter_fff(decimation, (output_filter_taps)) 85 | 86 | self.connect((self, 0), (self.symbol_filter_h, 0)) 87 | self.connect((self, 0), (self.symbol_filter_l, 0)) 88 | self.connect((self.symbol_filter_h, 0), (self.mag_h, 0)) 89 | self.connect((self.symbol_filter_l, 0), (self.mag_l, 0)) 90 | self.connect((self.mag_h, 0), (self.sub, 0)) 91 | self.connect((self.mag_l, 0), (self.sub, 1)) 92 | self.connect((self.sub, 0), (self.output_filter, 0)) 93 | self.connect((self.output_filter, 0), (self, 0)) 94 | -------------------------------------------------------------------------------- /python/packet_check.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2014 Jared Boone 4 | # 5 | # This file is part of gr-tpms. 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; see the file COPYING. If not, write to 19 | # the Free Software Foundation, Inc., 51 Franklin Street, 20 | # Boston, MA 02110-1301, USA. 21 | # 22 | 23 | import sys 24 | from datetime import datetime 25 | 26 | # from argparse import ArgumentParser 27 | 28 | import crcmod 29 | 30 | from bit_coding import * 31 | 32 | def crc8(polynomial, init): 33 | check_fn = crcmod.mkCrcFun(0x100 | polynomial, initCrc=init, rev=False) 34 | 35 | def crc8_fn(data, check): 36 | data_s = ''.join(map(chr, data)) 37 | calculated = check_fn(data_s) 38 | return calculated == check 39 | 40 | return crc8_fn 41 | 42 | def checksum8(extra): 43 | def checksum8_fn(data, check): 44 | calculated = (sum(data) - check + extra) & 0xff 45 | return calculated == 0 46 | 47 | return checksum8_fn 48 | 49 | def no_check(): 50 | def no_check_fn(data, check): 51 | return True 52 | 53 | return no_check_fn 54 | 55 | packet_types = [ 56 | { 57 | 'modulation': 'fsk', 58 | 'symbol_rate': 19220, 59 | 'access_code': '01010101010101010101010101010110', 60 | 'encoding': 'man', 61 | 'prefixes': ('00111111111100',), 62 | 'length': 80, 63 | 'range_data': (0, 72), 64 | 'range_check': (72, 80), 65 | 'check_fn': crc8(polynomial=0x1, init=0xc0), 66 | }, 67 | { 68 | 'modulation': 'fsk', 69 | 'symbol_rate': 19220, 70 | 'access_code': '01010101010101010101010101010110', 71 | 'encoding': 'man', 72 | 'prefixes': ('00111101',), 73 | 'length': 80, 74 | 'range_data': (0, 72), 75 | 'range_check': (72, 80), 76 | 'check_fn': crc8(polynomial=0x1, init=0xc2), 77 | }, 78 | { 79 | 'modulation': 'fsk', 80 | 'symbol_rate': 19220, 81 | 'access_code': '01010101010101010101010101010110', 82 | 'encoding': 'man', 83 | 'prefixes': ('00111101', '00101001111000'), 84 | 'length': 80, 85 | 'range_data': (0, 72), 86 | 'range_check': (72, 80), 87 | 'check_fn': crc8(polynomial=0x7, init=0xf3), 88 | }, 89 | { 90 | 'modulation': 'fsk', 91 | 'symbol_rate': 19220, 92 | 'access_code': '01010101010101010101010101010110', 93 | 'encoding': 'man', 94 | 'prefixes': ('00110', '0101111111', '1111001'), 95 | 'length': 72, 96 | 'range_data': (0, 64), 97 | 'range_check': (64, 72), 98 | 'check_fn': crc8(polynomial=0x1, init=0xff), 99 | }, 100 | { 101 | 'modulation': 'fsk', 102 | 'symbol_rate': 19220, 103 | 'access_code': '01010101010101010101010101010110', 104 | 'encoding': 'man', 105 | 'prefixes': ('110',), 106 | 'length': 64, 107 | 'range_data': (0, 56), 108 | 'range_check': (56, 64), 109 | 'check_fn': checksum8(extra=6), 110 | }, 111 | { 112 | 'modulation': 'fsk', 113 | 'symbol_rate': 19220, 114 | 'access_code': '01010101010101010101010101010110', 115 | 'encoding': 'man', 116 | 'prefixes': ('11111101',), 117 | 'length': 80, 118 | 'range_data': (0, 72), 119 | 'range_check': (72, 80), 120 | 'check_fn': crc8(polynomial=0x1, init=0x2), 121 | }, 122 | { 123 | 'modulation': 'fsk', 124 | 'symbol_rate': 19220, 125 | 'access_code': '01010101010101010101010101010110', 126 | 'encoding': 'man', 127 | 'prefixes': ('0000', '1111'), 128 | 'length': 68, 129 | 'range_data': (4, 60), 130 | 'range_check': (60, 68), 131 | 'check_fn': crc8(polynomial=0x7, init=0x0), 132 | }, 133 | ####################################################################### 134 | { 135 | 'modulation': 'fsk', 136 | 'symbol_rate': 19250, 137 | 'access_code': '00110011001100110011001100110011010', 138 | 'encoding': 'man', 139 | 'prefixes': (''), 140 | 'length': 64, 141 | }, 142 | ####################################################################### 143 | { 144 | 'modulation': 'fsk', 145 | 'symbol_rate': 19440, 146 | 'access_code': '01010101010101010101010101010110', 147 | 'encoding': 'man', 148 | 'prefixes': (''), 149 | 'length': 64, 150 | }, 151 | ####################################################################### 152 | { 153 | 'modulation': 'fsk', 154 | 'symbol_rate': 9910, 155 | 'access_code': '00111111001', 156 | 'encoding': 'diffman', 157 | 'prefixes': ('',), 158 | 'length': 64, # Some fraction of packets are 68 bits long? 159 | }, 160 | ####################################################################### 161 | { 162 | 'modulation': 'fsk', 163 | 'symbol_rate': 19224, 164 | 'access_code': '1010101010101010101010101010100110', 165 | 'encoding': 'man', 166 | 'prefixes': ('',), 167 | 'length': 80, 168 | }, 169 | ####################################################################### 170 | { 171 | 'modulation': 'fsk', 172 | 'symbol_rate': 19950, 173 | 'access_code': '110110101110001', 174 | 'encoding': 'man', 175 | 'prefixes': ('',), 176 | 'length': 69, 177 | }, 178 | { 179 | 'modulation': 'fsk', 180 | 'symbol_rate': 20480, 181 | 'access_code': '110110101110001', 182 | 'encoding': 'man', 183 | 'prefixes': ('',), 184 | 'length': 69, 185 | }, 186 | { 187 | 'modulation': 'fsk', 188 | 'symbol_rate': 20040, 189 | 'access_code': '010101010011110', 190 | 'encoding': 'man', 191 | 'prefixes': ('',), 192 | 'length': 72, 193 | }, 194 | { 195 | 'modulation': 'ask', 196 | 'symbol_rate': 8157, 197 | 'access_code': '111101010101010101010101010101011110', 198 | 'encoding': 'man', 199 | 'prefixes': ('',), 200 | 'length': 37, 201 | }, 202 | { 203 | 'modulation': 'ask', 204 | 'symbol_rate': 8400, 205 | 'access_code': '1010101010101010101010101010110', 206 | 'encoding': 'man', 207 | 'prefixes': ('110010',), 208 | 'length': 78, 209 | 'range_data': (6, 70), 210 | 'range_check': (70, 78), 211 | 'check_fn': checksum8(extra=186), 212 | }, 213 | # { 214 | # MAY BE TOO SLOW FOR BURST CAPTURE TO CATCH COMPLETE TRANSMISSIONS. 215 | # 'modulation': 'ask', 216 | # 'symbol_rate': 4000-ish, 217 | # 'access_code': '10101010101010101010101010101010101010101010101010011001010110101010'?, 218 | # }, 219 | # { 220 | # 'modulation': 'ask', 221 | # 'symbol_rate': 4096, 222 | # 'access_code': '10011001100110011001', # Wake up code, followed by separate packet transmission 223 | # 'access_code_2': '10011001100110010101010101010110', 224 | # }, 225 | # { 226 | # 'modulation': 'ask', 227 | # 'symbol_rate': 2200?, 228 | # 'access_code': '1011011011001001001001001001001001001', # Or quite possibly the whole message? 229 | # }, 230 | # { 231 | # 'modulation': 'ask', 232 | # 'symbol_rate': 4667, 233 | # 'access_code': '10101010101010101010101010101001', 234 | # 'encoding': 'man', 235 | # 'prefixes': ('110000011010101110011010101100111011111011111011',), 236 | # 'length': 96, 237 | # 'range_data': ?, 238 | # 'range_check': ?, 239 | # 'check_fn': None, 240 | # }, 241 | # { 242 | # 'modulation': 'ask', 243 | # 'symbol_rate': 10000, 244 | # 'access_code': '11001111101010101001', 245 | # 'encoding': 'diffman', 246 | # 'prefixes': ('0000', '0101'), 247 | # 'length': 68, 248 | # 'range_data': (4, 60), 249 | # 'range_check': (60, 68), 250 | # 'check_fn': crc8(polynomial=0x07, init=0x00), 251 | # }, 252 | { 253 | 'modulation': 'ask', 254 | 'symbol_rate': 10000, 255 | 'access_code': '11001111101010101001', 256 | 'encoding': 'diffman', 257 | 'prefixes': None, 258 | 'length': 64, 259 | 'range_data': (0, 56), 260 | 'range_check': (56, 64), 261 | 'check_fn': crc8(polynomial=0x07, init=0x87), 262 | }, 263 | ] 264 | 265 | decoders = { 266 | 'man': manchester_decode, 267 | 'diffman': differential_manchester_decode, 268 | } 269 | 270 | def prefix_matches(decoded, prefixes): 271 | if prefixes: 272 | for prefix in prefixes: 273 | if decoded.startswith(prefix): 274 | return True 275 | else: 276 | return True 277 | 278 | def bit_range_to_bytes(bits): 279 | c = [] 280 | for n in range(0, len(bits), 8): 281 | v = int(bits[n:n+8], 2) 282 | c.append(v) 283 | return c 284 | 285 | def format_time(t): 286 | return t.strftime('%Y%m%d%H%M%S') 287 | 288 | def packet_decode(packet, output_raw=False): 289 | decoded_results = [] 290 | 291 | utcnow = datetime.utcnow() 292 | 293 | raw_matches = set() 294 | 295 | for packet_type in packet_types: 296 | if (packet['modulation'] == packet_type['modulation']) and \ 297 | (packet['symbol_rate'] == packet_type['symbol_rate']) and \ 298 | (packet['access_code'] == packet_type['access_code']): 299 | decode_result = None 300 | decoder_fn = decoders[packet_type['encoding']] 301 | decoded = decoder_fn(packet['data']).split('X')[0] 302 | if len(decoded) >= 32: 303 | raw_matches.add(( 304 | packet['modulation'], 305 | packet['symbol_rate'], 306 | packet['access_code'], 307 | packet['data'] 308 | )) 309 | 310 | if len(decoded) >= packet_type['length']: 311 | if prefix_matches(decoded, packet_type['prefixes']): 312 | if 'check_fn' in packet_type: 313 | payload_bits = decoded.__getslice__(*packet_type['range_data']) 314 | payload_bytes = bit_range_to_bytes(payload_bits) 315 | check_bits = decoded.__getslice__(*packet_type['range_check']) 316 | check_bytes = int(check_bits, 2) 317 | check_fn = packet_type['check_fn'] 318 | check_ok = check_fn(payload_bytes, check_bytes) 319 | if check_ok: 320 | decode_result = 'success' 321 | else: 322 | decode_result = 'check failed' 323 | else: 324 | check_ok = True 325 | decode_result = 'success' 326 | 327 | if check_ok: 328 | decoded_result = packet_type.copy() 329 | decoded_result['payload'] = decoded 330 | decoded_results.append(decoded_result) 331 | else: 332 | decode_result = 'no prefix matches' 333 | else: 334 | decode_result = 'decoded length %d < %d' % (len(decoded), packet_type['length']) 335 | 336 | if output_raw: 337 | for modulation, symbol_rate, access_code, data in raw_matches: 338 | print('%s %s %s %s %s %s' % ( 339 | format_time(utcnow), 340 | modulation, 341 | symbol_rate, 342 | access_code, 343 | 'raw', 344 | ''.join(map(str, data)), 345 | )) 346 | 347 | for decoded_result in decoded_results: 348 | print('%s %s %s %s %s %s' % ( 349 | format_time(utcnow), 350 | decoded_result['modulation'], 351 | decoded_result['symbol_rate'], 352 | decoded_result['access_code'], 353 | decoded_result['encoding'], 354 | decoded_result['payload'], 355 | )) 356 | 357 | sys.stdout.flush() 358 | 359 | return len(decoded_results) > 0 360 | -------------------------------------------------------------------------------- /python/qa_ask_env.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright 2014 Jared Boone . 5 | # 6 | # This is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3, or (at your option) 9 | # any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software; see the file COPYING. If not, write to 18 | # the Free Software Foundation, Inc., 51 Franklin Street, 19 | # Boston, MA 02110-1301, USA. 20 | 21 | from gnuradio import gr, gr_unittest 22 | from gnuradio import blocks 23 | import tpms_swig as tpms 24 | 25 | class qa_ask_env (gr_unittest.TestCase): 26 | 27 | def setUp (self): 28 | self.tb = gr.top_block () 29 | 30 | def tearDown (self): 31 | self.tb = None 32 | 33 | def test_001_t (self): 34 | src_data = (0, 1, 0.5, 0.25, -1, 1) 35 | expected_result = (0, 1, 0, -0.5, -1, 1) 36 | src = blocks.vector_source_f(src_data) 37 | dut = tpms.ask_env(0.0) 38 | dut.set_alpha(0.001) 39 | dst = blocks.vector_sink_f() 40 | self.tb.connect(src, dut) 41 | self.tb.connect(dut, dst) 42 | self.tb.run () 43 | result_data = dst.data() 44 | self.assertFloatTuplesAlmostEqual(expected_result, result_data, 2) 45 | 46 | if __name__ == '__main__': 47 | gr_unittest.run(qa_ask_env, "qa_ask_env.xml") 48 | -------------------------------------------------------------------------------- /python/qa_burst_detector.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright 2014 Jared Boone . 5 | # 6 | # This is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3, or (at your option) 9 | # any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software; see the file COPYING. If not, write to 18 | # the Free Software Foundation, Inc., 51 Franklin Street, 19 | # Boston, MA 02110-1301, USA. 20 | 21 | from gnuradio import gr, gr_unittest 22 | from gnuradio import blocks 23 | import tpms_swig as tpms 24 | 25 | class qa_burst_detector (gr_unittest.TestCase): 26 | 27 | def setUp (self): 28 | self.tb = gr.top_block () 29 | 30 | def tearDown (self): 31 | self.tb = None 32 | 33 | def test_001_t (self): 34 | # set up fg 35 | self.tb.run () 36 | # check data 37 | 38 | 39 | if __name__ == '__main__': 40 | gr_unittest.run(qa_burst_detector, "qa_burst_detector.xml") 41 | -------------------------------------------------------------------------------- /python/qa_fixed_length_frame_sink.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # 4 | # Copyright 2014 Jared Boone . 5 | # 6 | # This is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 3, or (at your option) 9 | # any later version. 10 | # 11 | # This software is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with this software; see the file COPYING. If not, write to 18 | # the Free Software Foundation, Inc., 51 Franklin Street, 19 | # Boston, MA 02110-1301, USA. 20 | 21 | from gnuradio import gr, gr_unittest 22 | from gnuradio import blocks 23 | import tpms_swig as tpms 24 | 25 | class qa_fixed_length_frame_sink (gr_unittest.TestCase): 26 | 27 | def setUp (self): 28 | self.tb = gr.top_block () 29 | 30 | def tearDown (self): 31 | self.tb = None 32 | 33 | def test_001_t (self): 34 | # set up fg 35 | self.tb.run () 36 | # check data 37 | 38 | 39 | if __name__ == '__main__': 40 | gr_unittest.run(qa_fixed_length_frame_sink, "qa_fixed_length_frame_sink.xml") 41 | -------------------------------------------------------------------------------- /python/source.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2014 Jared Boone 4 | # 5 | # This file is part of gr-tpms. 6 | # 7 | # This program is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2, or (at your option) 10 | # any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with this program; see the file COPYING. If not, write to 19 | # the Free Software Foundation, Inc., 51 Franklin Street, 20 | # Boston, MA 02110-1301, USA. 21 | # 22 | 23 | from gnuradio import blocks 24 | from gnuradio import gr 25 | from gnuradio import filter 26 | from gnuradio.filter import firdes 27 | 28 | class source_hackrf(gr.hier_block2): 29 | def __init__(self, target_frequency, if_sampling_rate): 30 | super(source_hackrf, self).__init__( 31 | "source_hackrf", 32 | gr.io_signature(0, 0, 0), 33 | gr.io_signature(1, 1, gr.sizeof_gr_complex*1), 34 | ) 35 | 36 | import osmosdr 37 | 38 | rf_sampling_rate = 10000000 39 | baseband_bandwidth = 1750000 40 | offset_frequency = 400000 # Keep well below roll-off of baseband filter. 41 | rf_gain = 14 # Gains set assuming a front-end filter keeps out-of-band noise down. 42 | if_gain = 40 43 | bb_gain = 24 44 | if_filter_attenuation = 60 45 | 46 | rf_decimation, rf_decimation_remainder = divmod(rf_sampling_rate, if_sampling_rate) 47 | if rf_decimation_remainder != 0: 48 | raise RuntimeError('RF decimation must be an integer') 49 | rf_decimation = int(round(rf_decimation)) 50 | tuning_frequency = target_frequency - offset_frequency 51 | 52 | self.source = osmosdr.source(args="numchan=1 hackrf=0") 53 | self.source.set_sample_rate(rf_sampling_rate) 54 | self.source.set_center_freq(tuning_frequency, 0) 55 | self.source.set_freq_corr(0, 0) 56 | self.source.set_dc_offset_mode(0, 0) 57 | self.source.set_iq_balance_mode(0, 0) 58 | self.source.set_gain_mode(False, 0) 59 | self.source.set_gain(rf_gain, 0) 60 | self.source.set_if_gain(if_gain, 0) 61 | self.source.set_bb_gain(bb_gain, 0) 62 | self.source.set_antenna("", 0) 63 | self.source.set_bandwidth(baseband_bandwidth, 0) 64 | 65 | if_taps = firdes.low_pass_2(1.0, rf_sampling_rate, if_sampling_rate*0.45, if_sampling_rate*0.1, if_filter_attenuation) 66 | self.if_filter = filter.freq_xlating_fir_filter_ccc(rf_decimation, (if_taps), offset_frequency, rf_sampling_rate) 67 | #self.if_filter.set_min_output_buffer(1048576) 68 | 69 | self.connect(self.source, self.if_filter, self) 70 | 71 | class source_rtlsdr(gr.hier_block2): 72 | def __init__(self, target_frequency, if_sampling_rate): 73 | super(source_rtlsdr, self).__init__( 74 | "source_rtlsdr", 75 | gr.io_signature(0, 0, 0), 76 | gr.io_signature(1, 1, gr.sizeof_gr_complex*1), 77 | ) 78 | 79 | import osmosdr 80 | 81 | model = 'r820t' 82 | 83 | rf_sampling_rate = 2400000 84 | if model == 'e4000': 85 | offset_frequency = rf_sampling_rate / 4 86 | rf_gain = 42 # E4000, Gains set assuming a front-end filter keeps out-of-band noise down. 87 | elif model == 'r820t': 88 | offset_frequency = 0 89 | rf_gain = 42.1 # R820T, Gains set assuming a front-end filter keeps out-of-band noise down. 90 | else: 91 | raise RuntimeError('Unknown RTL-SDR receiver model') 92 | 93 | if_gain = 42 94 | bb_gain = 20 # Not sure this has an effect on RTL-SDR. 95 | baseband_bandwidth = 0 # No effect on RTL-SDR? 96 | if_filter_attenuation = 60 97 | 98 | rf_decimation, rf_decimation_remainder = divmod(rf_sampling_rate, if_sampling_rate) 99 | if rf_decimation_remainder != 0: 100 | raise RuntimeError('RF decimation must be an integer') 101 | rf_decimation = int(round(rf_decimation)) 102 | tuning_frequency = target_frequency - offset_frequency 103 | 104 | self.source = osmosdr.source(args="numchan=1 rtl=0") 105 | self.source.set_sample_rate(rf_sampling_rate) 106 | self.source.set_center_freq(tuning_frequency, 0) 107 | self.source.set_freq_corr(0, 0) 108 | self.source.set_dc_offset_mode(0, 0) 109 | self.source.set_iq_balance_mode(0, 0) 110 | self.source.set_gain_mode(False, 0) 111 | self.source.set_gain(rf_gain, 0) 112 | self.source.set_if_gain(if_gain, 0) 113 | self.source.set_bb_gain(bb_gain, 0) 114 | self.source.set_antenna("", 0) 115 | self.source.set_bandwidth(baseband_bandwidth, 0) 116 | 117 | if_taps = firdes.low_pass_2(1.0, rf_sampling_rate, if_sampling_rate*0.45, if_sampling_rate*0.1, if_filter_attenuation) 118 | self.if_filter = filter.freq_xlating_fir_filter_ccc(rf_decimation, (if_taps), offset_frequency, rf_sampling_rate) 119 | #self.if_filter.set_min_output_buffer(1048576) 120 | 121 | self.connect(self.source, self.if_filter, self) 122 | 123 | class source_file(gr.hier_block2): 124 | def __init__(self, file_path, throttle_rate=None): 125 | super(source_file, self).__init__( 126 | "source_file", 127 | gr.io_signature(0, 0, 0), 128 | gr.io_signature(1, 1, gr.sizeof_gr_complex*1), 129 | ) 130 | 131 | self.source = blocks.file_source(gr.sizeof_gr_complex*1, file_path, False) 132 | 133 | if throttle_rate: 134 | self.throttle = blocks.throttle(gr.sizeof_gr_complex*1, throttle_rate, True) 135 | self.connect(self.source, self.throttle, self) 136 | else: 137 | self.connect(self.source, self) 138 | 139 | -------------------------------------------------------------------------------- /swig/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Jared Boone . 2 | # 3 | # This is free software; you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation; either version 3, or (at your option) 6 | # any later version. 7 | # 8 | # This software is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this software; see the file COPYING. If not, write to 15 | # the Free Software Foundation, Inc., 51 Franklin Street, 16 | # Boston, MA 02110-1301, USA. 17 | 18 | ######################################################################## 19 | # Include swig generation macros 20 | ######################################################################## 21 | find_package(SWIG) 22 | find_package(PythonLibs 2) 23 | if(NOT SWIG_FOUND OR NOT PYTHONLIBS_FOUND) 24 | return() 25 | endif() 26 | include(GrSwig) 27 | include(GrPython) 28 | 29 | ######################################################################## 30 | # Setup swig generation 31 | ######################################################################## 32 | foreach(incdir ${GNURADIO_RUNTIME_INCLUDE_DIRS}) 33 | list(APPEND GR_SWIG_INCLUDE_DIRS ${incdir}/gnuradio/swig) 34 | endforeach(incdir) 35 | 36 | set(GR_SWIG_LIBRARIES gnuradio-tpms) 37 | set(GR_SWIG_DOC_FILE ${CMAKE_CURRENT_BINARY_DIR}/tpms_swig_doc.i) 38 | set(GR_SWIG_DOC_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../include) 39 | 40 | GR_SWIG_MAKE(tpms_swig tpms_swig.i) 41 | 42 | ######################################################################## 43 | # Install the build swig module 44 | ######################################################################## 45 | GR_SWIG_INSTALL(TARGETS tpms_swig DESTINATION ${GR_PYTHON_DIR}/tpms) 46 | 47 | ######################################################################## 48 | # Install swig .i files for development 49 | ######################################################################## 50 | install( 51 | FILES 52 | tpms_swig.i 53 | ${CMAKE_CURRENT_BINARY_DIR}/tpms_swig_doc.i 54 | DESTINATION ${GR_INCLUDE_DIR}/tpms/swig 55 | ) 56 | -------------------------------------------------------------------------------- /swig/tpms_swig.i: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | 3 | #define TPMS_API 4 | 5 | %include "gnuradio.i" // the common stuff 6 | 7 | //load generated python docstrings 8 | %include "tpms_swig_doc.i" 9 | 10 | %{ 11 | #include "tpms/ask_env.h" 12 | #include "tpms/fixed_length_frame_sink.h" 13 | #include "tpms/burst_detector.h" 14 | %} 15 | 16 | 17 | %include "tpms/ask_env.h" 18 | GR_SWIG_BLOCK_MAGIC2(tpms, ask_env); 19 | %include "tpms/fixed_length_frame_sink.h" 20 | GR_SWIG_BLOCK_MAGIC2(tpms, fixed_length_frame_sink); 21 | %include "tpms/burst_detector.h" 22 | GR_SWIG_BLOCK_MAGIC2(tpms, burst_detector); 23 | --------------------------------------------------------------------------------