├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── MANIFEST.md ├── README.md ├── apps └── CMakeLists.txt ├── cmake ├── Modules │ ├── CMakeParseArgumentsCopy.cmake │ ├── FindCppUnit.cmake │ ├── FindGnuradioRuntime.cmake │ ├── GrMiscUtils.cmake │ ├── GrPlatform.cmake │ ├── GrPython.cmake │ ├── GrSwig.cmake │ ├── GrTest.cmake │ ├── UseSWIG.cmake │ └── nordicConfig.cmake └── cmake_uninstall.cmake.in ├── docs ├── CMakeLists.txt ├── README.nordic └── 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 ├── microsoft_mouse_sniffer.py ├── nordic_auto_ack.py ├── nordic_channelized_receiver.py ├── nordic_channelized_transmitter.py ├── nordic_receiver.py └── nordic_sniffer_scanner.py ├── grc ├── CMakeLists.txt ├── nordic_nordic_rx.xml └── nordic_nordic_tx.xml ├── include └── nordic │ ├── CMakeLists.txt │ ├── api.h │ ├── nordic_rx.h │ └── nordic_tx.h ├── lib ├── CMakeLists.txt ├── bit_shifting_byte_vector.cc ├── bit_shifting_byte_vector.h ├── enhanced_shockburst_packet.cc ├── enhanced_shockburst_packet.h ├── nordic_rx_impl.cc ├── nordic_rx_impl.h ├── nordic_tx_impl.cc ├── nordic_tx_impl.h ├── nordictap.h ├── qa_nordic.cc ├── qa_nordic.h └── test_nordic.cc ├── python ├── CMakeLists.txt ├── __init__.py ├── build_utils.py └── build_utils_codes.py ├── swig ├── CMakeLists.txt └── nordic_swig.i └── wireshark └── nordic_dissector.lua /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | *.pyc 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | 17 | ######################################################################## 18 | # Project setup 19 | ######################################################################## 20 | cmake_minimum_required(VERSION 2.6) 21 | project(gr-nordic CXX C) 22 | enable_testing() 23 | 24 | #select the release build type by default to get optimization flags 25 | if(NOT CMAKE_BUILD_TYPE) 26 | set(CMAKE_BUILD_TYPE "Release") 27 | message(STATUS "Build type not specified: defaulting to release.") 28 | endif(NOT CMAKE_BUILD_TYPE) 29 | set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") 30 | 31 | #make sure our local CMake Modules path comes first 32 | list(INSERT CMAKE_MODULE_PATH 0 ${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 nordic") 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/nordic) 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 | # On Apple only, set install name and use rpath correctly, if not already set 82 | ######################################################################## 83 | if(APPLE) 84 | if(NOT CMAKE_INSTALL_NAME_DIR) 85 | set(CMAKE_INSTALL_NAME_DIR 86 | ${CMAKE_INSTALL_PREFIX}/${GR_LIBRARY_DIR} CACHE 87 | PATH "Library Install Name Destination Directory" FORCE) 88 | endif(NOT CMAKE_INSTALL_NAME_DIR) 89 | if(NOT CMAKE_INSTALL_RPATH) 90 | set(CMAKE_INSTALL_RPATH 91 | ${CMAKE_INSTALL_PREFIX}/${GR_LIBRARY_DIR} CACHE 92 | PATH "Library Install RPath" FORCE) 93 | endif(NOT CMAKE_INSTALL_RPATH) 94 | if(NOT CMAKE_BUILD_WITH_INSTALL_RPATH) 95 | set(CMAKE_BUILD_WITH_INSTALL_RPATH ON CACHE 96 | BOOL "Do Build Using Library Install RPath" FORCE) 97 | endif(NOT CMAKE_BUILD_WITH_INSTALL_RPATH) 98 | endif(APPLE) 99 | 100 | ######################################################################## 101 | # Find gnuradio build dependencies 102 | ######################################################################## 103 | find_package(CppUnit) 104 | find_package(Doxygen) 105 | 106 | # Search for GNU Radio and its components and versions. Add any 107 | # components required to the list of GR_REQUIRED_COMPONENTS (in all 108 | # caps such as FILTER or FFT) and change the version to the minimum 109 | # API compatible version required. 110 | set(GR_REQUIRED_COMPONENTS RUNTIME) 111 | find_package(Gnuradio "3.7.2" REQUIRED) 112 | 113 | if(NOT CPPUNIT_FOUND) 114 | message(FATAL_ERROR "CppUnit required to compile nordic") 115 | endif() 116 | 117 | ######################################################################## 118 | # Setup doxygen option 119 | ######################################################################## 120 | if(DOXYGEN_FOUND) 121 | option(ENABLE_DOXYGEN "Build docs using Doxygen" ON) 122 | else(DOXYGEN_FOUND) 123 | option(ENABLE_DOXYGEN "Build docs using Doxygen" OFF) 124 | endif(DOXYGEN_FOUND) 125 | 126 | ######################################################################## 127 | # Setup the include and linker paths 128 | ######################################################################## 129 | include_directories( 130 | ${CMAKE_SOURCE_DIR}/lib 131 | ${CMAKE_SOURCE_DIR}/include 132 | ${CMAKE_BINARY_DIR}/lib 133 | ${CMAKE_BINARY_DIR}/include 134 | ${Boost_INCLUDE_DIRS} 135 | ${CPPUNIT_INCLUDE_DIRS} 136 | ${GNURADIO_ALL_INCLUDE_DIRS} 137 | ) 138 | 139 | link_directories( 140 | ${Boost_LIBRARY_DIRS} 141 | ${CPPUNIT_LIBRARY_DIRS} 142 | ${GNURADIO_RUNTIME_LIBRARY_DIRS} 143 | ) 144 | 145 | # Set component parameters 146 | set(GR_NORDIC_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE INTERNAL "" FORCE) 147 | set(GR_NORDIC_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/swig CACHE INTERNAL "" FORCE) 148 | 149 | ######################################################################## 150 | # Create uninstall target 151 | ######################################################################## 152 | configure_file( 153 | ${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in 154 | ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake 155 | @ONLY) 156 | 157 | add_custom_target(uninstall 158 | ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake 159 | ) 160 | 161 | ######################################################################## 162 | # Add subdirectories 163 | ######################################################################## 164 | add_subdirectory(include/nordic) 165 | add_subdirectory(lib) 166 | add_subdirectory(swig) 167 | add_subdirectory(python) 168 | add_subdirectory(grc) 169 | add_subdirectory(apps) 170 | add_subdirectory(docs) 171 | 172 | ######################################################################## 173 | # Install cmake search helper for this library 174 | ######################################################################## 175 | if(NOT CMAKE_MODULES_DIR) 176 | set(CMAKE_MODULES_DIR lib${LIB_SUFFIX}/cmake) 177 | endif(NOT CMAKE_MODULES_DIR) 178 | 179 | install(FILES cmake/Modules/nordicConfig.cmake 180 | DESTINATION ${CMAKE_MODULES_DIR}/nordic 181 | ) 182 | -------------------------------------------------------------------------------- /MANIFEST.md: -------------------------------------------------------------------------------- 1 | title: The NORDIC OOT Module 2 | brief: Short description of gr-nordic 3 | tags: # Tags are arbitrary, but look at CGRAN what other authors are using 4 | - sdr 5 | author: 6 | - Author Name 7 | copyright_owner: 8 | - Copyright Owner 1 9 | license: 10 | #repo: # Put the URL of the repository here, or leave blank for default 11 | #website: # If you have a separate project website, put it here 12 | #icon: # Put a URL to a square image here that will be used as an icon on CGRAN 13 | --- 14 | A longer, multi-line description of gr-nordic. 15 | You may use some *basic* Markdown here. 16 | If left empty, it will try to find a README file instead. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gr-nordic 2 | 3 | GNU Radio module and Wireshark dissector for the Nordic Semiconductor nRF24L Enhanced Shockburst protocol. 4 | 5 | ## external c++ classes 6 | 7 | ### nordic_rx 8 | 9 | Receiver class which consumes a GFSK demodulated bitstream and reconstructs Enhanced Shockburst packets. PDUs are printed standard out and sent to Wireshark. 10 | 11 | ### nordic_tx 12 | 13 | Transmitter class which consumes nordictap structs, generates Enhanced Shockburst packets, and produces a byte stream to be fed to a GFSK modulator. 14 | 15 | ## python examples 16 | 17 | All python examples use the osmosdr_source/osmosdr_sink blocks, and are SDR agnostic. 18 | 19 | ### nordic_receiver.py 20 | 21 | Single channel receiver. Listening on channel 4 (2404MHz) with a 2Mbps data rate, 5 byte address, and 2 byte CRC is invoked as follows: 22 | 23 | ```./nordic_receiver.py --channel 4 --data_rate 2e6 --crc_length 2 --address_length 5 --samples_per_symbol 2 --gain 40``` 24 | 25 | ### nordic_auto_ack.py 26 | 27 | Single channel receiver with auto-ACK. Listening (and ACKing) on channel 4 (2404MHz) with a 2Mbps data rate, 5 byte address, and 2 byte CRC is invoked as follows: 28 | 29 | ```./nordic_auto_ack.py --channel 4 --data_rate 2e6 --crc_length 2 --address_length 5 --samples_per_symbol 2 --gain 40``` 30 | 31 | ### nordic_sniffer_scanner.py 32 | 33 | Sweeping single channel receiver, which sweeps between channels 2-83 looking for Enhanced Shockburst packets. During receive activity, it camps on a given channel until idle. 34 | 35 | ```./nordic_sniffer_scanner.py ``` 36 | 37 | ### microsoft_mouse_sniffer.py 38 | 39 | Microsoft mouse/keyboard following receiver. When launched, this script will sweep between the 24 possible Microsoft wireless keyboard/mouse channels. When a device is found, it switches to that device's 4-channel group, sweeping between that set to follow the device. 40 | 41 | ```./microsoft_mouse_sniffer.py ``` 42 | 43 | ### nordic_channelized_receiver.py 44 | 45 | Channelized receiver example, which tunes to 2414MHz, and receives 2Mbps Enhanced Shockburst packets on channels 10, 14, and 18. 46 | 47 | ```./nordic_channelized_receiver.py ``` 48 | 49 | ### nordic_channelized_transmitter.py 50 | 51 | Channelized transmitter example, which tunes to 2414MHz, and transmits 2Mbps Enhanced Shockburst packets on channels 10, 14, and 18. 52 | 53 | ```./nordic_channelized_transmitter.py ``` 54 | 55 | ## wireshark dissector 56 | 57 | The wireshark dissector will display Enhanced Shockburst packets in Wireshark. The logic is very straightforward, and will be simple to extend to classify various device types. 58 | 59 | ### wireshark/nordic_dissector.lua 60 | 61 | ```wireshark -X lua_script:wireshark/nordic_dissector.lua -i lo -k -f udp ``` 62 | 63 | ## nRF24LU1+ research firmware 64 | 65 | Corresponding research firmware for the nRF24LU1+ chips (including Logitech Unifying dongles) is available [here](https://github.com/BastilleResearch/nrf-research-firmware/). 66 | 67 | Documentation on the packet formats covered by the MouseJack and KeySniffer vulnerability sets is available [here](https://github.com/BastilleResearch/mousejack/tree/master/doc/pdf). 68 | -------------------------------------------------------------------------------- /apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | include(GrPython) 17 | 18 | GR_PYTHON_INSTALL( 19 | PROGRAMS 20 | DESTINATION bin 21 | ) 22 | -------------------------------------------------------------------------------- /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 | ${CMAKE_INSTALL_PREFIX}/include 19 | PATHS 20 | /usr/local/include 21 | /usr/include 22 | ) 23 | 24 | FIND_LIBRARY(CPPUNIT_LIBRARIES 25 | NAMES cppunit 26 | HINTS ${PC_CPPUNIT_LIBDIR} 27 | ${CMAKE_INSTALL_PREFIX}/lib 28 | ${CMAKE_INSTALL_PREFIX}/lib64 29 | PATHS 30 | ${CPPUNIT_INCLUDE_DIRS}/../lib 31 | /usr/local/lib 32 | /usr/lib 33 | ) 34 | 35 | LIST(APPEND CPPUNIT_LIBRARIES ${CMAKE_DL_LIBS}) 36 | 37 | INCLUDE(FindPackageHandleStandardArgs) 38 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(CPPUNIT DEFAULT_MSG CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS) 39 | MARK_AS_ADVANCED(CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS) 40 | -------------------------------------------------------------------------------- /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 (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | if(DEFINED __INCLUDED_GR_MISC_UTILS_CMAKE) 17 | return() 18 | endif() 19 | set(__INCLUDED_GR_MISC_UTILS_CMAKE TRUE) 20 | 21 | ######################################################################## 22 | # Set global variable macro. 23 | # Used for subdirectories to export settings. 24 | # Example: include and library paths. 25 | ######################################################################## 26 | function(GR_SET_GLOBAL var) 27 | set(${var} ${ARGN} CACHE INTERNAL "" FORCE) 28 | endfunction(GR_SET_GLOBAL) 29 | 30 | ######################################################################## 31 | # Set the pre-processor definition if the condition is true. 32 | # - def the pre-processor definition to set and condition name 33 | ######################################################################## 34 | function(GR_ADD_COND_DEF def) 35 | if(${def}) 36 | add_definitions(-D${def}) 37 | endif(${def}) 38 | endfunction(GR_ADD_COND_DEF) 39 | 40 | ######################################################################## 41 | # Check for a header and conditionally set a compile define. 42 | # - hdr the relative path to the header file 43 | # - def the pre-processor definition to set 44 | ######################################################################## 45 | function(GR_CHECK_HDR_N_DEF hdr def) 46 | include(CheckIncludeFileCXX) 47 | CHECK_INCLUDE_FILE_CXX(${hdr} ${def}) 48 | GR_ADD_COND_DEF(${def}) 49 | endfunction(GR_CHECK_HDR_N_DEF) 50 | 51 | ######################################################################## 52 | # Include subdirectory macro. 53 | # Sets the CMake directory variables, 54 | # includes the subdirectory CMakeLists.txt, 55 | # resets the CMake directory variables. 56 | # 57 | # This macro includes subdirectories rather than adding them 58 | # so that the subdirectory can affect variables in the level above. 59 | # This provides a work-around for the lack of convenience libraries. 60 | # This way a subdirectory can append to the list of library sources. 61 | ######################################################################## 62 | macro(GR_INCLUDE_SUBDIRECTORY subdir) 63 | #insert the current directories on the front of the list 64 | list(INSERT _cmake_source_dirs 0 ${CMAKE_CURRENT_SOURCE_DIR}) 65 | list(INSERT _cmake_binary_dirs 0 ${CMAKE_CURRENT_BINARY_DIR}) 66 | 67 | #set the current directories to the names of the subdirs 68 | set(CMAKE_CURRENT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${subdir}) 69 | set(CMAKE_CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${subdir}) 70 | 71 | #include the subdirectory CMakeLists to run it 72 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 73 | include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) 74 | 75 | #reset the value of the current directories 76 | list(GET _cmake_source_dirs 0 CMAKE_CURRENT_SOURCE_DIR) 77 | list(GET _cmake_binary_dirs 0 CMAKE_CURRENT_BINARY_DIR) 78 | 79 | #pop the subdir names of the front of the list 80 | list(REMOVE_AT _cmake_source_dirs 0) 81 | list(REMOVE_AT _cmake_binary_dirs 0) 82 | endmacro(GR_INCLUDE_SUBDIRECTORY) 83 | 84 | ######################################################################## 85 | # Check if a compiler flag works and conditionally set a compile define. 86 | # - flag the compiler flag to check for 87 | # - have the variable to set with result 88 | ######################################################################## 89 | macro(GR_ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have) 90 | include(CheckCXXCompilerFlag) 91 | CHECK_CXX_COMPILER_FLAG(${flag} ${have}) 92 | if(${have}) 93 | if(${CMAKE_VERSION} VERSION_GREATER "2.8.4") 94 | STRING(FIND "${CMAKE_CXX_FLAGS}" "${flag}" flag_dup) 95 | if(${flag_dup} EQUAL -1) 96 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${flag}") 97 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${flag}") 98 | endif(${flag_dup} EQUAL -1) 99 | endif(${CMAKE_VERSION} VERSION_GREATER "2.8.4") 100 | endif(${have}) 101 | endmacro(GR_ADD_CXX_COMPILER_FLAG_IF_AVAILABLE) 102 | 103 | ######################################################################## 104 | # Generates the .la libtool file 105 | # This appears to generate libtool files that cannot be used by auto*. 106 | # Usage GR_LIBTOOL(TARGET [target] DESTINATION [dest]) 107 | # Notice: there is not COMPONENT option, these will not get distributed. 108 | ######################################################################## 109 | function(GR_LIBTOOL) 110 | if(NOT DEFINED GENERATE_LIBTOOL) 111 | set(GENERATE_LIBTOOL OFF) #disabled by default 112 | endif() 113 | 114 | if(GENERATE_LIBTOOL) 115 | include(CMakeParseArgumentsCopy) 116 | CMAKE_PARSE_ARGUMENTS(GR_LIBTOOL "" "TARGET;DESTINATION" "" ${ARGN}) 117 | 118 | find_program(LIBTOOL libtool) 119 | if(LIBTOOL) 120 | include(CMakeMacroLibtoolFile) 121 | CREATE_LIBTOOL_FILE(${GR_LIBTOOL_TARGET} /${GR_LIBTOOL_DESTINATION}) 122 | endif(LIBTOOL) 123 | endif(GENERATE_LIBTOOL) 124 | 125 | endfunction(GR_LIBTOOL) 126 | 127 | ######################################################################## 128 | # Do standard things to the library target 129 | # - set target properties 130 | # - make install rules 131 | # Also handle gnuradio custom naming conventions w/ extras mode. 132 | ######################################################################## 133 | function(GR_LIBRARY_FOO target) 134 | #parse the arguments for component names 135 | include(CMakeParseArgumentsCopy) 136 | CMAKE_PARSE_ARGUMENTS(GR_LIBRARY "" "RUNTIME_COMPONENT;DEVEL_COMPONENT" "" ${ARGN}) 137 | 138 | #set additional target properties 139 | set_target_properties(${target} PROPERTIES SOVERSION ${LIBVER}) 140 | 141 | #install the generated files like so... 142 | install(TARGETS ${target} 143 | LIBRARY DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} # .so/.dylib file 144 | ARCHIVE DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_DEVEL_COMPONENT} # .lib file 145 | RUNTIME DESTINATION ${GR_RUNTIME_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} # .dll file 146 | ) 147 | 148 | #extras mode enabled automatically on linux 149 | if(NOT DEFINED LIBRARY_EXTRAS) 150 | set(LIBRARY_EXTRAS ${LINUX}) 151 | endif() 152 | 153 | #special extras mode to enable alternative naming conventions 154 | if(LIBRARY_EXTRAS) 155 | 156 | #create .la file before changing props 157 | GR_LIBTOOL(TARGET ${target} DESTINATION ${GR_LIBRARY_DIR}) 158 | 159 | #give the library a special name with ultra-zero soversion 160 | set_target_properties(${target} PROPERTIES OUTPUT_NAME ${target}-${LIBVER} SOVERSION "0.0.0") 161 | set(target_name lib${target}-${LIBVER}.so.0.0.0) 162 | 163 | #custom command to generate symlinks 164 | add_custom_command( 165 | TARGET ${target} 166 | POST_BUILD 167 | COMMAND ${CMAKE_COMMAND} -E create_symlink ${target_name} ${CMAKE_CURRENT_BINARY_DIR}/lib${target}.so 168 | COMMAND ${CMAKE_COMMAND} -E create_symlink ${target_name} ${CMAKE_CURRENT_BINARY_DIR}/lib${target}-${LIBVER}.so.0 169 | COMMAND ${CMAKE_COMMAND} -E touch ${target_name} #so the symlinks point to something valid so cmake 2.6 will install 170 | ) 171 | 172 | #and install the extra symlinks 173 | install( 174 | FILES 175 | ${CMAKE_CURRENT_BINARY_DIR}/lib${target}.so 176 | ${CMAKE_CURRENT_BINARY_DIR}/lib${target}-${LIBVER}.so.0 177 | DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} 178 | ) 179 | 180 | endif(LIBRARY_EXTRAS) 181 | endfunction(GR_LIBRARY_FOO) 182 | 183 | ######################################################################## 184 | # Create a dummy custom command that depends on other targets. 185 | # Usage: 186 | # GR_GEN_TARGET_DEPS(unique_name target_deps ...) 187 | # ADD_CUSTOM_COMMAND( ${target_deps}) 188 | # 189 | # Custom command cant depend on targets, but can depend on executables, 190 | # and executables can depend on targets. So this is the process: 191 | ######################################################################## 192 | function(GR_GEN_TARGET_DEPS name var) 193 | file( 194 | WRITE ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp.in 195 | "int main(void){return 0;}\n" 196 | ) 197 | execute_process( 198 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 199 | ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp.in 200 | ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp 201 | ) 202 | add_executable(${name} ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp) 203 | if(ARGN) 204 | add_dependencies(${name} ${ARGN}) 205 | endif(ARGN) 206 | 207 | if(CMAKE_CROSSCOMPILING) 208 | set(${var} "DEPENDS;${name}" PARENT_SCOPE) #cant call command when cross 209 | else() 210 | set(${var} "DEPENDS;${name};COMMAND;${name}" PARENT_SCOPE) 211 | endif() 212 | endfunction(GR_GEN_TARGET_DEPS) 213 | 214 | ######################################################################## 215 | # Control use of gr_logger 216 | # Usage: 217 | # GR_LOGGING() 218 | # 219 | # Will set ENABLE_GR_LOG to 1 by default. 220 | # Can manually set with -DENABLE_GR_LOG=0|1 221 | ######################################################################## 222 | function(GR_LOGGING) 223 | find_package(Log4cpp) 224 | 225 | OPTION(ENABLE_GR_LOG "Use gr_logger" ON) 226 | if(ENABLE_GR_LOG) 227 | # If gr_logger is enabled, make it usable 228 | add_definitions( -DENABLE_GR_LOG ) 229 | 230 | # also test LOG4CPP; if we have it, use this version of the logger 231 | # otherwise, default to the stdout/stderr model. 232 | if(LOG4CPP_FOUND) 233 | SET(HAVE_LOG4CPP True CACHE INTERNAL "" FORCE) 234 | add_definitions( -DHAVE_LOG4CPP ) 235 | else(not LOG4CPP_FOUND) 236 | SET(HAVE_LOG4CPP False CACHE INTERNAL "" FORCE) 237 | SET(LOG4CPP_INCLUDE_DIRS "" CACHE INTERNAL "" FORCE) 238 | SET(LOG4CPP_LIBRARY_DIRS "" CACHE INTERNAL "" FORCE) 239 | SET(LOG4CPP_LIBRARIES "" CACHE INTERNAL "" FORCE) 240 | endif(LOG4CPP_FOUND) 241 | 242 | SET(ENABLE_GR_LOG ${ENABLE_GR_LOG} CACHE INTERNAL "" FORCE) 243 | 244 | else(ENABLE_GR_LOG) 245 | SET(HAVE_LOG4CPP False CACHE INTERNAL "" FORCE) 246 | SET(LOG4CPP_INCLUDE_DIRS "" CACHE INTERNAL "" FORCE) 247 | SET(LOG4CPP_LIBRARY_DIRS "" CACHE INTERNAL "" FORCE) 248 | SET(LOG4CPP_LIBRARIES "" CACHE INTERNAL "" FORCE) 249 | endif(ENABLE_GR_LOG) 250 | 251 | message(STATUS "ENABLE_GR_LOG set to ${ENABLE_GR_LOG}.") 252 | message(STATUS "HAVE_LOG4CPP set to ${HAVE_LOG4CPP}.") 253 | message(STATUS "LOG4CPP_LIBRARIES set to ${LOG4CPP_LIBRARIES}.") 254 | 255 | endfunction(GR_LOGGING) 256 | 257 | ######################################################################## 258 | # Run GRCC to compile .grc files into .py files. 259 | # 260 | # Usage: GRCC(filename, directory) 261 | # - filenames: List of file name of .grc file 262 | # - directory: directory of built .py file - usually in 263 | # ${CMAKE_CURRENT_BINARY_DIR} 264 | # - Sets PYFILES: output converted GRC file names to Python files. 265 | ######################################################################## 266 | function(GRCC) 267 | # Extract directory from list of args, remove it for the list of filenames. 268 | list(GET ARGV -1 directory) 269 | list(REMOVE_AT ARGV -1) 270 | set(filenames ${ARGV}) 271 | file(MAKE_DIRECTORY ${directory}) 272 | 273 | SET(GRCC_COMMAND ${CMAKE_SOURCE_DIR}/gr-utils/python/grcc) 274 | 275 | # GRCC uses some stuff in grc and gnuradio-runtime, so we force 276 | # the known paths here 277 | list(APPEND PYTHONPATHS 278 | ${CMAKE_SOURCE_DIR} 279 | ${CMAKE_SOURCE_DIR}/gnuradio-runtime/python 280 | ${CMAKE_SOURCE_DIR}/gnuradio-runtime/lib/swig 281 | ${CMAKE_BINARY_DIR}/gnuradio-runtime/lib/swig 282 | ) 283 | 284 | if(WIN32) 285 | #SWIG generates the python library files into a subdirectory. 286 | #Therefore, we must append this subdirectory into PYTHONPATH. 287 | #Only do this for the python directories matching the following: 288 | foreach(pydir ${PYTHONPATHS}) 289 | get_filename_component(name ${pydir} NAME) 290 | if(name MATCHES "^(swig|lib|src)$") 291 | list(APPEND PYTHONPATHS ${pydir}/${CMAKE_BUILD_TYPE}) 292 | endif() 293 | endforeach(pydir) 294 | endif(WIN32) 295 | 296 | file(TO_NATIVE_PATH "${PYTHONPATHS}" pypath) 297 | 298 | if(UNIX) 299 | list(APPEND pypath "$PYTHONPATH") 300 | string(REPLACE ";" ":" pypath "${pypath}") 301 | set(ENV{PYTHONPATH} ${pypath}) 302 | endif(UNIX) 303 | 304 | if(WIN32) 305 | list(APPEND pypath "%PYTHONPATH%") 306 | string(REPLACE ";" "\\;" pypath "${pypath}") 307 | #list(APPEND environs "PYTHONPATH=${pypath}") 308 | set(ENV{PYTHONPATH} ${pypath}) 309 | endif(WIN32) 310 | 311 | foreach(f ${filenames}) 312 | execute_process( 313 | COMMAND ${GRCC_COMMAND} -d ${directory} ${f} 314 | ) 315 | string(REPLACE ".grc" ".py" pyfile "${f}") 316 | string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" pyfile "${pyfile}") 317 | list(APPEND pyfiles ${pyfile}) 318 | endforeach(f) 319 | 320 | set(PYFILES ${pyfiles} PARENT_SCOPE) 321 | endfunction(GRCC) 322 | 323 | ######################################################################## 324 | # Check if HAVE_PTHREAD_SETSCHEDPARAM and HAVE_SCHED_SETSCHEDULER 325 | # should be defined 326 | ######################################################################## 327 | macro(GR_CHECK_LINUX_SCHED_AVAIL) 328 | set(CMAKE_REQUIRED_LIBRARIES -lpthread) 329 | CHECK_CXX_SOURCE_COMPILES(" 330 | #include 331 | int main(){ 332 | pthread_t pthread; 333 | pthread_setschedparam(pthread, 0, 0); 334 | return 0; 335 | } " HAVE_PTHREAD_SETSCHEDPARAM 336 | ) 337 | GR_ADD_COND_DEF(HAVE_PTHREAD_SETSCHEDPARAM) 338 | 339 | CHECK_CXX_SOURCE_COMPILES(" 340 | #include 341 | int main(){ 342 | pid_t pid; 343 | sched_setscheduler(pid, 0, 0); 344 | return 0; 345 | } " HAVE_SCHED_SETSCHEDULER 346 | ) 347 | GR_ADD_COND_DEF(HAVE_SCHED_SETSCHEDULER) 348 | endmacro(GR_CHECK_LINUX_SCHED_AVAIL) 349 | 350 | ######################################################################## 351 | # Macros to generate source and header files from template 352 | ######################################################################## 353 | macro(GR_EXPAND_X_H component root) 354 | 355 | include(GrPython) 356 | 357 | file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py 358 | "#!${PYTHON_EXECUTABLE} 359 | 360 | import sys, os, re 361 | sys.path.append('${GR_RUNTIME_PYTHONPATH}') 362 | os.environ['srcdir'] = '${CMAKE_CURRENT_SOURCE_DIR}' 363 | os.chdir('${CMAKE_CURRENT_BINARY_DIR}') 364 | 365 | if __name__ == '__main__': 366 | import build_utils 367 | root, inp = sys.argv[1:3] 368 | for sig in sys.argv[3:]: 369 | name = re.sub ('X+', sig, root) 370 | d = build_utils.standard_dict2(name, sig, '${component}') 371 | build_utils.expand_template(d, inp) 372 | ") 373 | 374 | #make a list of all the generated headers 375 | unset(expanded_files_h) 376 | foreach(sig ${ARGN}) 377 | string(REGEX REPLACE "X+" ${sig} name ${root}) 378 | list(APPEND expanded_files_h ${CMAKE_CURRENT_BINARY_DIR}/${name}.h) 379 | endforeach(sig) 380 | unset(name) 381 | 382 | #create a command to generate the headers 383 | add_custom_command( 384 | OUTPUT ${expanded_files_h} 385 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${root}.h.t 386 | COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} 387 | ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py 388 | ${root} ${root}.h.t ${ARGN} 389 | ) 390 | 391 | #install rules for the generated headers 392 | list(APPEND generated_includes ${expanded_files_h}) 393 | 394 | endmacro(GR_EXPAND_X_H) 395 | 396 | macro(GR_EXPAND_X_CC_H component root) 397 | 398 | include(GrPython) 399 | 400 | file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py 401 | "#!${PYTHON_EXECUTABLE} 402 | 403 | import sys, os, re 404 | sys.path.append('${GR_RUNTIME_PYTHONPATH}') 405 | os.environ['srcdir'] = '${CMAKE_CURRENT_SOURCE_DIR}' 406 | os.chdir('${CMAKE_CURRENT_BINARY_DIR}') 407 | 408 | if __name__ == '__main__': 409 | import build_utils 410 | root, inp = sys.argv[1:3] 411 | for sig in sys.argv[3:]: 412 | name = re.sub ('X+', sig, root) 413 | d = build_utils.standard_impl_dict2(name, sig, '${component}') 414 | build_utils.expand_template(d, inp) 415 | ") 416 | 417 | #make a list of all the generated files 418 | unset(expanded_files_cc) 419 | unset(expanded_files_h) 420 | foreach(sig ${ARGN}) 421 | string(REGEX REPLACE "X+" ${sig} name ${root}) 422 | list(APPEND expanded_files_cc ${CMAKE_CURRENT_BINARY_DIR}/${name}.cc) 423 | list(APPEND expanded_files_h ${CMAKE_CURRENT_BINARY_DIR}/${name}.h) 424 | endforeach(sig) 425 | unset(name) 426 | 427 | #create a command to generate the source files 428 | add_custom_command( 429 | OUTPUT ${expanded_files_cc} 430 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${root}.cc.t 431 | COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} 432 | ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py 433 | ${root} ${root}.cc.t ${ARGN} 434 | ) 435 | 436 | #create a command to generate the header files 437 | add_custom_command( 438 | OUTPUT ${expanded_files_h} 439 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${root}.h.t 440 | COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} 441 | ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py 442 | ${root} ${root}.h.t ${ARGN} 443 | ) 444 | 445 | #make source files depends on headers to force generation 446 | set_source_files_properties(${expanded_files_cc} 447 | PROPERTIES OBJECT_DEPENDS "${expanded_files_h}" 448 | ) 449 | 450 | #install rules for the generated files 451 | list(APPEND generated_sources ${expanded_files_cc}) 452 | list(APPEND generated_headers ${expanded_files_h}) 453 | 454 | endmacro(GR_EXPAND_X_CC_H) 455 | 456 | macro(GR_EXPAND_X_CC_H_IMPL component root) 457 | 458 | include(GrPython) 459 | 460 | file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py 461 | "#!${PYTHON_EXECUTABLE} 462 | 463 | import sys, os, re 464 | sys.path.append('${GR_RUNTIME_PYTHONPATH}') 465 | os.environ['srcdir'] = '${CMAKE_CURRENT_SOURCE_DIR}' 466 | os.chdir('${CMAKE_CURRENT_BINARY_DIR}') 467 | 468 | if __name__ == '__main__': 469 | import build_utils 470 | root, inp = sys.argv[1:3] 471 | for sig in sys.argv[3:]: 472 | name = re.sub ('X+', sig, root) 473 | d = build_utils.standard_dict(name, sig, '${component}') 474 | build_utils.expand_template(d, inp, '_impl') 475 | ") 476 | 477 | #make a list of all the generated files 478 | unset(expanded_files_cc_impl) 479 | unset(expanded_files_h_impl) 480 | unset(expanded_files_h) 481 | foreach(sig ${ARGN}) 482 | string(REGEX REPLACE "X+" ${sig} name ${root}) 483 | list(APPEND expanded_files_cc_impl ${CMAKE_CURRENT_BINARY_DIR}/${name}_impl.cc) 484 | list(APPEND expanded_files_h_impl ${CMAKE_CURRENT_BINARY_DIR}/${name}_impl.h) 485 | list(APPEND expanded_files_h ${CMAKE_CURRENT_BINARY_DIR}/../include/gnuradio/${component}/${name}.h) 486 | endforeach(sig) 487 | unset(name) 488 | 489 | #create a command to generate the _impl.cc files 490 | add_custom_command( 491 | OUTPUT ${expanded_files_cc_impl} 492 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${root}_impl.cc.t 493 | COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} 494 | ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py 495 | ${root} ${root}_impl.cc.t ${ARGN} 496 | ) 497 | 498 | #create a command to generate the _impl.h files 499 | add_custom_command( 500 | OUTPUT ${expanded_files_h_impl} 501 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${root}_impl.h.t 502 | COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} 503 | ${CMAKE_CURRENT_BINARY_DIR}/generate_helper.py 504 | ${root} ${root}_impl.h.t ${ARGN} 505 | ) 506 | 507 | #make _impl.cc source files depend on _impl.h to force generation 508 | set_source_files_properties(${expanded_files_cc_impl} 509 | PROPERTIES OBJECT_DEPENDS "${expanded_files_h_impl}" 510 | ) 511 | 512 | #make _impl.h source files depend on headers to force generation 513 | set_source_files_properties(${expanded_files_h_impl} 514 | PROPERTIES OBJECT_DEPENDS "${expanded_files_h}" 515 | ) 516 | 517 | #install rules for the generated files 518 | list(APPEND generated_sources ${expanded_files_cc_impl}) 519 | list(APPEND generated_headers ${expanded_files_h_impl}) 520 | 521 | endmacro(GR_EXPAND_X_CC_H_IMPL) 522 | -------------------------------------------------------------------------------- /cmake/Modules/GrPlatform.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | if(DEFINED __INCLUDED_GR_PLATFORM_CMAKE) 17 | return() 18 | endif() 19 | set(__INCLUDED_GR_PLATFORM_CMAKE TRUE) 20 | 21 | ######################################################################## 22 | # Setup additional defines for OS types 23 | ######################################################################## 24 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 25 | set(LINUX TRUE) 26 | endif() 27 | 28 | if(NOT CMAKE_CROSSCOMPILING AND LINUX AND EXISTS "/etc/debian_version") 29 | set(DEBIAN TRUE) 30 | endif() 31 | 32 | if(NOT CMAKE_CROSSCOMPILING AND LINUX AND EXISTS "/etc/redhat-release") 33 | set(REDHAT TRUE) 34 | endif() 35 | 36 | if(NOT CMAKE_CROSSCOMPILING AND LINUX AND EXISTS "/etc/slackware-version") 37 | set(SLACKWARE TRUE) 38 | endif() 39 | 40 | ######################################################################## 41 | # when the library suffix should be 64 (applies to redhat linux family) 42 | ######################################################################## 43 | if (REDHAT OR SLACKWARE) 44 | set(LIB64_CONVENTION TRUE) 45 | endif() 46 | 47 | if(NOT DEFINED LIB_SUFFIX AND LIB64_CONVENTION AND CMAKE_SYSTEM_PROCESSOR MATCHES "64$") 48 | set(LIB_SUFFIX 64) 49 | endif() 50 | set(LIB_SUFFIX ${LIB_SUFFIX} CACHE STRING "lib directory suffix") 51 | -------------------------------------------------------------------------------- /cmake/Modules/GrPython.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | if(DEFINED __INCLUDED_GR_PYTHON_CMAKE) 17 | return() 18 | endif() 19 | set(__INCLUDED_GR_PYTHON_CMAKE TRUE) 20 | 21 | ######################################################################## 22 | # Setup the python interpreter: 23 | # This allows the user to specify a specific interpreter, 24 | # or finds the interpreter via the built-in cmake module. 25 | ######################################################################## 26 | #this allows the user to override PYTHON_EXECUTABLE 27 | if(PYTHON_EXECUTABLE) 28 | 29 | set(PYTHONINTERP_FOUND TRUE) 30 | 31 | #otherwise if not set, try to automatically find it 32 | else(PYTHON_EXECUTABLE) 33 | 34 | #use the built-in find script 35 | find_package(PythonInterp 2) 36 | 37 | #and if that fails use the find program routine 38 | if(NOT PYTHONINTERP_FOUND) 39 | find_program(PYTHON_EXECUTABLE NAMES python python2 python2.7 python2.6 python2.5) 40 | if(PYTHON_EXECUTABLE) 41 | set(PYTHONINTERP_FOUND TRUE) 42 | endif(PYTHON_EXECUTABLE) 43 | endif(NOT PYTHONINTERP_FOUND) 44 | 45 | endif(PYTHON_EXECUTABLE) 46 | 47 | if (CMAKE_CROSSCOMPILING) 48 | set(QA_PYTHON_EXECUTABLE "/usr/bin/python") 49 | else (CMAKE_CROSSCOMPILING) 50 | set(QA_PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE}) 51 | endif(CMAKE_CROSSCOMPILING) 52 | 53 | #make the path to the executable appear in the cmake gui 54 | set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} CACHE FILEPATH "python interpreter") 55 | set(QA_PYTHON_EXECUTABLE ${QA_PYTHON_EXECUTABLE} CACHE FILEPATH "python interpreter for QA tests") 56 | 57 | #make sure we can use -B with python (introduced in 2.6) 58 | if(PYTHON_EXECUTABLE) 59 | execute_process( 60 | COMMAND ${PYTHON_EXECUTABLE} -B -c "" 61 | OUTPUT_QUIET ERROR_QUIET 62 | RESULT_VARIABLE PYTHON_HAS_DASH_B_RESULT 63 | ) 64 | if(PYTHON_HAS_DASH_B_RESULT EQUAL 0) 65 | set(PYTHON_DASH_B "-B") 66 | endif() 67 | endif(PYTHON_EXECUTABLE) 68 | 69 | ######################################################################## 70 | # Check for the existence of a python module: 71 | # - desc a string description of the check 72 | # - mod the name of the module to import 73 | # - cmd an additional command to run 74 | # - have the result variable to set 75 | ######################################################################## 76 | macro(GR_PYTHON_CHECK_MODULE desc mod cmd have) 77 | message(STATUS "") 78 | message(STATUS "Python checking for ${desc}") 79 | execute_process( 80 | COMMAND ${PYTHON_EXECUTABLE} -c " 81 | ######################################### 82 | try: 83 | import ${mod} 84 | assert ${cmd} 85 | except ImportError, AssertionError: exit(-1) 86 | except: pass 87 | #########################################" 88 | RESULT_VARIABLE ${have} 89 | ) 90 | if(${have} EQUAL 0) 91 | message(STATUS "Python checking for ${desc} - found") 92 | set(${have} TRUE) 93 | else(${have} EQUAL 0) 94 | message(STATUS "Python checking for ${desc} - not found") 95 | set(${have} FALSE) 96 | endif(${have} EQUAL 0) 97 | endmacro(GR_PYTHON_CHECK_MODULE) 98 | 99 | ######################################################################## 100 | # Sets the python installation directory GR_PYTHON_DIR 101 | ######################################################################## 102 | if(NOT DEFINED GR_PYTHON_DIR) 103 | execute_process(COMMAND ${PYTHON_EXECUTABLE} -c " 104 | from distutils import sysconfig 105 | print sysconfig.get_python_lib(plat_specific=True, prefix='') 106 | " OUTPUT_VARIABLE GR_PYTHON_DIR OUTPUT_STRIP_TRAILING_WHITESPACE 107 | ) 108 | endif() 109 | file(TO_CMAKE_PATH ${GR_PYTHON_DIR} GR_PYTHON_DIR) 110 | 111 | ######################################################################## 112 | # Create an always-built target with a unique name 113 | # Usage: GR_UNIQUE_TARGET( ) 114 | ######################################################################## 115 | function(GR_UNIQUE_TARGET desc) 116 | file(RELATIVE_PATH reldir ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) 117 | execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import re, hashlib 118 | unique = hashlib.md5('${reldir}${ARGN}').hexdigest()[:5] 119 | print(re.sub('\\W', '_', '${desc} ${reldir} ' + unique))" 120 | OUTPUT_VARIABLE _target OUTPUT_STRIP_TRAILING_WHITESPACE) 121 | add_custom_target(${_target} ALL DEPENDS ${ARGN}) 122 | endfunction(GR_UNIQUE_TARGET) 123 | 124 | ######################################################################## 125 | # Install python sources (also builds and installs byte-compiled python) 126 | ######################################################################## 127 | function(GR_PYTHON_INSTALL) 128 | include(CMakeParseArgumentsCopy) 129 | CMAKE_PARSE_ARGUMENTS(GR_PYTHON_INSTALL "" "DESTINATION;COMPONENT" "FILES;PROGRAMS" ${ARGN}) 130 | 131 | #################################################################### 132 | if(GR_PYTHON_INSTALL_FILES) 133 | #################################################################### 134 | install(${ARGN}) #installs regular python files 135 | 136 | #create a list of all generated files 137 | unset(pysrcfiles) 138 | unset(pycfiles) 139 | unset(pyofiles) 140 | foreach(pyfile ${GR_PYTHON_INSTALL_FILES}) 141 | get_filename_component(pyfile ${pyfile} ABSOLUTE) 142 | list(APPEND pysrcfiles ${pyfile}) 143 | 144 | #determine if this file is in the source or binary directory 145 | file(RELATIVE_PATH source_rel_path ${CMAKE_CURRENT_SOURCE_DIR} ${pyfile}) 146 | string(LENGTH "${source_rel_path}" source_rel_path_len) 147 | file(RELATIVE_PATH binary_rel_path ${CMAKE_CURRENT_BINARY_DIR} ${pyfile}) 148 | string(LENGTH "${binary_rel_path}" binary_rel_path_len) 149 | 150 | #and set the generated path appropriately 151 | if(${source_rel_path_len} GREATER ${binary_rel_path_len}) 152 | set(pygenfile ${CMAKE_CURRENT_BINARY_DIR}/${binary_rel_path}) 153 | else() 154 | set(pygenfile ${CMAKE_CURRENT_BINARY_DIR}/${source_rel_path}) 155 | endif() 156 | list(APPEND pycfiles ${pygenfile}c) 157 | list(APPEND pyofiles ${pygenfile}o) 158 | 159 | #ensure generation path exists 160 | get_filename_component(pygen_path ${pygenfile} PATH) 161 | file(MAKE_DIRECTORY ${pygen_path}) 162 | 163 | endforeach(pyfile) 164 | 165 | #the command to generate the pyc files 166 | add_custom_command( 167 | DEPENDS ${pysrcfiles} OUTPUT ${pycfiles} 168 | COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_BINARY_DIR}/python_compile_helper.py ${pysrcfiles} ${pycfiles} 169 | ) 170 | 171 | #the command to generate the pyo files 172 | add_custom_command( 173 | DEPENDS ${pysrcfiles} OUTPUT ${pyofiles} 174 | COMMAND ${PYTHON_EXECUTABLE} -O ${CMAKE_BINARY_DIR}/python_compile_helper.py ${pysrcfiles} ${pyofiles} 175 | ) 176 | 177 | #create install rule and add generated files to target list 178 | set(python_install_gen_targets ${pycfiles} ${pyofiles}) 179 | install(FILES ${python_install_gen_targets} 180 | DESTINATION ${GR_PYTHON_INSTALL_DESTINATION} 181 | COMPONENT ${GR_PYTHON_INSTALL_COMPONENT} 182 | ) 183 | 184 | #################################################################### 185 | elseif(GR_PYTHON_INSTALL_PROGRAMS) 186 | #################################################################### 187 | file(TO_NATIVE_PATH ${PYTHON_EXECUTABLE} pyexe_native) 188 | 189 | if (CMAKE_CROSSCOMPILING) 190 | set(pyexe_native "/usr/bin/env python") 191 | endif() 192 | 193 | foreach(pyfile ${GR_PYTHON_INSTALL_PROGRAMS}) 194 | get_filename_component(pyfile_name ${pyfile} NAME) 195 | get_filename_component(pyfile ${pyfile} ABSOLUTE) 196 | string(REPLACE "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" pyexefile "${pyfile}.exe") 197 | list(APPEND python_install_gen_targets ${pyexefile}) 198 | 199 | get_filename_component(pyexefile_path ${pyexefile} PATH) 200 | file(MAKE_DIRECTORY ${pyexefile_path}) 201 | 202 | add_custom_command( 203 | OUTPUT ${pyexefile} DEPENDS ${pyfile} 204 | COMMAND ${PYTHON_EXECUTABLE} -c 205 | "import re; R=re.compile('^\#!.*$\\n',flags=re.MULTILINE); open('${pyexefile}','w').write('\#!${pyexe_native}\\n'+R.sub('',open('${pyfile}','r').read()))" 206 | COMMENT "Shebangin ${pyfile_name}" 207 | VERBATIM 208 | ) 209 | 210 | #on windows, python files need an extension to execute 211 | get_filename_component(pyfile_ext ${pyfile} EXT) 212 | if(WIN32 AND NOT pyfile_ext) 213 | set(pyfile_name "${pyfile_name}.py") 214 | endif() 215 | 216 | install(PROGRAMS ${pyexefile} RENAME ${pyfile_name} 217 | DESTINATION ${GR_PYTHON_INSTALL_DESTINATION} 218 | COMPONENT ${GR_PYTHON_INSTALL_COMPONENT} 219 | ) 220 | endforeach(pyfile) 221 | 222 | endif() 223 | 224 | GR_UNIQUE_TARGET("pygen" ${python_install_gen_targets}) 225 | 226 | endfunction(GR_PYTHON_INSTALL) 227 | 228 | ######################################################################## 229 | # Write the python helper script that generates byte code files 230 | ######################################################################## 231 | file(WRITE ${CMAKE_BINARY_DIR}/python_compile_helper.py " 232 | import sys, py_compile 233 | files = sys.argv[1:] 234 | srcs, gens = files[:len(files)/2], files[len(files)/2:] 235 | for src, gen in zip(srcs, gens): 236 | py_compile.compile(file=src, cfile=gen, doraise=True) 237 | ") 238 | -------------------------------------------------------------------------------- /cmake/Modules/GrSwig.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | if(DEFINED __INCLUDED_GR_SWIG_CMAKE) 17 | return() 18 | endif() 19 | set(__INCLUDED_GR_SWIG_CMAKE TRUE) 20 | 21 | include(GrPython) 22 | 23 | ######################################################################## 24 | # Builds a swig documentation file to be generated into python docstrings 25 | # Usage: GR_SWIG_MAKE_DOCS(output_file input_path input_path....) 26 | # 27 | # Set the following variable to specify extra dependent targets: 28 | # - GR_SWIG_DOCS_SOURCE_DEPS 29 | # - GR_SWIG_DOCS_TARGET_DEPS 30 | ######################################################################## 31 | function(GR_SWIG_MAKE_DOCS output_file) 32 | if(ENABLE_DOXYGEN) 33 | 34 | #setup the input files variable list, quote formated 35 | set(input_files) 36 | unset(INPUT_PATHS) 37 | foreach(input_path ${ARGN}) 38 | if(IS_DIRECTORY ${input_path}) #when input path is a directory 39 | file(GLOB input_path_h_files ${input_path}/*.h) 40 | else() #otherwise its just a file, no glob 41 | set(input_path_h_files ${input_path}) 42 | endif() 43 | list(APPEND input_files ${input_path_h_files}) 44 | set(INPUT_PATHS "${INPUT_PATHS} \"${input_path}\"") 45 | endforeach(input_path) 46 | 47 | #determine the output directory 48 | get_filename_component(name ${output_file} NAME_WE) 49 | get_filename_component(OUTPUT_DIRECTORY ${output_file} PATH) 50 | set(OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}/${name}_swig_docs) 51 | make_directory(${OUTPUT_DIRECTORY}) 52 | 53 | #generate the Doxyfile used by doxygen 54 | configure_file( 55 | ${CMAKE_SOURCE_DIR}/docs/doxygen/Doxyfile.swig_doc.in 56 | ${OUTPUT_DIRECTORY}/Doxyfile 57 | @ONLY) 58 | 59 | #Create a dummy custom command that depends on other targets 60 | include(GrMiscUtils) 61 | GR_GEN_TARGET_DEPS(_${name}_tag tag_deps ${GR_SWIG_DOCS_TARGET_DEPS}) 62 | 63 | #call doxygen on the Doxyfile + input headers 64 | add_custom_command( 65 | OUTPUT ${OUTPUT_DIRECTORY}/xml/index.xml 66 | DEPENDS ${input_files} ${GR_SWIG_DOCS_SOURCE_DEPS} ${tag_deps} 67 | COMMAND ${DOXYGEN_EXECUTABLE} ${OUTPUT_DIRECTORY}/Doxyfile 68 | COMMENT "Generating doxygen xml for ${name} docs" 69 | ) 70 | 71 | #call the swig_doc script on the xml files 72 | add_custom_command( 73 | OUTPUT ${output_file} 74 | DEPENDS ${input_files} ${stamp-file} ${OUTPUT_DIRECTORY}/xml/index.xml 75 | COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} 76 | ${CMAKE_SOURCE_DIR}/docs/doxygen/swig_doc.py 77 | ${OUTPUT_DIRECTORY}/xml 78 | ${output_file} 79 | COMMENT "Generating python docstrings for ${name}" 80 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs/doxygen 81 | ) 82 | 83 | else(ENABLE_DOXYGEN) 84 | file(WRITE ${output_file} "\n") #no doxygen -> empty file 85 | endif(ENABLE_DOXYGEN) 86 | endfunction(GR_SWIG_MAKE_DOCS) 87 | 88 | ######################################################################## 89 | # Build a swig target for the common gnuradio use case. Usage: 90 | # GR_SWIG_MAKE(target ifile ifile ifile...) 91 | # 92 | # Set the following variables before calling: 93 | # - GR_SWIG_FLAGS 94 | # - GR_SWIG_INCLUDE_DIRS 95 | # - GR_SWIG_LIBRARIES 96 | # - GR_SWIG_SOURCE_DEPS 97 | # - GR_SWIG_TARGET_DEPS 98 | # - GR_SWIG_DOC_FILE 99 | # - GR_SWIG_DOC_DIRS 100 | ######################################################################## 101 | macro(GR_SWIG_MAKE name) 102 | set(ifiles ${ARGN}) 103 | 104 | # Shimming this in here to take care of a SWIG bug with handling 105 | # vector and vector (on 32-bit machines) and 106 | # vector (on 64-bit machines). Use this to test 107 | # the size of size_t, then set SIZE_T_32 if it's a 32-bit machine 108 | # or not if it's 64-bit. The logic in gr_type.i handles the rest. 109 | INCLUDE(CheckTypeSize) 110 | CHECK_TYPE_SIZE("size_t" SIZEOF_SIZE_T) 111 | CHECK_TYPE_SIZE("unsigned int" SIZEOF_UINT) 112 | if(${SIZEOF_SIZE_T} EQUAL ${SIZEOF_UINT}) 113 | list(APPEND GR_SWIG_FLAGS -DSIZE_T_32) 114 | endif(${SIZEOF_SIZE_T} EQUAL ${SIZEOF_UINT}) 115 | 116 | #do swig doc generation if specified 117 | if(GR_SWIG_DOC_FILE) 118 | set(GR_SWIG_DOCS_SOURCE_DEPS ${GR_SWIG_SOURCE_DEPS}) 119 | list(APPEND GR_SWIG_DOCS_TARGET_DEPS ${GR_SWIG_TARGET_DEPS}) 120 | GR_SWIG_MAKE_DOCS(${GR_SWIG_DOC_FILE} ${GR_SWIG_DOC_DIRS}) 121 | add_custom_target(${name}_swig_doc DEPENDS ${GR_SWIG_DOC_FILE}) 122 | list(APPEND GR_SWIG_TARGET_DEPS ${name}_swig_doc ${GR_RUNTIME_SWIG_DOC_FILE}) 123 | endif() 124 | 125 | #append additional include directories 126 | find_package(PythonLibs 2) 127 | list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_PATH}) #deprecated name (now dirs) 128 | list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS}) 129 | 130 | #prepend local swig directories 131 | list(INSERT GR_SWIG_INCLUDE_DIRS 0 ${CMAKE_CURRENT_SOURCE_DIR}) 132 | list(INSERT GR_SWIG_INCLUDE_DIRS 0 ${CMAKE_CURRENT_BINARY_DIR}) 133 | 134 | #determine include dependencies for swig file 135 | execute_process( 136 | COMMAND ${PYTHON_EXECUTABLE} 137 | ${CMAKE_BINARY_DIR}/get_swig_deps.py 138 | "${ifiles}" "${GR_SWIG_INCLUDE_DIRS}" 139 | OUTPUT_STRIP_TRAILING_WHITESPACE 140 | OUTPUT_VARIABLE SWIG_MODULE_${name}_EXTRA_DEPS 141 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 142 | ) 143 | 144 | #Create a dummy custom command that depends on other targets 145 | include(GrMiscUtils) 146 | GR_GEN_TARGET_DEPS(_${name}_swig_tag tag_deps ${GR_SWIG_TARGET_DEPS}) 147 | set(tag_file ${CMAKE_CURRENT_BINARY_DIR}/${name}.tag) 148 | add_custom_command( 149 | OUTPUT ${tag_file} 150 | DEPENDS ${GR_SWIG_SOURCE_DEPS} ${tag_deps} 151 | COMMAND ${CMAKE_COMMAND} -E touch ${tag_file} 152 | ) 153 | 154 | #append the specified include directories 155 | include_directories(${GR_SWIG_INCLUDE_DIRS}) 156 | list(APPEND SWIG_MODULE_${name}_EXTRA_DEPS ${tag_file}) 157 | 158 | #setup the swig flags with flags and include directories 159 | set(CMAKE_SWIG_FLAGS -fvirtual -modern -keyword -w511 -module ${name} ${GR_SWIG_FLAGS}) 160 | foreach(dir ${GR_SWIG_INCLUDE_DIRS}) 161 | list(APPEND CMAKE_SWIG_FLAGS "-I${dir}") 162 | endforeach(dir) 163 | 164 | #set the C++ property on the swig .i file so it builds 165 | set_source_files_properties(${ifiles} PROPERTIES CPLUSPLUS ON) 166 | 167 | #setup the actual swig library target to be built 168 | include(UseSWIG) 169 | SWIG_ADD_MODULE(${name} python ${ifiles}) 170 | SWIG_LINK_LIBRARIES(${name} ${PYTHON_LIBRARIES} ${GR_SWIG_LIBRARIES}) 171 | if(${name} STREQUAL "runtime_swig") 172 | SET_TARGET_PROPERTIES(${SWIG_MODULE_runtime_swig_REAL_NAME} PROPERTIES DEFINE_SYMBOL "gnuradio_runtime_EXPORTS") 173 | endif(${name} STREQUAL "runtime_swig") 174 | 175 | endmacro(GR_SWIG_MAKE) 176 | 177 | ######################################################################## 178 | # Install swig targets generated by GR_SWIG_MAKE. Usage: 179 | # GR_SWIG_INSTALL( 180 | # TARGETS target target target... 181 | # [DESTINATION destination] 182 | # [COMPONENT component] 183 | # ) 184 | ######################################################################## 185 | macro(GR_SWIG_INSTALL) 186 | 187 | include(CMakeParseArgumentsCopy) 188 | CMAKE_PARSE_ARGUMENTS(GR_SWIG_INSTALL "" "DESTINATION;COMPONENT" "TARGETS" ${ARGN}) 189 | 190 | foreach(name ${GR_SWIG_INSTALL_TARGETS}) 191 | install(TARGETS ${SWIG_MODULE_${name}_REAL_NAME} 192 | DESTINATION ${GR_SWIG_INSTALL_DESTINATION} 193 | COMPONENT ${GR_SWIG_INSTALL_COMPONENT} 194 | ) 195 | 196 | include(GrPython) 197 | GR_PYTHON_INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${name}.py 198 | DESTINATION ${GR_SWIG_INSTALL_DESTINATION} 199 | COMPONENT ${GR_SWIG_INSTALL_COMPONENT} 200 | ) 201 | 202 | GR_LIBTOOL( 203 | TARGET ${SWIG_MODULE_${name}_REAL_NAME} 204 | DESTINATION ${GR_SWIG_INSTALL_DESTINATION} 205 | ) 206 | 207 | endforeach(name) 208 | 209 | endmacro(GR_SWIG_INSTALL) 210 | 211 | ######################################################################## 212 | # Generate a python file that can determine swig dependencies. 213 | # Used by the make macro above to determine extra dependencies. 214 | # When you build C++, CMake figures out the header dependencies. 215 | # This code essentially performs that logic for swig includes. 216 | ######################################################################## 217 | file(WRITE ${CMAKE_BINARY_DIR}/get_swig_deps.py " 218 | 219 | import os, sys, re 220 | 221 | i_include_matcher = re.compile('%(include|import)\\s*[<|\"](.*)[>|\"]') 222 | h_include_matcher = re.compile('#(include)\\s*[<|\"](.*)[>|\"]') 223 | include_dirs = sys.argv[2].split(';') 224 | 225 | def get_swig_incs(file_path): 226 | if file_path.endswith('.i'): matcher = i_include_matcher 227 | else: matcher = h_include_matcher 228 | file_contents = open(file_path, 'r').read() 229 | return matcher.findall(file_contents, re.MULTILINE) 230 | 231 | def get_swig_deps(file_path, level): 232 | deps = [file_path] 233 | if level == 0: return deps 234 | for keyword, inc_file in get_swig_incs(file_path): 235 | for inc_dir in include_dirs: 236 | inc_path = os.path.join(inc_dir, inc_file) 237 | if not os.path.exists(inc_path): continue 238 | deps.extend(get_swig_deps(inc_path, level-1)) 239 | break #found, we dont search in lower prio inc dirs 240 | return deps 241 | 242 | if __name__ == '__main__': 243 | ifiles = sys.argv[1].split(';') 244 | deps = sum([get_swig_deps(ifile, 3) for ifile in ifiles], []) 245 | #sys.stderr.write(';'.join(set(deps)) + '\\n\\n') 246 | print(';'.join(set(deps))) 247 | ") 248 | -------------------------------------------------------------------------------- /cmake/Modules/GrTest.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | if(DEFINED __INCLUDED_GR_TEST_CMAKE) 17 | return() 18 | endif() 19 | set(__INCLUDED_GR_TEST_CMAKE TRUE) 20 | 21 | ######################################################################## 22 | # Add a unit test and setup the environment for a unit test. 23 | # Takes the same arguments as the ADD_TEST function. 24 | # 25 | # Before calling set the following variables: 26 | # GR_TEST_TARGET_DEPS - built targets for the library path 27 | # GR_TEST_LIBRARY_DIRS - directories for the library path 28 | # GR_TEST_PYTHON_DIRS - directories for the python path 29 | # GR_TEST_ENVIRONS - other environment key/value pairs 30 | ######################################################################## 31 | function(GR_ADD_TEST test_name) 32 | 33 | #Ensure that the build exe also appears in the PATH. 34 | list(APPEND GR_TEST_TARGET_DEPS ${ARGN}) 35 | 36 | #In the land of windows, all libraries must be in the PATH. 37 | #Since the dependent libraries are not yet installed, 38 | #we must manually set them in the PATH to run tests. 39 | #The following appends the path of a target dependency. 40 | foreach(target ${GR_TEST_TARGET_DEPS}) 41 | get_target_property(location ${target} LOCATION) 42 | if(location) 43 | get_filename_component(path ${location} PATH) 44 | string(REGEX REPLACE "\\$\\(.*\\)" ${CMAKE_BUILD_TYPE} path ${path}) 45 | list(APPEND GR_TEST_LIBRARY_DIRS ${path}) 46 | endif(location) 47 | endforeach(target) 48 | 49 | if(WIN32) 50 | #SWIG generates the python library files into a subdirectory. 51 | #Therefore, we must append this subdirectory into PYTHONPATH. 52 | #Only do this for the python directories matching the following: 53 | foreach(pydir ${GR_TEST_PYTHON_DIRS}) 54 | get_filename_component(name ${pydir} NAME) 55 | if(name MATCHES "^(swig|lib|src)$") 56 | list(APPEND GR_TEST_PYTHON_DIRS ${pydir}/${CMAKE_BUILD_TYPE}) 57 | endif() 58 | endforeach(pydir) 59 | endif(WIN32) 60 | 61 | file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR} srcdir) 62 | file(TO_NATIVE_PATH "${GR_TEST_LIBRARY_DIRS}" libpath) #ok to use on dir list? 63 | file(TO_NATIVE_PATH "${GR_TEST_PYTHON_DIRS}" pypath) #ok to use on dir list? 64 | 65 | set(environs "VOLK_GENERIC=1" "GR_DONT_LOAD_PREFS=1" "srcdir=${srcdir}") 66 | list(APPEND environs ${GR_TEST_ENVIRONS}) 67 | 68 | #http://www.cmake.org/pipermail/cmake/2009-May/029464.html 69 | #Replaced this add test + set environs code with the shell script generation. 70 | #Its nicer to be able to manually run the shell script to diagnose problems. 71 | #ADD_TEST(${ARGV}) 72 | #SET_TESTS_PROPERTIES(${test_name} PROPERTIES ENVIRONMENT "${environs}") 73 | 74 | if(UNIX) 75 | set(LD_PATH_VAR "LD_LIBRARY_PATH") 76 | if(APPLE) 77 | set(LD_PATH_VAR "DYLD_LIBRARY_PATH") 78 | endif() 79 | 80 | set(binpath "${CMAKE_CURRENT_BINARY_DIR}:$PATH") 81 | list(APPEND libpath "$${LD_PATH_VAR}") 82 | list(APPEND pypath "$PYTHONPATH") 83 | 84 | #replace list separator with the path separator 85 | string(REPLACE ";" ":" libpath "${libpath}") 86 | string(REPLACE ";" ":" pypath "${pypath}") 87 | list(APPEND environs "PATH=${binpath}" "${LD_PATH_VAR}=${libpath}" "PYTHONPATH=${pypath}") 88 | 89 | #generate a bat file that sets the environment and runs the test 90 | if (CMAKE_CROSSCOMPILING) 91 | set(SHELL "/bin/sh") 92 | else(CMAKE_CROSSCOMPILING) 93 | find_program(SHELL sh) 94 | endif(CMAKE_CROSSCOMPILING) 95 | set(sh_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.sh) 96 | file(WRITE ${sh_file} "#!${SHELL}\n") 97 | #each line sets an environment variable 98 | foreach(environ ${environs}) 99 | file(APPEND ${sh_file} "export ${environ}\n") 100 | endforeach(environ) 101 | #load the command to run with its arguments 102 | foreach(arg ${ARGN}) 103 | file(APPEND ${sh_file} "${arg} ") 104 | endforeach(arg) 105 | file(APPEND ${sh_file} "\n") 106 | 107 | #make the shell file executable 108 | execute_process(COMMAND chmod +x ${sh_file}) 109 | 110 | add_test(${test_name} ${SHELL} ${sh_file}) 111 | 112 | endif(UNIX) 113 | 114 | if(WIN32) 115 | list(APPEND libpath ${DLL_PATHS} "%PATH%") 116 | list(APPEND pypath "%PYTHONPATH%") 117 | 118 | #replace list separator with the path separator (escaped) 119 | string(REPLACE ";" "\\;" libpath "${libpath}") 120 | string(REPLACE ";" "\\;" pypath "${pypath}") 121 | list(APPEND environs "PATH=${libpath}" "PYTHONPATH=${pypath}") 122 | 123 | #generate a bat file that sets the environment and runs the test 124 | set(bat_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.bat) 125 | file(WRITE ${bat_file} "@echo off\n") 126 | #each line sets an environment variable 127 | foreach(environ ${environs}) 128 | file(APPEND ${bat_file} "SET ${environ}\n") 129 | endforeach(environ) 130 | #load the command to run with its arguments 131 | foreach(arg ${ARGN}) 132 | file(APPEND ${bat_file} "${arg} ") 133 | endforeach(arg) 134 | file(APPEND ${bat_file} "\n") 135 | 136 | add_test(${test_name} ${bat_file}) 137 | endif(WIN32) 138 | 139 | endfunction(GR_ADD_TEST) 140 | -------------------------------------------------------------------------------- /cmake/Modules/UseSWIG.cmake: -------------------------------------------------------------------------------- 1 | # - SWIG module for CMake 2 | # Defines the following macros: 3 | # SWIG_ADD_MODULE(name language [ files ]) 4 | # - Define swig module with given name and specified language 5 | # SWIG_LINK_LIBRARIES(name [ libraries ]) 6 | # - Link libraries to swig module 7 | # All other macros are for internal use only. 8 | # To get the actual name of the swig module, 9 | # use: ${SWIG_MODULE_${name}_REAL_NAME}. 10 | # Set Source files properties such as CPLUSPLUS and SWIG_FLAGS to specify 11 | # special behavior of SWIG. Also global CMAKE_SWIG_FLAGS can be used to add 12 | # special flags to all swig calls. 13 | # Another special variable is CMAKE_SWIG_OUTDIR, it allows one to specify 14 | # where to write all the swig generated module (swig -outdir option) 15 | # The name-specific variable SWIG_MODULE__EXTRA_DEPS may be used 16 | # to specify extra dependencies for the generated modules. 17 | # If the source file generated by swig need some special flag you can use 18 | # set_source_files_properties( ${swig_generated_file_fullname} 19 | # PROPERTIES COMPILE_FLAGS "-bla") 20 | 21 | 22 | #============================================================================= 23 | # Copyright 2004-2009 Kitware, Inc. 24 | # Copyright 2009 Mathieu Malaterre 25 | # 26 | # Distributed under the OSI-approved BSD License (the "License"); 27 | # see accompanying file Copyright.txt for details. 28 | # 29 | # This software is distributed WITHOUT ANY WARRANTY; without even the 30 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 31 | # See the License for more information. 32 | #============================================================================= 33 | # (To distribute this file outside of CMake, substitute the full 34 | # License text for the above reference.) 35 | 36 | set(SWIG_CXX_EXTENSION "cxx") 37 | set(SWIG_EXTRA_LIBRARIES "") 38 | 39 | set(SWIG_PYTHON_EXTRA_FILE_EXTENSION "py") 40 | 41 | # 42 | # For given swig module initialize variables associated with it 43 | # 44 | macro(SWIG_MODULE_INITIALIZE name language) 45 | string(TOUPPER "${language}" swig_uppercase_language) 46 | string(TOLOWER "${language}" swig_lowercase_language) 47 | set(SWIG_MODULE_${name}_LANGUAGE "${swig_uppercase_language}") 48 | set(SWIG_MODULE_${name}_SWIG_LANGUAGE_FLAG "${swig_lowercase_language}") 49 | 50 | set(SWIG_MODULE_${name}_REAL_NAME "${name}") 51 | if("${SWIG_MODULE_${name}_LANGUAGE}" STREQUAL "UNKNOWN") 52 | message(FATAL_ERROR "SWIG Error: Language \"${language}\" not found") 53 | elseif("${SWIG_MODULE_${name}_LANGUAGE}" STREQUAL "PYTHON") 54 | # when swig is used without the -interface it will produce in the module.py 55 | # a 'import _modulename' statement, which implies having a corresponding 56 | # _modulename.so (*NIX), _modulename.pyd (Win32). 57 | set(SWIG_MODULE_${name}_REAL_NAME "_${name}") 58 | elseif("${SWIG_MODULE_${name}_LANGUAGE}" STREQUAL "PERL") 59 | set(SWIG_MODULE_${name}_EXTRA_FLAGS "-shadow") 60 | endif() 61 | endmacro() 62 | 63 | # 64 | # For a given language, input file, and output file, determine extra files that 65 | # will be generated. This is internal swig macro. 66 | # 67 | 68 | macro(SWIG_GET_EXTRA_OUTPUT_FILES language outfiles generatedpath infile) 69 | set(${outfiles} "") 70 | get_source_file_property(SWIG_GET_EXTRA_OUTPUT_FILES_module_basename 71 | ${infile} SWIG_MODULE_NAME) 72 | if(SWIG_GET_EXTRA_OUTPUT_FILES_module_basename STREQUAL "NOTFOUND") 73 | get_filename_component(SWIG_GET_EXTRA_OUTPUT_FILES_module_basename "${infile}" NAME_WE) 74 | endif() 75 | foreach(it ${SWIG_${language}_EXTRA_FILE_EXTENSION}) 76 | set(${outfiles} ${${outfiles}} 77 | "${generatedpath}/${SWIG_GET_EXTRA_OUTPUT_FILES_module_basename}.${it}") 78 | endforeach() 79 | endmacro() 80 | 81 | # 82 | # Take swig (*.i) file and add proper custom commands for it 83 | # 84 | macro(SWIG_ADD_SOURCE_TO_MODULE name outfiles infile) 85 | set(swig_full_infile ${infile}) 86 | get_filename_component(swig_source_file_path "${infile}" PATH) 87 | get_filename_component(swig_source_file_name_we "${infile}" NAME_WE) 88 | get_source_file_property(swig_source_file_generated ${infile} GENERATED) 89 | get_source_file_property(swig_source_file_cplusplus ${infile} CPLUSPLUS) 90 | get_source_file_property(swig_source_file_flags ${infile} SWIG_FLAGS) 91 | if("${swig_source_file_flags}" STREQUAL "NOTFOUND") 92 | set(swig_source_file_flags "") 93 | endif() 94 | set(swig_source_file_fullname "${infile}") 95 | if(${swig_source_file_path} MATCHES "^${CMAKE_CURRENT_SOURCE_DIR}") 96 | string(REGEX REPLACE 97 | "^${CMAKE_CURRENT_SOURCE_DIR}" "" 98 | swig_source_file_relative_path 99 | "${swig_source_file_path}") 100 | else() 101 | if(${swig_source_file_path} MATCHES "^${CMAKE_CURRENT_BINARY_DIR}") 102 | string(REGEX REPLACE 103 | "^${CMAKE_CURRENT_BINARY_DIR}" "" 104 | swig_source_file_relative_path 105 | "${swig_source_file_path}") 106 | set(swig_source_file_generated 1) 107 | else() 108 | set(swig_source_file_relative_path "${swig_source_file_path}") 109 | if(swig_source_file_generated) 110 | set(swig_source_file_fullname "${CMAKE_CURRENT_BINARY_DIR}/${infile}") 111 | else() 112 | set(swig_source_file_fullname "${CMAKE_CURRENT_SOURCE_DIR}/${infile}") 113 | endif() 114 | endif() 115 | endif() 116 | 117 | set(swig_generated_file_fullname 118 | "${CMAKE_CURRENT_BINARY_DIR}") 119 | if(swig_source_file_relative_path) 120 | set(swig_generated_file_fullname 121 | "${swig_generated_file_fullname}/${swig_source_file_relative_path}") 122 | endif() 123 | # If CMAKE_SWIG_OUTDIR was specified then pass it to -outdir 124 | if(CMAKE_SWIG_OUTDIR) 125 | set(swig_outdir ${CMAKE_SWIG_OUTDIR}) 126 | else() 127 | set(swig_outdir ${CMAKE_CURRENT_BINARY_DIR}) 128 | endif() 129 | SWIG_GET_EXTRA_OUTPUT_FILES(${SWIG_MODULE_${name}_LANGUAGE} 130 | swig_extra_generated_files 131 | "${swig_outdir}" 132 | "${infile}") 133 | set(swig_generated_file_fullname 134 | "${swig_generated_file_fullname}/${swig_source_file_name_we}") 135 | # add the language into the name of the file (i.e. TCL_wrap) 136 | # this allows for the same .i file to be wrapped into different languages 137 | set(swig_generated_file_fullname 138 | "${swig_generated_file_fullname}${SWIG_MODULE_${name}_LANGUAGE}_wrap") 139 | 140 | if(swig_source_file_cplusplus) 141 | set(swig_generated_file_fullname 142 | "${swig_generated_file_fullname}.${SWIG_CXX_EXTENSION}") 143 | else() 144 | set(swig_generated_file_fullname 145 | "${swig_generated_file_fullname}.c") 146 | endif() 147 | 148 | # Shut up some warnings from poor SWIG code generation that we 149 | # can do nothing about, when this flag is available 150 | include(CheckCXXCompilerFlag) 151 | check_cxx_compiler_flag("-Wno-unused-but-set-variable" HAVE_WNO_UNUSED_BUT_SET_VARIABLE) 152 | if(HAVE_WNO_UNUSED_BUT_SET_VARIABLE) 153 | set_source_files_properties(${swig_generated_file_fullname} 154 | PROPERTIES COMPILE_FLAGS "-Wno-unused-but-set-variable") 155 | endif(HAVE_WNO_UNUSED_BUT_SET_VARIABLE) 156 | 157 | get_directory_property(cmake_include_directories INCLUDE_DIRECTORIES) 158 | set(swig_include_dirs) 159 | foreach(it ${cmake_include_directories}) 160 | set(swig_include_dirs ${swig_include_dirs} "-I${it}") 161 | endforeach() 162 | 163 | set(swig_special_flags) 164 | # default is c, so add c++ flag if it is c++ 165 | if(swig_source_file_cplusplus) 166 | set(swig_special_flags ${swig_special_flags} "-c++") 167 | endif() 168 | set(swig_extra_flags) 169 | if(SWIG_MODULE_${name}_EXTRA_FLAGS) 170 | set(swig_extra_flags ${swig_extra_flags} ${SWIG_MODULE_${name}_EXTRA_FLAGS}) 171 | endif() 172 | 173 | # hack to work around CMake bug in add_custom_command with multiple OUTPUT files 174 | 175 | file(RELATIVE_PATH reldir ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) 176 | execute_process( 177 | COMMAND ${PYTHON_EXECUTABLE} -c "import re, hashlib 178 | unique = hashlib.md5('${reldir}${ARGN}').hexdigest()[:5] 179 | print(re.sub('\\W', '_', '${name} ${reldir} ' + unique))" 180 | OUTPUT_VARIABLE _target OUTPUT_STRIP_TRAILING_WHITESPACE 181 | ) 182 | 183 | file( 184 | WRITE ${CMAKE_CURRENT_BINARY_DIR}/${_target}.cpp.in 185 | "int main(void){return 0;}\n" 186 | ) 187 | 188 | # create dummy dependencies 189 | add_custom_command( 190 | OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_target}.cpp 191 | COMMAND ${CMAKE_COMMAND} -E copy 192 | ${CMAKE_CURRENT_BINARY_DIR}/${_target}.cpp.in 193 | ${CMAKE_CURRENT_BINARY_DIR}/${_target}.cpp 194 | DEPENDS "${swig_source_file_fullname}" ${SWIG_MODULE_${name}_EXTRA_DEPS} 195 | COMMENT "" 196 | ) 197 | 198 | # create the dummy target 199 | add_executable(${_target} ${CMAKE_CURRENT_BINARY_DIR}/${_target}.cpp) 200 | 201 | # add a custom command to the dummy target 202 | add_custom_command( 203 | TARGET ${_target} 204 | # Let's create the ${swig_outdir} at execution time, in case dir contains $(OutDir) 205 | COMMAND ${CMAKE_COMMAND} -E make_directory ${swig_outdir} 206 | COMMAND "${SWIG_EXECUTABLE}" 207 | ARGS "-${SWIG_MODULE_${name}_SWIG_LANGUAGE_FLAG}" 208 | ${swig_source_file_flags} 209 | ${CMAKE_SWIG_FLAGS} 210 | -outdir ${swig_outdir} 211 | ${swig_special_flags} 212 | ${swig_extra_flags} 213 | ${swig_include_dirs} 214 | -o "${swig_generated_file_fullname}" 215 | "${swig_source_file_fullname}" 216 | COMMENT "Swig source" 217 | ) 218 | 219 | #add dummy independent dependencies from the _target to each file 220 | #that will be generated by the SWIG command above 221 | 222 | set(${outfiles} "${swig_generated_file_fullname}" ${swig_extra_generated_files}) 223 | 224 | foreach(swig_gen_file ${${outfiles}}) 225 | add_custom_command( 226 | OUTPUT ${swig_gen_file} 227 | COMMAND "" 228 | DEPENDS ${_target} 229 | COMMENT "" 230 | ) 231 | endforeach() 232 | 233 | set_source_files_properties( 234 | ${outfiles} PROPERTIES GENERATED 1 235 | ) 236 | 237 | endmacro() 238 | 239 | # 240 | # Create Swig module 241 | # 242 | macro(SWIG_ADD_MODULE name language) 243 | SWIG_MODULE_INITIALIZE(${name} ${language}) 244 | set(swig_dot_i_sources) 245 | set(swig_other_sources) 246 | foreach(it ${ARGN}) 247 | if(${it} MATCHES ".*\\.i$") 248 | set(swig_dot_i_sources ${swig_dot_i_sources} "${it}") 249 | else() 250 | set(swig_other_sources ${swig_other_sources} "${it}") 251 | endif() 252 | endforeach() 253 | 254 | set(swig_generated_sources) 255 | foreach(it ${swig_dot_i_sources}) 256 | SWIG_ADD_SOURCE_TO_MODULE(${name} swig_generated_source ${it}) 257 | set(swig_generated_sources ${swig_generated_sources} "${swig_generated_source}") 258 | endforeach() 259 | get_directory_property(swig_extra_clean_files ADDITIONAL_MAKE_CLEAN_FILES) 260 | set_directory_properties(PROPERTIES 261 | ADDITIONAL_MAKE_CLEAN_FILES "${swig_extra_clean_files};${swig_generated_sources}") 262 | add_library(${SWIG_MODULE_${name}_REAL_NAME} 263 | MODULE 264 | ${swig_generated_sources} 265 | ${swig_other_sources}) 266 | string(TOLOWER "${language}" swig_lowercase_language) 267 | if ("${swig_lowercase_language}" STREQUAL "java") 268 | if (APPLE) 269 | # In java you want: 270 | # System.loadLibrary("LIBRARY"); 271 | # then JNI will look for a library whose name is platform dependent, namely 272 | # MacOS : libLIBRARY.jnilib 273 | # Windows: LIBRARY.dll 274 | # Linux : libLIBRARY.so 275 | set_target_properties (${SWIG_MODULE_${name}_REAL_NAME} PROPERTIES SUFFIX ".jnilib") 276 | endif () 277 | endif () 278 | if ("${swig_lowercase_language}" STREQUAL "python") 279 | # this is only needed for the python case where a _modulename.so is generated 280 | set_target_properties(${SWIG_MODULE_${name}_REAL_NAME} PROPERTIES PREFIX "") 281 | # Python extension modules on Windows must have the extension ".pyd" 282 | # instead of ".dll" as of Python 2.5. Older python versions do support 283 | # this suffix. 284 | # http://docs.python.org/whatsnew/ports.html#SECTION0001510000000000000000 285 | # 286 | # Windows: .dll is no longer supported as a filename extension for extension modules. 287 | # .pyd is now the only filename extension that will be searched for. 288 | # 289 | if(WIN32 AND NOT CYGWIN) 290 | set_target_properties(${SWIG_MODULE_${name}_REAL_NAME} PROPERTIES SUFFIX ".pyd") 291 | endif() 292 | endif () 293 | endmacro() 294 | 295 | # 296 | # Like TARGET_LINK_LIBRARIES but for swig modules 297 | # 298 | macro(SWIG_LINK_LIBRARIES name) 299 | if(SWIG_MODULE_${name}_REAL_NAME) 300 | target_link_libraries(${SWIG_MODULE_${name}_REAL_NAME} ${ARGN}) 301 | else() 302 | message(SEND_ERROR "Cannot find Swig library \"${name}\".") 303 | endif() 304 | endmacro() 305 | -------------------------------------------------------------------------------- /cmake/Modules/nordicConfig.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_NORDIC nordic) 3 | 4 | FIND_PATH( 5 | NORDIC_INCLUDE_DIRS 6 | NAMES nordic/api.h 7 | HINTS $ENV{NORDIC_DIR}/include 8 | ${PC_NORDIC_INCLUDEDIR} 9 | PATHS ${CMAKE_INSTALL_PREFIX}/include 10 | /usr/local/include 11 | /usr/include 12 | ) 13 | 14 | FIND_LIBRARY( 15 | NORDIC_LIBRARIES 16 | NAMES gnuradio-nordic 17 | HINTS $ENV{NORDIC_DIR}/lib 18 | ${PC_NORDIC_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(NORDIC DEFAULT_MSG NORDIC_LIBRARIES NORDIC_INCLUDE_DIRS) 29 | MARK_AS_ADVANCED(NORDIC_LIBRARIES NORDIC_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 (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | ######################################################################## 17 | # Setup dependencies 18 | ######################################################################## 19 | find_package(Doxygen) 20 | 21 | ######################################################################## 22 | # Begin conditional configuration 23 | ######################################################################## 24 | if(ENABLE_DOXYGEN) 25 | 26 | ######################################################################## 27 | # Add subdirectories 28 | ######################################################################## 29 | add_subdirectory(doxygen) 30 | 31 | endif(ENABLE_DOXYGEN) 32 | -------------------------------------------------------------------------------- /docs/README.nordic: -------------------------------------------------------------------------------- 1 | This is the nordic-write-a-block package meant as a guide to building 2 | out-of-tree packages. To use the nordic blocks, the Python namespaces 3 | is in 'nordic', which is imported as: 4 | 5 | import nordic 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(nordic) 12 | -------------------------------------------------------------------------------- /docs/doxygen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | ######################################################################## 17 | # Create the doxygen configuration file 18 | ######################################################################## 19 | file(TO_NATIVE_PATH ${CMAKE_SOURCE_DIR} top_srcdir) 20 | file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR} top_builddir) 21 | file(TO_NATIVE_PATH ${CMAKE_SOURCE_DIR} abs_top_srcdir) 22 | file(TO_NATIVE_PATH ${CMAKE_BINARY_DIR} abs_top_builddir) 23 | 24 | set(HAVE_DOT ${DOXYGEN_DOT_FOUND}) 25 | set(enable_html_docs YES) 26 | set(enable_latex_docs NO) 27 | set(enable_xml_docs YES) 28 | 29 | configure_file( 30 | ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in 31 | ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile 32 | @ONLY) 33 | 34 | set(BUILT_DIRS ${CMAKE_CURRENT_BINARY_DIR}/xml ${CMAKE_CURRENT_BINARY_DIR}/html) 35 | 36 | ######################################################################## 37 | # Make and install doxygen docs 38 | ######################################################################## 39 | add_custom_command( 40 | OUTPUT ${BUILT_DIRS} 41 | COMMAND ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile 42 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 43 | COMMENT "Generating documentation with doxygen" 44 | ) 45 | 46 | add_custom_target(doxygen_target ALL DEPENDS ${BUILT_DIRS}) 47 | 48 | install(DIRECTORY ${BUILT_DIRS} DESTINATION ${GR_PKG_DOC_DIR}) 49 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Copyright (C) 2016 Bastille Networks 3 | 4 | This program 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 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 15 | along with this program. If not, see . 16 | ''' 17 | 18 | """ 19 | Python interface to contents of doxygen xml documentation. 20 | 21 | Example use: 22 | See the contents of the example folder for the C++ and 23 | doxygen-generated xml used in this example. 24 | 25 | >>> # Parse the doxygen docs. 26 | >>> import os 27 | >>> this_dir = os.path.dirname(globals()['__file__']) 28 | >>> xml_path = this_dir + "/example/xml/" 29 | >>> di = DoxyIndex(xml_path) 30 | 31 | Get a list of all top-level objects. 32 | 33 | >>> print([mem.name() for mem in di.members()]) 34 | [u'Aadvark', u'aadvarky_enough', u'main'] 35 | 36 | Get all functions. 37 | 38 | >>> print([mem.name() for mem in di.in_category(DoxyFunction)]) 39 | [u'aadvarky_enough', u'main'] 40 | 41 | Check if an object is present. 42 | 43 | >>> di.has_member(u'Aadvark') 44 | True 45 | >>> di.has_member(u'Fish') 46 | False 47 | 48 | Get an item by name and check its properties. 49 | 50 | >>> aad = di.get_member(u'Aadvark') 51 | >>> print(aad.brief_description) 52 | Models the mammal Aadvark. 53 | >>> print(aad.detailed_description) 54 | Sadly the model is incomplete and cannot capture all aspects of an aadvark yet. 55 | 56 | This line is uninformative and is only to test line breaks in the comments. 57 | >>> [mem.name() for mem in aad.members()] 58 | [u'aadvarkness', u'print', u'Aadvark', u'get_aadvarkness'] 59 | >>> aad.get_member(u'print').brief_description 60 | u'Outputs the vital aadvark statistics.' 61 | 62 | """ 63 | 64 | from doxyindex import DoxyIndex, DoxyFunction, DoxyParam, DoxyClass, DoxyFile, DoxyNamespace, DoxyGroup, DoxyFriend, DoxyOther 65 | 66 | def _test(): 67 | import os 68 | this_dir = os.path.dirname(globals()['__file__']) 69 | xml_path = this_dir + "/example/xml/" 70 | di = DoxyIndex(xml_path) 71 | # Get the Aadvark class 72 | aad = di.get_member('Aadvark') 73 | aad.brief_description 74 | import doctest 75 | return doctest.testmod() 76 | 77 | if __name__ == "__main__": 78 | _test() 79 | 80 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/base.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Copyright (C) 2016 Bastille Networks 3 | 4 | This program 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 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 15 | along with this program. If not, see . 16 | ''' 17 | 18 | """ 19 | A base class is created. 20 | 21 | Classes based upon this are used to make more user-friendly interfaces 22 | to the doxygen xml docs than the generated classes provide. 23 | """ 24 | 25 | import os 26 | import pdb 27 | 28 | from xml.parsers.expat import ExpatError 29 | 30 | from generated import compound 31 | 32 | 33 | class Base(object): 34 | 35 | class Duplicate(StandardError): 36 | pass 37 | 38 | class NoSuchMember(StandardError): 39 | pass 40 | 41 | class ParsingError(StandardError): 42 | pass 43 | 44 | def __init__(self, parse_data, top=None): 45 | self._parsed = False 46 | self._error = False 47 | self._parse_data = parse_data 48 | self._members = [] 49 | self._dict_members = {} 50 | self._in_category = {} 51 | self._data = {} 52 | if top is not None: 53 | self._xml_path = top._xml_path 54 | # Set up holder of references 55 | else: 56 | top = self 57 | self._refs = {} 58 | self._xml_path = parse_data 59 | self.top = top 60 | 61 | @classmethod 62 | def from_refid(cls, refid, top=None): 63 | """ Instantiate class from a refid rather than parsing object. """ 64 | # First check to see if its already been instantiated. 65 | if top is not None and refid in top._refs: 66 | return top._refs[refid] 67 | # Otherwise create a new instance and set refid. 68 | inst = cls(None, top=top) 69 | inst.refid = refid 70 | inst.add_ref(inst) 71 | return inst 72 | 73 | @classmethod 74 | def from_parse_data(cls, parse_data, top=None): 75 | refid = getattr(parse_data, 'refid', None) 76 | if refid is not None and top is not None and refid in top._refs: 77 | return top._refs[refid] 78 | inst = cls(parse_data, top=top) 79 | if refid is not None: 80 | inst.refid = refid 81 | inst.add_ref(inst) 82 | return inst 83 | 84 | def add_ref(self, obj): 85 | if hasattr(obj, 'refid'): 86 | self.top._refs[obj.refid] = obj 87 | 88 | mem_classes = [] 89 | 90 | def get_cls(self, mem): 91 | for cls in self.mem_classes: 92 | if cls.can_parse(mem): 93 | return cls 94 | raise StandardError(("Did not find a class for object '%s'." \ 95 | % (mem.get_name()))) 96 | 97 | def convert_mem(self, mem): 98 | try: 99 | cls = self.get_cls(mem) 100 | converted = cls.from_parse_data(mem, self.top) 101 | if converted is None: 102 | raise StandardError('No class matched this object.') 103 | self.add_ref(converted) 104 | return converted 105 | except StandardError, e: 106 | print e 107 | 108 | @classmethod 109 | def includes(cls, inst): 110 | return isinstance(inst, cls) 111 | 112 | @classmethod 113 | def can_parse(cls, obj): 114 | return False 115 | 116 | def _parse(self): 117 | self._parsed = True 118 | 119 | def _get_dict_members(self, cat=None): 120 | """ 121 | For given category a dictionary is returned mapping member names to 122 | members of that category. For names that are duplicated the name is 123 | mapped to None. 124 | """ 125 | self.confirm_no_error() 126 | if cat not in self._dict_members: 127 | new_dict = {} 128 | for mem in self.in_category(cat): 129 | if mem.name() not in new_dict: 130 | new_dict[mem.name()] = mem 131 | else: 132 | new_dict[mem.name()] = self.Duplicate 133 | self._dict_members[cat] = new_dict 134 | return self._dict_members[cat] 135 | 136 | def in_category(self, cat): 137 | self.confirm_no_error() 138 | if cat is None: 139 | return self._members 140 | if cat not in self._in_category: 141 | self._in_category[cat] = [mem for mem in self._members 142 | if cat.includes(mem)] 143 | return self._in_category[cat] 144 | 145 | def get_member(self, name, cat=None): 146 | self.confirm_no_error() 147 | # Check if it's in a namespace or class. 148 | bits = name.split('::') 149 | first = bits[0] 150 | rest = '::'.join(bits[1:]) 151 | member = self._get_dict_members(cat).get(first, self.NoSuchMember) 152 | # Raise any errors that are returned. 153 | if member in set([self.NoSuchMember, self.Duplicate]): 154 | raise member() 155 | if rest: 156 | return member.get_member(rest, cat=cat) 157 | return member 158 | 159 | def has_member(self, name, cat=None): 160 | try: 161 | mem = self.get_member(name, cat=cat) 162 | return True 163 | except self.NoSuchMember: 164 | return False 165 | 166 | def data(self): 167 | self.confirm_no_error() 168 | return self._data 169 | 170 | def members(self): 171 | self.confirm_no_error() 172 | return self._members 173 | 174 | def process_memberdefs(self): 175 | mdtss = [] 176 | for sec in self._retrieved_data.compounddef.sectiondef: 177 | mdtss += sec.memberdef 178 | # At the moment we lose all information associated with sections. 179 | # Sometimes a memberdef is in several sectiondef. 180 | # We make sure we don't get duplicates here. 181 | uniques = set([]) 182 | for mem in mdtss: 183 | converted = self.convert_mem(mem) 184 | pair = (mem.name, mem.__class__) 185 | if pair not in uniques: 186 | uniques.add(pair) 187 | self._members.append(converted) 188 | 189 | def retrieve_data(self): 190 | filename = os.path.join(self._xml_path, self.refid + '.xml') 191 | try: 192 | self._retrieved_data = compound.parse(filename) 193 | except ExpatError: 194 | print('Error in xml in file %s' % filename) 195 | self._error = True 196 | self._retrieved_data = None 197 | 198 | def check_parsed(self): 199 | if not self._parsed: 200 | self._parse() 201 | 202 | def confirm_no_error(self): 203 | self.check_parsed() 204 | if self._error: 205 | raise self.ParsingError() 206 | 207 | def error(self): 208 | self.check_parsed() 209 | return self._error 210 | 211 | def name(self): 212 | # first see if we can do it without processing. 213 | if self._parse_data is not None: 214 | return self._parse_data.name 215 | self.check_parsed() 216 | return self._retrieved_data.compounddef.name 217 | -------------------------------------------------------------------------------- /docs/doxygen/doxyxml/doxyindex.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Copyright (C) 2016 Bastille Networks 3 | 4 | This program 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 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 15 | along with this program. If not, see . 16 | ''' 17 | 18 | """ 19 | Classes providing more user-friendly interfaces to the doxygen xml 20 | docs than the generated classes provide. 21 | """ 22 | 23 | import os 24 | 25 | from generated import index 26 | from base import Base 27 | from text import description 28 | 29 | class DoxyIndex(Base): 30 | """ 31 | Parses a doxygen xml directory. 32 | """ 33 | 34 | __module__ = "gnuradio.utils.doxyxml" 35 | 36 | def _parse(self): 37 | if self._parsed: 38 | return 39 | super(DoxyIndex, self)._parse() 40 | self._root = index.parse(os.path.join(self._xml_path, 'index.xml')) 41 | for mem in self._root.compound: 42 | converted = self.convert_mem(mem) 43 | # For files we want the contents to be accessible directly 44 | # from the parent rather than having to go through the file 45 | # object. 46 | if self.get_cls(mem) == DoxyFile: 47 | if mem.name.endswith('.h'): 48 | self._members += converted.members() 49 | self._members.append(converted) 50 | else: 51 | self._members.append(converted) 52 | 53 | 54 | def generate_swig_doc_i(self): 55 | """ 56 | %feature("docstring") gr_make_align_on_samplenumbers_ss::align_state " 57 | Wraps the C++: gr_align_on_samplenumbers_ss::align_state"; 58 | """ 59 | pass 60 | 61 | 62 | class DoxyCompMem(Base): 63 | 64 | 65 | kind = None 66 | 67 | def __init__(self, *args, **kwargs): 68 | super(DoxyCompMem, self).__init__(*args, **kwargs) 69 | 70 | @classmethod 71 | def can_parse(cls, obj): 72 | return obj.kind == cls.kind 73 | 74 | def set_descriptions(self, parse_data): 75 | bd = description(getattr(parse_data, 'briefdescription', None)) 76 | dd = description(getattr(parse_data, 'detaileddescription', None)) 77 | self._data['brief_description'] = bd 78 | self._data['detailed_description'] = dd 79 | 80 | class DoxyCompound(DoxyCompMem): 81 | pass 82 | 83 | class DoxyMember(DoxyCompMem): 84 | pass 85 | 86 | 87 | class DoxyFunction(DoxyMember): 88 | 89 | __module__ = "gnuradio.utils.doxyxml" 90 | 91 | kind = 'function' 92 | 93 | def _parse(self): 94 | if self._parsed: 95 | return 96 | super(DoxyFunction, self)._parse() 97 | self.set_descriptions(self._parse_data) 98 | self._data['params'] = [] 99 | prms = self._parse_data.param 100 | for prm in prms: 101 | self._data['params'].append(DoxyParam(prm)) 102 | 103 | brief_description = property(lambda self: self.data()['brief_description']) 104 | detailed_description = property(lambda self: self.data()['detailed_description']) 105 | params = property(lambda self: self.data()['params']) 106 | 107 | Base.mem_classes.append(DoxyFunction) 108 | 109 | 110 | class DoxyParam(DoxyMember): 111 | 112 | __module__ = "gnuradio.utils.doxyxml" 113 | 114 | def _parse(self): 115 | if self._parsed: 116 | return 117 | super(DoxyParam, self)._parse() 118 | self.set_descriptions(self._parse_data) 119 | self._data['declname'] = self._parse_data.declname 120 | 121 | brief_description = property(lambda self: self.data()['brief_description']) 122 | detailed_description = property(lambda self: self.data()['detailed_description']) 123 | declname = property(lambda self: self.data()['declname']) 124 | 125 | class DoxyClass(DoxyCompound): 126 | 127 | __module__ = "gnuradio.utils.doxyxml" 128 | 129 | kind = 'class' 130 | 131 | def _parse(self): 132 | if self._parsed: 133 | return 134 | super(DoxyClass, self)._parse() 135 | self.retrieve_data() 136 | if self._error: 137 | return 138 | self.set_descriptions(self._retrieved_data.compounddef) 139 | # Sectiondef.kind tells about whether private or public. 140 | # We just ignore this for now. 141 | self.process_memberdefs() 142 | 143 | brief_description = property(lambda self: self.data()['brief_description']) 144 | detailed_description = property(lambda self: self.data()['detailed_description']) 145 | 146 | Base.mem_classes.append(DoxyClass) 147 | 148 | 149 | class DoxyFile(DoxyCompound): 150 | 151 | __module__ = "gnuradio.utils.doxyxml" 152 | 153 | kind = 'file' 154 | 155 | def _parse(self): 156 | if self._parsed: 157 | return 158 | super(DoxyFile, self)._parse() 159 | self.retrieve_data() 160 | self.set_descriptions(self._retrieved_data.compounddef) 161 | if self._error: 162 | return 163 | self.process_memberdefs() 164 | 165 | brief_description = property(lambda self: self.data()['brief_description']) 166 | detailed_description = property(lambda self: self.data()['detailed_description']) 167 | 168 | Base.mem_classes.append(DoxyFile) 169 | 170 | 171 | class DoxyNamespace(DoxyCompound): 172 | 173 | __module__ = "gnuradio.utils.doxyxml" 174 | 175 | kind = 'namespace' 176 | 177 | Base.mem_classes.append(DoxyNamespace) 178 | 179 | 180 | class DoxyGroup(DoxyCompound): 181 | 182 | __module__ = "gnuradio.utils.doxyxml" 183 | 184 | kind = 'group' 185 | 186 | def _parse(self): 187 | if self._parsed: 188 | return 189 | super(DoxyGroup, self)._parse() 190 | self.retrieve_data() 191 | if self._error: 192 | return 193 | cdef = self._retrieved_data.compounddef 194 | self._data['title'] = description(cdef.title) 195 | # Process inner groups 196 | grps = cdef.innergroup 197 | for grp in grps: 198 | converted = DoxyGroup.from_refid(grp.refid, top=self.top) 199 | self._members.append(converted) 200 | # Process inner classes 201 | klasses = cdef.innerclass 202 | for kls in klasses: 203 | converted = DoxyClass.from_refid(kls.refid, top=self.top) 204 | self._members.append(converted) 205 | # Process normal members 206 | self.process_memberdefs() 207 | 208 | title = property(lambda self: self.data()['title']) 209 | 210 | 211 | Base.mem_classes.append(DoxyGroup) 212 | 213 | 214 | class DoxyFriend(DoxyMember): 215 | 216 | __module__ = "gnuradio.utils.doxyxml" 217 | 218 | kind = 'friend' 219 | 220 | Base.mem_classes.append(DoxyFriend) 221 | 222 | 223 | class DoxyOther(Base): 224 | 225 | __module__ = "gnuradio.utils.doxyxml" 226 | 227 | kinds = set(['variable', 'struct', 'union', 'define', 'typedef', 'enum', 'dir', 'page']) 228 | 229 | @classmethod 230 | def can_parse(cls, obj): 231 | return obj.kind in cls.kinds 232 | 233 | Base.mem_classes.append(DoxyOther) 234 | 235 | -------------------------------------------------------------------------------- /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 (C) 2016 Bastille Networks 3 | 4 | This program 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 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 15 | along with this program. If not, see . 16 | ''' 17 | 18 | """ 19 | Utilities for extracting text from generated classes. 20 | """ 21 | 22 | def is_string(txt): 23 | if isinstance(txt, str): 24 | return True 25 | try: 26 | if isinstance(txt, unicode): 27 | return True 28 | except NameError: 29 | pass 30 | return False 31 | 32 | def description(obj): 33 | if obj is None: 34 | return None 35 | return description_bit(obj).strip() 36 | 37 | def description_bit(obj): 38 | if hasattr(obj, 'content'): 39 | contents = [description_bit(item) for item in obj.content] 40 | result = ''.join(contents) 41 | elif hasattr(obj, 'content_'): 42 | contents = [description_bit(item) for item in obj.content_] 43 | result = ''.join(contents) 44 | elif hasattr(obj, 'value'): 45 | result = description_bit(obj.value) 46 | elif is_string(obj): 47 | return obj 48 | else: 49 | raise StandardError('Expecting a string or something with content, content_ or value attribute') 50 | # If this bit is a paragraph then add one some line breaks. 51 | if hasattr(obj, 'name') and obj.name == 'para': 52 | result += "\n\n" 53 | return result 54 | -------------------------------------------------------------------------------- /docs/doxygen/other/group_defs.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | * \defgroup block GNU Radio NORDIC C++ Signal Processing Blocks 3 | * \brief All C++ blocks that can be used from the NORDIC 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 NORDIC Block 4 | 5 | This is the intro page for the Doxygen manual generated for the NORDIC 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 (C) 2016 Bastille Networks 3 | 4 | This program 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 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 15 | along with this program. If not, see . 16 | ''' 17 | 18 | """ 19 | Creates the swig_doc.i SWIG interface file. 20 | Execute using: python swig_doc.py xml_path outputfilename 21 | 22 | The file instructs SWIG to transfer the doxygen comments into the 23 | python docstrings. 24 | 25 | """ 26 | 27 | import sys 28 | 29 | try: 30 | from doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base 31 | except ImportError: 32 | from gnuradio.doxyxml import DoxyIndex, DoxyClass, DoxyFriend, DoxyFunction, DoxyFile, base 33 | 34 | 35 | def py_name(name): 36 | bits = name.split('_') 37 | return '_'.join(bits[1:]) 38 | 39 | def make_name(name): 40 | bits = name.split('_') 41 | return bits[0] + '_make_' + '_'.join(bits[1:]) 42 | 43 | 44 | class Block(object): 45 | """ 46 | Checks if doxyxml produced objects correspond to a gnuradio block. 47 | """ 48 | 49 | @classmethod 50 | def includes(cls, item): 51 | if not isinstance(item, DoxyClass): 52 | return False 53 | # Check for a parsing error. 54 | if item.error(): 55 | return False 56 | return item.has_member(make_name(item.name()), DoxyFriend) 57 | 58 | 59 | def utoascii(text): 60 | """ 61 | Convert unicode text into ascii and escape quotes. 62 | """ 63 | if text is None: 64 | return '' 65 | out = text.encode('ascii', 'replace') 66 | out = out.replace('"', '\\"') 67 | return out 68 | 69 | 70 | def combine_descriptions(obj): 71 | """ 72 | Combines the brief and detailed descriptions of an object together. 73 | """ 74 | description = [] 75 | bd = obj.brief_description.strip() 76 | dd = obj.detailed_description.strip() 77 | if bd: 78 | description.append(bd) 79 | if dd: 80 | description.append(dd) 81 | return utoascii('\n\n'.join(description)).strip() 82 | 83 | 84 | entry_templ = '%feature("docstring") {name} "{docstring}"' 85 | def make_entry(obj, name=None, templ="{description}", description=None): 86 | """ 87 | Create a docstring entry for a swig interface file. 88 | 89 | obj - a doxyxml object from which documentation will be extracted. 90 | name - the name of the C object (defaults to obj.name()) 91 | templ - an optional template for the docstring containing only one 92 | variable named 'description'. 93 | description - if this optional variable is set then it's value is 94 | used as the description instead of extracting it from obj. 95 | """ 96 | if name is None: 97 | name=obj.name() 98 | if "operator " in name: 99 | return '' 100 | if description is None: 101 | description = combine_descriptions(obj) 102 | docstring = templ.format(description=description) 103 | if not docstring: 104 | return '' 105 | return entry_templ.format( 106 | name=name, 107 | docstring=docstring, 108 | ) 109 | 110 | 111 | def make_func_entry(func, name=None, description=None, params=None): 112 | """ 113 | Create a function docstring entry for a swig interface file. 114 | 115 | func - a doxyxml object from which documentation will be extracted. 116 | name - the name of the C object (defaults to func.name()) 117 | description - if this optional variable is set then it's value is 118 | used as the description instead of extracting it from func. 119 | params - a parameter list that overrides using func.params. 120 | """ 121 | if params is None: 122 | params = func.params 123 | params = [prm.declname for prm in params] 124 | if params: 125 | sig = "Params: (%s)" % ", ".join(params) 126 | else: 127 | sig = "Params: (NONE)" 128 | templ = "{description}\n\n" + sig 129 | return make_entry(func, name=name, templ=utoascii(templ), 130 | description=description) 131 | 132 | 133 | def make_class_entry(klass, description=None): 134 | """ 135 | Create a class docstring for a swig interface file. 136 | """ 137 | output = [] 138 | output.append(make_entry(klass, description=description)) 139 | for func in klass.in_category(DoxyFunction): 140 | name = klass.name() + '::' + func.name() 141 | output.append(make_func_entry(func, name=name)) 142 | return "\n\n".join(output) 143 | 144 | 145 | def make_block_entry(di, block): 146 | """ 147 | Create class and function docstrings of a gnuradio block for a 148 | swig interface file. 149 | """ 150 | descriptions = [] 151 | # Get the documentation associated with the class. 152 | class_desc = combine_descriptions(block) 153 | if class_desc: 154 | descriptions.append(class_desc) 155 | # Get the documentation associated with the make function 156 | make_func = di.get_member(make_name(block.name()), DoxyFunction) 157 | make_func_desc = combine_descriptions(make_func) 158 | if make_func_desc: 159 | descriptions.append(make_func_desc) 160 | # Get the documentation associated with the file 161 | try: 162 | block_file = di.get_member(block.name() + ".h", DoxyFile) 163 | file_desc = combine_descriptions(block_file) 164 | if file_desc: 165 | descriptions.append(file_desc) 166 | except base.Base.NoSuchMember: 167 | # Don't worry if we can't find a matching file. 168 | pass 169 | # And join them all together to make a super duper description. 170 | super_description = "\n\n".join(descriptions) 171 | # Associate the combined description with the class and 172 | # the make function. 173 | output = [] 174 | output.append(make_class_entry(block, description=super_description)) 175 | creator = block.get_member(block.name(), DoxyFunction) 176 | output.append(make_func_entry(make_func, description=super_description, 177 | params=creator.params)) 178 | return "\n\n".join(output) 179 | 180 | 181 | def make_swig_interface_file(di, swigdocfilename, custom_output=None): 182 | 183 | output = [""" 184 | /* 185 | * This file was automatically generated using swig_doc.py. 186 | * 187 | * Any changes to it will be lost next time it is regenerated. 188 | */ 189 | """] 190 | 191 | if custom_output is not None: 192 | output.append(custom_output) 193 | 194 | # Create docstrings for the blocks. 195 | blocks = di.in_category(Block) 196 | make_funcs = set([]) 197 | for block in blocks: 198 | try: 199 | make_func = di.get_member(make_name(block.name()), DoxyFunction) 200 | make_funcs.add(make_func.name()) 201 | output.append(make_block_entry(di, block)) 202 | except block.ParsingError: 203 | print('Parsing error for block %s' % block.name()) 204 | 205 | # Create docstrings for functions 206 | # Don't include the make functions since they have already been dealt with. 207 | funcs = [f for f in di.in_category(DoxyFunction) if f.name() not in make_funcs] 208 | for f in funcs: 209 | try: 210 | output.append(make_func_entry(f)) 211 | except f.ParsingError: 212 | print('Parsing error for function %s' % f.name()) 213 | 214 | # Create docstrings for classes 215 | block_names = [block.name() for block in blocks] 216 | klasses = [k for k in di.in_category(DoxyClass) if k.name() not in block_names] 217 | for k in klasses: 218 | try: 219 | output.append(make_class_entry(k)) 220 | except k.ParsingError: 221 | print('Parsing error for class %s' % k.name()) 222 | 223 | # Docstrings are not created for anything that is not a function or a class. 224 | # If this excludes anything important please add it here. 225 | 226 | output = "\n\n".join(output) 227 | 228 | swig_doc = file(swigdocfilename, 'w') 229 | swig_doc.write(output) 230 | swig_doc.close() 231 | 232 | if __name__ == "__main__": 233 | # Parse command line options and set up doxyxml. 234 | err_msg = "Execute using: python swig_doc.py xml_path outputfilename" 235 | if len(sys.argv) != 3: 236 | raise StandardError(err_msg) 237 | xml_path = sys.argv[1] 238 | swigdocfilename = sys.argv[2] 239 | di = DoxyIndex(xml_path) 240 | 241 | # gnuradio.gr.msq_queue.insert_tail and delete_head create errors unless docstrings are defined! 242 | # This is presumably a bug in SWIG. 243 | #msg_q = di.get_member(u'gr_msg_queue', DoxyClass) 244 | #insert_tail = msg_q.get_member(u'insert_tail', DoxyFunction) 245 | #delete_head = msg_q.get_member(u'delete_head', DoxyFunction) 246 | output = [] 247 | #output.append(make_func_entry(insert_tail, name='gr_py_msg_queue__insert_tail')) 248 | #output.append(make_func_entry(delete_head, name='gr_py_msg_queue__delete_head')) 249 | custom_output = "\n\n".join(output) 250 | 251 | # Generate the docstrings interface file. 252 | make_swig_interface_file(di, swigdocfilename, custom_output=custom_output) 253 | -------------------------------------------------------------------------------- /examples/microsoft_mouse_sniffer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from gnuradio import gr, blocks, digital, filter 4 | from gnuradio.filter import firdes 5 | import thread 6 | import osmosdr 7 | import nordic 8 | import pmt 9 | import struct 10 | import time 11 | 12 | 13 | class top_block(gr.top_block): 14 | 15 | def __init__(self): 16 | gr.top_block.__init__(self, "Microsoft Mouse Sniffer") 17 | 18 | # SDR configuration 19 | self.freq = 2403e6 20 | self.gain = 70 21 | self.symbol_rate = 2e6 22 | self.sample_rate = 4e6 23 | 24 | # SDR source (gr-osmosdr source) 25 | self.osmosdr_source = osmosdr.source() 26 | self.osmosdr_source.set_sample_rate(self.sample_rate) 27 | self.osmosdr_source.set_center_freq(self.freq) 28 | self.osmosdr_source.set_gain(self.gain) 29 | self.osmosdr_source.set_antenna('TX/RX') 30 | 31 | # Low pass filter 32 | self.lpf = filter.fir_filter_ccf( 33 | 1, firdes.low_pass_2(1, self.sample_rate, self.symbol_rate / 2, 50e3, 50)) 34 | 35 | # GFSK demod, defaults to 2 samples per symbol 36 | self.gfsk_demod = digital.gfsk_demod() 37 | 38 | # Nordic RX 39 | self.nordic_rx = nordic.nordic_rx(3, 5, 2, 2) 40 | 41 | # Connect the blocks 42 | self.connect((self.osmosdr_source, 0), (self.lpf, 0)) 43 | self.connect((self.lpf, 0), (self.gfsk_demod, 0)) 44 | self.connect((self.gfsk_demod, 0), (self.nordic_rx, 0)) 45 | 46 | # Handle incoming packets 47 | self.nordictap_handler = microsoft_nordictap_handler(self) 48 | self.msg_connect( 49 | self.nordic_rx, "nordictap_out", self.nordictap_handler, "nordictap_in") 50 | 51 | # Tune the USRP by nRF24L channel number 52 | def set_channel(self, channel): 53 | 54 | new_channel = 2400e6 + channel * 1e6 55 | self.osmosdr_source.set_center_freq(2400e6 + channel * 1e6) 56 | self.nordic_rx.set_channel(channel) 57 | 58 | 59 | # Microsoft mouse nordictap handler 60 | class microsoft_nordictap_handler(gr.sync_block): 61 | 62 | def __init__(self, tb): 63 | gr.sync_block.__init__( 64 | self, name="Nordictap Handler", in_sig=None, out_sig=None) 65 | 66 | self.tb = tb 67 | self.message_port_register_in(pmt.intern("nordictap_in")) 68 | self.set_msg_handler( 69 | pmt.intern("nordictap_in"), self.nordictap_handler) 70 | 71 | # Tick / channel hopping state and logic 72 | self.last_rx = time.time() 73 | self.last_tune = time.time() 74 | self.ch_timeout = 0.4 # timeout a channel after 200ms 75 | self.last_ch = 0 76 | thread.start_new_thread(self.tick, ()) 77 | 78 | # Channels and channel groups 79 | self.channels = [3, 29, 21, 5, 23, 17, 19, 50, 31, 25, 80 | 46, 27, 78, 70, 72, 44, 56, 48, 68, 80, 54, 52, 74, 76] 81 | self.channel_groups = [] 82 | for x in range(6): 83 | chs = [] 84 | for y in range(4): 85 | chs.append(self.channels[y * 6 + x]) 86 | self.channel_groups.append(chs) 87 | 88 | # Discovered device state 89 | self.mouse_address = None 90 | 91 | # 10ms tick 92 | def tick(self): 93 | 94 | while True: 95 | 96 | # Check for a stale channel 97 | if ((time.time() - self.last_rx) > self.ch_timeout * 5) and \ 98 | ((time.time() - self.last_tune) > self.ch_timeout): 99 | 100 | self.last_ch += 1 101 | if self.last_ch >= len(self.channels): 102 | self.last_ch = 0 103 | print 'Tuning to 24%02i MHz' % self.channels[self.last_ch] 104 | self.last_tune = time.time() 105 | self.tb.set_channel(self.channels[self.last_ch]) 106 | 107 | # Wait 10ms 108 | time.sleep(0.01) 109 | 110 | def nordictap_handler(self, msg): 111 | 112 | data = pmt.to_python(msg).tostring() 113 | 114 | # Unpack the header 115 | values = struct.unpack('BBBBBBB', data[0:7]) 116 | channel = values[0] 117 | data_rate = values[1] 118 | address_length = values[2] 119 | payload_length = values[3] 120 | sequence_number = values[4] 121 | no_ack = values[5] 122 | crc_length = values[6] 123 | 124 | # Parse the address, payload, and crc 125 | address = data[7:7 + address_length] 126 | payload = data[7 + address_length:7 + address_length + payload_length] 127 | crc = data[7 + address_length + payload_length: 128 | 7 + address_length + payload_length + crc_length] 129 | 130 | # Check for a Microsoft mouse 131 | if self.mouse_address is None: 132 | if ((ord(address[0]) & 0xF0) == 0xA0) and \ 133 | ((ord(address[4]) & 0x0F) == 0x06) and \ 134 | payload_length == 19: 135 | 136 | # Set the mouse address 137 | self.mouse_address = address 138 | 139 | # Set the channel group 140 | for x in range(6): 141 | if channel in self.channel_groups[x]: 142 | self.channels = self.channel_groups[x] 143 | break 144 | 145 | # Camp on the channel and print out the packet if this is our target 146 | # device 147 | if self.mouse_address == address: 148 | 149 | self.last_rx = time.time() 150 | 151 | # Print the channel, sequence number, address and payload 152 | print 'CH=' + str(2400 + channel), 153 | print 'SEQ=' + str(sequence_number), 154 | print 'ADDR=' + ':'.join('%02X' % ord(b) for b in address), 155 | print 'PLD=' + ':'.join('%02X' % ord(b) for b in payload), 156 | print 'CRC=' + ':'.join('%02X' % ord(b) for b in crc) 157 | 158 | 159 | def main(): 160 | tb = top_block() 161 | tb.start() 162 | try: 163 | raw_input('Press Enter to quit: ') 164 | except EOFError: 165 | pass 166 | tb.stop() 167 | tb.wait() 168 | 169 | 170 | if __name__ == '__main__': 171 | main() 172 | -------------------------------------------------------------------------------- /examples/nordic_auto_ack.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from gnuradio import gr, blocks, digital, filter 4 | from gnuradio.filter import firdes 5 | import thread 6 | import nordic 7 | import pmt 8 | import struct 9 | import time 10 | import numpy 11 | import array 12 | import osmosdr 13 | import argparse 14 | from bitstring import BitArray 15 | from gnuradio import uhd 16 | from Queue import Queue 17 | 18 | 19 | class top_block(gr.top_block): 20 | 21 | def __init__(self, args): 22 | gr.top_block.__init__(self, "Nordic Auto-ACK Example") 23 | 24 | # SDR configuration 25 | self.freq = 2400e6 + args.channel * 1e6 26 | self.gain = args.gain 27 | self.symbol_rate = args.data_rate 28 | self.sample_rate = args.data_rate * args.samples_per_symbol 29 | 30 | # SDR source (gr-osmosdr source) 31 | self.osmosdr_source = osmosdr.source() 32 | self.osmosdr_source.set_sample_rate(self.sample_rate) 33 | self.osmosdr_source.set_center_freq(self.freq) 34 | self.osmosdr_source.set_gain(self.gain) 35 | self.osmosdr_source.set_antenna('TX/RX') 36 | 37 | # SDR sink (gr-osmosdr source) 38 | self.osmosdr_sink = osmosdr.sink() 39 | self.osmosdr_sink.set_sample_rate(self.sample_rate) 40 | self.osmosdr_sink.set_center_freq(self.freq) 41 | self.osmosdr_sink.set_gain(self.gain) 42 | self.osmosdr_sink.set_antenna('TX/RX') 43 | 44 | # Transmit chain 45 | self.tx = nordic.nordic_tx() 46 | self.gfsk_mod = digital.gfsk_mod( 47 | samples_per_symbol=args.samples_per_symbol) 48 | self.connect(self.tx, self.gfsk_mod) 49 | self.connect(self.gfsk_mod, self.osmosdr_sink) 50 | 51 | # Receive chain 52 | dr = 0 53 | if args.data_rate == 1e6: 54 | dr = 1 55 | elif args.data_rate == 2e6: 56 | dr = 2 57 | self.rx = nordic.nordic_rx( 58 | args.channel, args.address_length, args.crc_length, dr) 59 | self.gfsk_demod = digital.gfsk_demod( 60 | samples_per_symbol=args.samples_per_symbol) 61 | self.lpf = filter.fir_filter_ccf( 62 | 1, firdes.low_pass_2(1, self.sample_rate, self.symbol_rate / 2, 50e3, 50)) 63 | self.connect(self.osmosdr_source, self.lpf) 64 | self.connect(self.lpf, self.gfsk_demod) 65 | self.connect(self.gfsk_demod, self.rx) 66 | 67 | # Handle incoming packets 68 | self.nordictap_ack_handler = nordictap_ack_handler() 69 | self.msg_connect(self.rx, "nordictap_out", 70 | self.nordictap_ack_handler, "nordictap_in") 71 | 72 | # Reply with ACKs 73 | self.msg_connect(self.nordictap_ack_handler, 74 | "nordictap_out", self.tx, "nordictap_in") 75 | 76 | 77 | # Nordic Auto-ACK handler 78 | class nordictap_ack_handler(gr.sync_block): 79 | 80 | # Constructor 81 | 82 | def __init__(self): 83 | gr.sync_block.__init__( 84 | self, name="Nordictap Handler", in_sig=None, out_sig=None) 85 | 86 | # Received packet input port 87 | self.message_port_register_in(pmt.intern("nordictap_in")) 88 | self.set_msg_handler( 89 | pmt.intern("nordictap_in"), self.nordictap_handler) 90 | 91 | # ACK output port 92 | self.message_port_register_out(pmt.intern("nordictap_out")) 93 | 94 | # Handle incoming packets, and reply with ACKs 95 | def nordictap_handler(self, msg): 96 | 97 | # PMT to byte string 98 | data = pmt.to_python(msg).tostring() 99 | 100 | # Unpack the header 101 | values = struct.unpack('BBBBBBBB', data[0:8]) 102 | channel = values[0] 103 | data_rate = values[1] 104 | address_length = values[2] 105 | payload_length = values[3] 106 | sequence_number = values[4] 107 | no_ack = values[5] 108 | crc_length = values[6] 109 | 110 | # Parse the address, payload, and crc 111 | address = data[7:7 + address_length] 112 | payload = data[7 + address_length:7 + address_length + payload_length] 113 | crc = data[7 + address_length + payload_length: 114 | 7 + address_length + payload_length + crc_length] 115 | 116 | # ACK if needed 117 | if payload_length > 0 and no_ack == 0: 118 | 119 | # Print the channel, sequence number, address and payload 120 | print "ACK'd Packet: ", 121 | print 'CH=' + str(2400 + channel), 122 | print 'SEQ=' + str(sequence_number), 123 | print 'ADDR=' + ':'.join('%02X' % ord(b) for b in address), 124 | print 'PLD=' + ':'.join('%02X' % ord(b) for b in payload), 125 | print 'CRC=' + ':'.join('%02X' % ord(b) for b in crc) 126 | 127 | # Build an ACK 128 | nordictap = [0] + [4, 2, 5, 0, sequence_number, 0, 2] 129 | for c in address: 130 | nordictap.append(ord(c)) 131 | 132 | # Transmit an ACK 133 | vec = pmt.make_u8vector(len(nordictap), 0) 134 | for x in range(len(nordictap)): 135 | pmt.u8vector_set(vec, x, nordictap[x]) 136 | self.message_port_pub(pmt.intern("nordictap_out"), vec) 137 | 138 | 139 | def main(): 140 | 141 | # Parse command line arguments 142 | parser = argparse.ArgumentParser('Nordic Auto-ACK Example', 143 | formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=50, width=120)) 144 | parser.add_argument( 145 | '-c', '--channel', type=int, help='RF channel (0-125)', default=4) 146 | parser.add_argument('-r', '--data_rate', type=float, 147 | help='Data Rate (250e3, 1e6 or 2e6', default=2e6, choices=[250e3, 1e6, 2e6]) 148 | parser.add_argument('-l', '--crc_length', type=int, 149 | help='CRC Length (1-2)', default=2, choices=[1, 2]) 150 | parser.add_argument('-a', '--address_length', type=int, 151 | help='Address Length (3-5)', default=5, choices=[3, 4, 5]) 152 | parser.add_argument('-s', '--samples_per_symbol', 153 | type=int, help='Samples Per Symbol', default=2) 154 | parser.add_argument( 155 | '-g', '--gain', type=float, help='Radio Gain', default=80) 156 | 157 | args = parser.parse_args() 158 | 159 | tb = top_block(args) 160 | tb.start() 161 | try: 162 | raw_input('Press Enter to quit: ') 163 | except EOFError: 164 | pass 165 | tb.stop() 166 | tb.wait() 167 | 168 | 169 | if __name__ == '__main__': 170 | main() 171 | -------------------------------------------------------------------------------- /examples/nordic_channelized_receiver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from gnuradio import gr, blocks, digital, filter 4 | from gnuradio.filter import firdes 5 | import thread 6 | import nordic 7 | import pmt 8 | import struct 9 | import time 10 | import numpy 11 | import array 12 | import osmosdr 13 | import argparse 14 | from bitstring import BitArray 15 | from gnuradio import uhd 16 | from Queue import Queue 17 | 18 | 19 | class top_block(gr.top_block): 20 | 21 | def __init__(self, args): 22 | gr.top_block.__init__(self, "Nordic Single-Channel Receiver Example") 23 | 24 | self.freq = 2414e6 25 | self.gain = args.gain 26 | self.symbol_rate = 2e6 27 | self.sample_rate = 4e6 28 | 29 | # Channel map 30 | channel_count = 3 31 | channel_map = [14, 18, 10] 32 | 33 | # Data rate index 34 | dr = 2 # 2M 35 | 36 | # SDR source (gr-osmosdr source) 37 | self.osmosdr_source = osmosdr.source() 38 | self.osmosdr_source.set_sample_rate(self.sample_rate * channel_count) 39 | self.osmosdr_source.set_center_freq(self.freq) 40 | self.osmosdr_source.set_gain(self.gain) 41 | self.osmosdr_source.set_antenna('TX/RX') 42 | 43 | # PFB channelizer 44 | taps = firdes.low_pass_2( 45 | 1, self.sample_rate, self.symbol_rate / 2, 100e3, 30) 46 | self.channelizer = filter.pfb_channelizer_ccf(channel_count, taps, 1) 47 | 48 | # Stream to streams (PFB channelizer input) 49 | self.s2ss = blocks.stream_to_streams( 50 | gr.sizeof_gr_complex, channel_count) 51 | self.connect(self.osmosdr_source, self.s2ss) 52 | 53 | # Demodulators and packet deframers 54 | self.nordictap_printer = nordictap_printer() 55 | self.demods = [] 56 | self.rxs = [] 57 | for x in range(channel_count): 58 | self.connect((self.s2ss, x), (self.channelizer, x)) 59 | self.demods.append(digital.gfsk_demod()) 60 | self.rxs.append(nordic.nordic_rx(x, 5, 2, dr)) 61 | self.connect((self.channelizer, x), self.demods[x]) 62 | self.connect(self.demods[x], self.rxs[x]) 63 | self.msg_connect( 64 | self.rxs[x], "nordictap_out", self.nordictap_printer, "nordictap_in") 65 | 66 | 67 | # Nordic Printer 68 | class nordictap_printer(gr.sync_block): 69 | 70 | # Constructor 71 | 72 | def __init__(self): 73 | gr.sync_block.__init__( 74 | self, name="Nordictap Printer", in_sig=None, out_sig=None) 75 | 76 | # Received packet input port 77 | self.message_port_register_in(pmt.intern("nordictap_in")) 78 | self.set_msg_handler( 79 | pmt.intern("nordictap_in"), self.nordictap_handler) 80 | 81 | # Handle incoming packets, and print payloads 82 | def nordictap_handler(self, msg): 83 | 84 | # PMT to byte string 85 | data = pmt.to_python(msg).tostring() 86 | 87 | # Unpack the header 88 | values = struct.unpack('BBBBBBBB', data[0:8]) 89 | channel = values[0] 90 | data_rate = values[1] 91 | address_length = values[2] 92 | payload_length = values[3] 93 | sequence_number = values[4] 94 | no_ack = values[5] 95 | crc_length = values[6] 96 | 97 | # Parse the address, payload, and crc 98 | address = data[7:7 + address_length] 99 | payload = data[7 + address_length:7 + address_length + payload_length] 100 | crc = data[7 + address_length + payload_length: 101 | 7 + address_length + payload_length + crc_length] 102 | 103 | # Print the channel, sequence number, address and payload 104 | print 'CH=' + str(2400 + channel), 105 | print 'SEQ=' + str(sequence_number), 106 | print 'ADDR=' + ':'.join('%02X' % ord(b) for b in address), 107 | print 'PLD=' + ':'.join('%02X' % ord(b) for b in payload), 108 | print 'CRC=' + ':'.join('%02X' % ord(b) for b in crc) 109 | 110 | 111 | def main(): 112 | 113 | # Parse command line arguments 114 | parser = argparse.ArgumentParser('Nordic Channelized Receiver Example', 115 | formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=50, width=120)) 116 | parser.add_argument( 117 | '-g', '--gain', type=float, help='Radio Gain', default=30) 118 | 119 | args = parser.parse_args() 120 | 121 | tb = top_block(args) 122 | tb.start() 123 | try: 124 | raw_input('Press Enter to quit: ') 125 | except EOFError: 126 | pass 127 | tb.stop() 128 | tb.wait() 129 | 130 | 131 | if __name__ == '__main__': 132 | main() 133 | -------------------------------------------------------------------------------- /examples/nordic_channelized_transmitter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from gnuradio import gr, blocks, digital, filter 4 | from gnuradio.filter import firdes 5 | import thread 6 | import nordic 7 | import pmt 8 | import struct 9 | import time 10 | import numpy 11 | import array 12 | import random 13 | import osmosdr 14 | import argparse 15 | from bitstring import BitArray 16 | from gnuradio import uhd 17 | from Queue import Queue 18 | 19 | 20 | class top_block(gr.top_block): 21 | 22 | def __init__(self, args): 23 | gr.top_block.__init__(self, "Nordic Single-Channel Receiver Example") 24 | 25 | self.freq = 2414e6 26 | self.gain = args.gain 27 | self.symbol_rate = 2e6 28 | self.sample_rate = 4e6 29 | 30 | # Channel map 31 | channel_count = 3 32 | channel_map = [14, 18, 10] 33 | 34 | # Data rate index 35 | dr = 2 # 2M 36 | 37 | # SDR sink (gr-osmosdr sink) 38 | self.osmosdr_sink = osmosdr.sink() 39 | self.osmosdr_sink.set_sample_rate(self.sample_rate * channel_count) 40 | self.osmosdr_sink.set_center_freq(self.freq) 41 | self.osmosdr_sink.set_gain(self.gain) 42 | self.osmosdr_sink.set_antenna('TX/RX') 43 | 44 | # PFB channelizer 45 | taps = firdes.low_pass_2( 46 | 1, self.sample_rate, self.symbol_rate / 2, 100e3, 30) 47 | self.synthesizer = filter.pfb_synthesizer_ccf(channel_count, taps) 48 | 49 | # Modulators and packet framers 50 | self.nordictap_transmitter = nordictap_transmitter(channel_map) 51 | self.mods = [] 52 | self.tx = nordic.nordic_tx(channel_count) 53 | for x in range(channel_count): 54 | self.mods.append(digital.gfsk_mod()) 55 | self.connect((self.tx, x), self.mods[x]) 56 | self.connect(self.mods[x], (self.synthesizer, x)) 57 | self.connect(self.synthesizer, self.osmosdr_sink) 58 | 59 | # Wire up output packet connection 60 | self.msg_connect(self.nordictap_transmitter, 61 | "nordictap_out", self.tx, "nordictap_in") 62 | 63 | 64 | # Nordic transmitter strobe 65 | class nordictap_transmitter(gr.sync_block): 66 | 67 | # Constructor 68 | 69 | def __init__(self, channel_map): 70 | gr.sync_block.__init__( 71 | self, name="Nordictap Printer/Transmitter", in_sig=None, out_sig=None) 72 | 73 | self.channel_map = channel_map 74 | 75 | # Packet output port 76 | self.message_port_register_out(pmt.intern("nordictap_out")) 77 | 78 | # Transmit a packet 79 | def transmit(self, address, payload, channel_index, sequence_number): 80 | 81 | channel = self.channel_map[channel_index] 82 | 83 | # Build a payload 84 | nordictap = [channel_index] + [ 85 | channel, 2, len(address), len(payload), sequence_number, 0, 2] 86 | for c in address: 87 | nordictap.append(ord(c)) 88 | for c in payload: 89 | nordictap.append(ord(c)) 90 | 91 | # Transmit packet 92 | vec = pmt.make_u8vector(len(nordictap), 0) 93 | for x in range(len(nordictap)): 94 | pmt.u8vector_set(vec, x, nordictap[x]) 95 | self.message_port_pub(pmt.intern("nordictap_out"), vec) 96 | 97 | 98 | def main(): 99 | 100 | # Parse command line arguments 101 | parser = argparse.ArgumentParser('Nordic Channelized Transmitter Example', 102 | formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=50, width=120)) 103 | parser.add_argument( 104 | '-g', '--gain', type=float, help='Radio Gain', default=30) 105 | 106 | args = parser.parse_args() 107 | 108 | tb = top_block(args) 109 | tb.start() 110 | 111 | # Transmit some packets, hopping between three channels 112 | address = '\x11\x22\x11\x22\x11' 113 | payload = '\x55\x44\x33\x22\x11' 114 | sequence_number = 0 115 | while True: 116 | for x in range(3): 117 | tb.nordictap_transmitter.transmit( 118 | address, payload, x, sequence_number) 119 | sequence_number += 1 120 | if sequence_number > 3: 121 | sequence_number = 0 122 | time.sleep(0.1) 123 | 124 | try: 125 | raw_input('Press Enter to quit: ') 126 | except EOFError: 127 | pass 128 | tb.stop() 129 | tb.wait() 130 | 131 | 132 | if __name__ == '__main__': 133 | main() 134 | -------------------------------------------------------------------------------- /examples/nordic_receiver.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from gnuradio import gr, blocks, digital, filter 4 | from gnuradio.filter import firdes 5 | import thread 6 | import nordic 7 | import pmt 8 | import struct 9 | import time 10 | import numpy 11 | import array 12 | import osmosdr 13 | import argparse 14 | from bitstring import BitArray 15 | from gnuradio import uhd 16 | from Queue import Queue 17 | 18 | 19 | class top_block(gr.top_block): 20 | 21 | def __init__(self, args): 22 | gr.top_block.__init__(self, "Nordic Single-Channel Receiver Example") 23 | 24 | # SDR configuration 25 | self.freq = 2400e6 + args.channel * 1e6 26 | self.gain = args.gain 27 | self.symbol_rate = args.data_rate 28 | self.sample_rate = args.data_rate * args.samples_per_symbol 29 | 30 | # SDR source (gr-osmosdr source)_tx_queue.push(msg); 31 | self.osmosdr_source = osmosdr.source() 32 | self.osmosdr_source.set_sample_rate(self.sample_rate) 33 | self.osmosdr_source.set_center_freq(self.freq) 34 | self.osmosdr_source.set_gain(self.gain) 35 | self.osmosdr_source.set_antenna('TX/RX') 36 | 37 | # Receive chain 38 | dr = 0 39 | if args.data_rate == 1e6: 40 | dr = 1 41 | elif args.data_rate == 2e6: 42 | dr = 2 43 | self.rx = nordic.nordic_rx( 44 | args.channel, args.address_length, args.crc_length, dr) 45 | self.gfsk_demod = digital.gfsk_demod( 46 | samples_per_symbol=args.samples_per_symbol) 47 | self.lpf = filter.fir_filter_ccf( 48 | 1, firdes.low_pass_2(1, self.sample_rate, self.symbol_rate / 2, 50e3, 50)) 49 | self.connect(self.osmosdr_source, self.lpf) 50 | self.connect(self.lpf, self.gfsk_demod) 51 | self.connect(self.gfsk_demod, self.rx) 52 | 53 | # Handle incoming packets 54 | self.nordictap_printer = nordictap_printer() 55 | self.msg_connect( 56 | self.rx, "nordictap_out", self.nordictap_printer, "nordictap_in") 57 | 58 | 59 | # Nordic Printer 60 | class nordictap_printer(gr.sync_block): 61 | 62 | # Constructor 63 | 64 | def __init__(self): 65 | gr.sync_block.__init__( 66 | self, name="Nordictap Handler", in_sig=None, out_sig=None) 67 | 68 | # Received packet input port 69 | self.message_port_register_in(pmt.intern("nordictap_in")) 70 | self.set_msg_handler( 71 | pmt.intern("nordictap_in"), self.nordictap_handler) 72 | 73 | # Handle incoming packets, and print payloads 74 | def nordictap_handler(self, msg): 75 | 76 | # PMT to byte string 77 | data = pmt.to_python(msg).tostring() 78 | 79 | # Unpack the header 80 | values = struct.unpack('BBBBBBBB', data[0:8]) 81 | channel = values[0] 82 | data_rate = values[1] 83 | address_length = values[2] 84 | payload_length = values[3] 85 | sequence_number = values[4] 86 | no_ack = values[5] 87 | crc_length = values[6] 88 | 89 | # Parse the address, payload, and crc 90 | address = data[7:7 + address_length] 91 | payload = data[7 + address_length:7 + address_length + payload_length] 92 | crc = data[7 + address_length + payload_length: 93 | 7 + address_length + payload_length + crc_length] 94 | 95 | # Print the channel, sequence number, address and payload 96 | print 'CH=' + str(2400 + channel), 97 | print 'SEQ=' + str(sequence_number), 98 | print 'ADDR=' + ':'.join('%02X' % ord(b) for b in address), 99 | print 'PLD=' + ':'.join('%02X' % ord(b) for b in payload), 100 | print 'CRC=' + ':'.join('%02X' % ord(b) for b in crc) 101 | 102 | 103 | def main(): 104 | 105 | # Parse command line arguments 106 | parser = argparse.ArgumentParser('Nordic Single-Channel Receiver Example', 107 | formatter_class=lambda prog: argparse.HelpFormatter(prog, max_help_position=50, width=120)) 108 | parser.add_argument( 109 | '-c', '--channel', type=int, help='RF channel (0-125)', default=4) 110 | parser.add_argument('-r', '--data_rate', type=float, 111 | help='Data Rate (250e3, 1e6 or 2e6', default=2e6, choices=[250e3, 1e6, 2e6]) 112 | parser.add_argument('-l', '--crc_length', type=int, 113 | help='CRC Length (1-2)', default=2, choices=[1, 2]) 114 | parser.add_argument('-a', '--address_length', type=int, 115 | help='Address Length (3-5)', default=5, choices=[3, 4, 5]) 116 | parser.add_argument('-s', '--samples_per_symbol', 117 | type=int, help='Samples Per Symbol', default=2) 118 | parser.add_argument( 119 | '-g', '--gain', type=float, help='Radio Gain', default=80) 120 | 121 | args = parser.parse_args() 122 | 123 | tb = top_block(args) 124 | tb.start() 125 | try: 126 | raw_input('Press Enter to quit: ') 127 | except EOFError: 128 | pass 129 | tb.stop() 130 | tb.wait() 131 | 132 | 133 | if __name__ == '__main__': 134 | main() 135 | -------------------------------------------------------------------------------- /examples/nordic_sniffer_scanner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from gnuradio import gr, blocks, digital, filter 4 | from gnuradio.filter import firdes 5 | import thread 6 | import osmosdr 7 | import nordic 8 | import pmt 9 | import struct 10 | import time 11 | 12 | 13 | class top_block(gr.top_block): 14 | 15 | def __init__(self): 16 | gr.top_block.__init__(self, "AirHogs Sync Framer Example") 17 | 18 | # SDR configuration 19 | self.freq = 2402e6 20 | self.gain = 70 21 | self.symbol_rate = 2e6 22 | self.sample_rate = 4e6 23 | 24 | # SDR source (gr-osmosdr source) 25 | self.osmosdr_source = osmosdr.source() 26 | self.osmosdr_source.set_sample_rate(self.sample_rate) 27 | self.osmosdr_source.set_center_freq(self.freq) 28 | self.osmosdr_source.set_gain(self.gain) 29 | self.osmosdr_source.set_antenna('TX/RX') 30 | 31 | # Low pass filter 32 | self.lpf = filter.fir_filter_ccf( 33 | 1, firdes.low_pass_2(1, self.sample_rate, self.symbol_rate / 2, 50e3, 50)) 34 | 35 | # GFSK demod, defaults to 2 samples per symbol 36 | self.gfsk_demod = digital.gfsk_demod() 37 | 38 | # Nordic RX 39 | self.nordic_rx = nordic.nordic_rx(3, 5, 2, 2) 40 | 41 | # Connect the blocks 42 | self.connect((self.osmosdr_source, 0), (self.lpf, 0)) 43 | self.connect((self.lpf, 0), (self.gfsk_demod, 0)) 44 | self.connect((self.gfsk_demod, 0), (self.nordic_rx, 0)) 45 | 46 | # Handle incoming packets 47 | self.nordictap_handler = microsoft_nordictap_handler(self) 48 | self.msg_connect( 49 | self.nordic_rx, "nordictap_out", self.nordictap_handler, "nordictap_in") 50 | 51 | # Tune the USRP by nRF24L channel number 52 | def set_channel(self, channel): 53 | 54 | new_channel = 2400e6 + channel * 1e6 55 | self.osmosdr_source.set_center_freq(2400e6 + channel * 1e6) 56 | self.nordic_rx.set_channel(channel) 57 | 58 | 59 | # Microsoft mouse nordictap handler 60 | class microsoft_nordictap_handler(gr.sync_block): 61 | 62 | def __init__(self, tb): 63 | gr.sync_block.__init__( 64 | self, name="Nordictap Handler", in_sig=None, out_sig=None) 65 | 66 | self.tb = tb 67 | self.message_port_register_in(pmt.intern("nordictap_in")) 68 | self.set_msg_handler( 69 | pmt.intern("nordictap_in"), self.nordictap_handler) 70 | 71 | # Tick / channel hopping state and logic 72 | self.last_rx = time.time() 73 | self.last_tune = time.time() 74 | self.ch_timeout = 0.4 # timeout a channel after 200ms 75 | self.last_ch = 0 76 | thread.start_new_thread(self.tick, ()) 77 | 78 | # Channels and channel groups 79 | self.channels = range(2, 84) 80 | 81 | # 10ms tick 82 | def tick(self): 83 | 84 | while True: 85 | 86 | # Check for a stale channel 87 | if ((time.time() - self.last_rx) > self.ch_timeout * 5) and \ 88 | ((time.time() - self.last_tune) > self.ch_timeout): 89 | 90 | self.last_ch += 1 91 | if self.last_ch >= len(self.channels): 92 | self.last_ch = 0 93 | print 'Tuning to 24%02i MHz' % self.channels[self.last_ch] 94 | self.last_tune = time.time() 95 | self.tb.set_channel(self.channels[self.last_ch]) 96 | 97 | # Wait 10ms 98 | time.sleep(0.01) 99 | 100 | def nordictap_handler(self, msg): 101 | 102 | data = pmt.to_python(msg).tostring() 103 | 104 | # Unpack the header 105 | values = struct.unpack('BBBBBBB', data[0:7]) 106 | channel = values[0] 107 | data_rate = values[1] 108 | address_length = values[2] 109 | payload_length = values[3] 110 | sequence_number = values[4] 111 | no_ack = values[5] 112 | crc_length = values[6] 113 | 114 | # Parse the address, payload, and crc 115 | address = data[7:7 + address_length] 116 | payload = data[7 + address_length:7 + address_length + payload_length] 117 | crc = data[7 + address_length + payload_length: 118 | 7 + address_length + payload_length + crc_length] 119 | 120 | self.last_rx = time.time() 121 | 122 | # Print the channel, sequence number, address and payload 123 | print 'CH=' + str(2400 + channel), 124 | print 'SEQ=' + str(sequence_number), 125 | print 'ADDR=' + ':'.join('%02X' % ord(b) for b in address), 126 | print 'PLD=' + ':'.join('%02X' % ord(b) for b in payload), 127 | print 'CRC=' + ':'.join('%02X' % ord(b) for b in crc) 128 | 129 | 130 | def main(): 131 | tb = top_block() 132 | tb.start() 133 | try: 134 | raw_input('Press Enter to quit: ') 135 | except EOFError: 136 | pass 137 | tb.stop() 138 | tb.wait() 139 | 140 | 141 | if __name__ == '__main__': 142 | main() 143 | -------------------------------------------------------------------------------- /grc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | install(FILES 16 | nordic_nordic_rx.xml 17 | nordic_nordic_tx.xml DESTINATION share/gnuradio/grc/blocks 18 | ) 19 | -------------------------------------------------------------------------------- /grc/nordic_nordic_rx.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | nordic_rx 4 | nordic_nordic_rx 5 | nordic 6 | import nordic 7 | nordic.nordic_rx() 8 | 13 | 14 | ... 15 | ... 16 | ... 17 | 18 | 19 | 24 | 25 | in 26 | 27 | 28 | 29 | 34 | 35 | out 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /grc/nordic_nordic_tx.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | nordic_tx 4 | nordic_nordic_tx 5 | nordic 6 | import nordic 7 | nordic.nordic_tx() 8 | 13 | 14 | ... 15 | ... 16 | ... 17 | 18 | 19 | 24 | 25 | in 26 | 27 | 28 | 29 | 34 | 35 | out 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /include/nordic/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | ######################################################################## 17 | # Install public header files 18 | ######################################################################## 19 | install(FILES 20 | api.h 21 | nordic_rx.h 22 | nordic_tx.h DESTINATION include/nordic 23 | ) 24 | 25 | -------------------------------------------------------------------------------- /include/nordic/api.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2016 Bastille Networks 3 | 4 | This program 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 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 15 | along with this program. If not, see . 16 | */ 17 | 18 | #ifndef INCLUDED_NORDIC_API_H 19 | #define INCLUDED_NORDIC_API_H 20 | 21 | #include 22 | 23 | #ifdef gnuradio_nordic_EXPORTS 24 | # define NORDIC_API __GR_ATTR_EXPORT 25 | #else 26 | # define NORDIC_API __GR_ATTR_IMPORT 27 | #endif 28 | 29 | #endif /* INCLUDED_NORDIC_API_H */ 30 | -------------------------------------------------------------------------------- /include/nordic/nordic_rx.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2016 Bastille Networks 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 INCLUDED_NORDIC_NORDIC_RX_H 23 | #define INCLUDED_NORDIC_NORDIC_RX_H 24 | 25 | #include 26 | #include 27 | 28 | namespace gr { 29 | namespace nordic { 30 | 31 | /*! 32 | * \brief <+description of block+> 33 | * \ingroup nordic 34 | * 35 | */ 36 | class NORDIC_API nordic_rx : 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 nordic::nordic_rx. 43 | * 44 | * To avoid accidental use of raw pointers, nordic::nordic_rx's 45 | * constructor is in a private implementation 46 | * class. nordic::nordic_rx::make is the public interface for 47 | * creating new instances. 48 | */ 49 | static sptr make(uint8_t channel=0, 50 | uint8_t address_length=5, 51 | uint8_t crc_length=2, 52 | uint8_t data_rate=0); 53 | 54 | // Channel getter/setter 55 | virtual uint8_t get_channel()=0; 56 | virtual void set_channel(uint8_t channel)=0; 57 | }; 58 | 59 | } // namespace nordic 60 | } // namespace gr 61 | 62 | #endif /* INCLUDED_NORDIC_NORDIC_RX_H */ 63 | 64 | -------------------------------------------------------------------------------- /include/nordic/nordic_tx.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2016 Bastille Networks 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 INCLUDED_NORDIC_NORDIC_TX_H 23 | #define INCLUDED_NORDIC_NORDIC_TX_H 24 | 25 | #include 26 | #include 27 | 28 | namespace gr { 29 | namespace nordic { 30 | 31 | /*! 32 | * \brief <+description of block+> 33 | * \ingroup nordic 34 | * 35 | */ 36 | class NORDIC_API nordic_tx : 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 nordic::nordic_tx. 43 | * 44 | * To avoid accidental use of raw pointers, nordic::nordic_tx's 45 | * constructor is in a private implementation 46 | * class. nordic::nordic_tx::make is the public interface for 47 | * creating new instances. 48 | */ 49 | static sptr make(uint8_t channel_count=1); 50 | }; 51 | 52 | } // namespace nordic 53 | } // namespace gr 54 | 55 | #endif /* INCLUDED_NORDIC_NORDIC_TX_H */ 56 | 57 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | ######################################################################## 17 | # Setup library 18 | ######################################################################## 19 | include(GrPlatform) #define LIB_SUFFIX 20 | 21 | include_directories(${Boost_INCLUDE_DIR}) 22 | link_directories(${Boost_LIBRARY_DIRS}) 23 | list(APPEND nordic_sources 24 | nordic_rx_impl.cc 25 | bit_shifting_byte_vector.cc 26 | nordic_tx_impl.cc 27 | enhanced_shockburst_packet.cc ) 28 | 29 | set(nordic_sources "${nordic_sources}" PARENT_SCOPE) 30 | if(NOT nordic_sources) 31 | MESSAGE(STATUS "No C++ sources... skipping lib/") 32 | return() 33 | endif(NOT nordic_sources) 34 | 35 | add_library(gnuradio-nordic SHARED ${nordic_sources}) 36 | target_link_libraries(gnuradio-nordic ${Boost_LIBRARIES} ${GNURADIO_ALL_LIBRARIES}) 37 | set_target_properties(gnuradio-nordic PROPERTIES DEFINE_SYMBOL "gnuradio_nordic_EXPORTS") 38 | 39 | if(APPLE) 40 | set_target_properties(gnuradio-nordic PROPERTIES 41 | INSTALL_NAME_DIR "${CMAKE_INSTALL_PREFIX}/lib" 42 | ) 43 | endif(APPLE) 44 | 45 | ######################################################################## 46 | # Install built library files 47 | ######################################################################## 48 | install(TARGETS gnuradio-nordic 49 | LIBRARY DESTINATION lib${LIB_SUFFIX} # .so/.dylib file 50 | ARCHIVE DESTINATION lib${LIB_SUFFIX} # .lib file 51 | RUNTIME DESTINATION bin # .dll file 52 | ) 53 | 54 | ######################################################################## 55 | # Build and register unit test 56 | ######################################################################## 57 | include(GrTest) 58 | 59 | include_directories(${CPPUNIT_INCLUDE_DIRS}) 60 | 61 | list(APPEND test_nordic_sources 62 | ${CMAKE_CURRENT_SOURCE_DIR}/test_nordic.cc 63 | ${CMAKE_CURRENT_SOURCE_DIR}/qa_nordic.cc 64 | ) 65 | 66 | add_executable(test-nordic ${test_nordic_sources}) 67 | 68 | target_link_libraries( 69 | test-nordic 70 | ${GNURADIO_RUNTIME_LIBRARIES} 71 | ${Boost_LIBRARIES} 72 | ${CPPUNIT_LIBRARIES} 73 | gnuradio-nordic 74 | ) 75 | 76 | GR_ADD_TEST(test_nordic test-nordic) 77 | -------------------------------------------------------------------------------- /lib/bit_shifting_byte_vector.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2016 Bastille Networks 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 "bit_shifting_byte_vector.h" 23 | 24 | #include 25 | 26 | // Constructor 27 | bit_shifting_byte_vector::bit_shifting_byte_vector(int length) : 28 | m_length(length), 29 | m_previous_state_write(7), 30 | m_previous_state_read(0) 31 | { 32 | m_bytes = new unsigned char[m_length]; 33 | for(int x = 0; x < 8; x++) 34 | m_previous_states[x] = new unsigned char[m_length]; 35 | } 36 | 37 | // Copy constructor 38 | bit_shifting_byte_vector::bit_shifting_byte_vector(const bit_shifting_byte_vector ©) 39 | { 40 | m_length = copy.m_length; 41 | m_bytes = new unsigned char[m_length]; 42 | memcpy(m_bytes, copy.m_bytes, m_length); 43 | for(int x = 0; x < 8; x++) 44 | { 45 | m_previous_states[x] = new unsigned char[m_length]; 46 | memcpy(m_previous_states[x], copy.m_previous_states[x], m_length); 47 | } 48 | m_previous_state_write = copy.m_previous_state_write; 49 | m_previous_state_read = copy.m_previous_state_read; 50 | } 51 | 52 | // Destructor 53 | bit_shifting_byte_vector::~bit_shifting_byte_vector() 54 | { 55 | delete[] m_bytes; 56 | for(int x = 0; x < 8; x++) 57 | { 58 | delete[] m_previous_states[x]; 59 | } 60 | } 61 | 62 | // Add a new bit 63 | void bit_shifting_byte_vector::add_bit(unsigned char bit) 64 | { 65 | // Update the previous state vector 66 | memcpy(m_previous_states[m_previous_state_write], m_bytes, m_length); 67 | 68 | // For the left m_length-1 bytes, we'll 69 | // use values from m_previous_states 70 | memcpy(m_bytes, m_previous_states[m_previous_state_read]+1, m_length - 1); 71 | 72 | // For the rightmost byte, we'll tack on 73 | // the the current new bit 74 | m_bytes[m_length - 1] = m_bytes[m_length - 1] << 1 | bit; 75 | 76 | // Update the previous state indices 77 | if(++m_previous_state_read > 7) m_previous_state_read = 0; 78 | if(++m_previous_state_write > 7) m_previous_state_write = 0; 79 | } -------------------------------------------------------------------------------- /lib/bit_shifting_byte_vector.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2016 Bastille Networks 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 BIT_SHIFTING_BYTE_VECTOR_H 23 | #define BIT_SHIFTING_BYTE_VECTOR_H 24 | 25 | // Byte vector that can be added to one bit at a time 26 | class bit_shifting_byte_vector 27 | { 28 | public: 29 | 30 | // Constructor 31 | bit_shifting_byte_vector(int length); 32 | 33 | // Copy constructor 34 | bit_shifting_byte_vector(const bit_shifting_byte_vector ©); 35 | 36 | // Destructor 37 | ~bit_shifting_byte_vector(); 38 | 39 | // Add a new bit 40 | void add_bit(unsigned char bit); 41 | 42 | // Get the byte array 43 | const unsigned char * bytes() { return m_bytes; } 44 | 45 | // Get a previous byte array state 46 | const unsigned char * bytes(int index) 47 | { 48 | index += m_previous_state_read; 49 | while(index < 0) index += 7; 50 | while(index > 7) index -= 7; 51 | return m_previous_states[index]; 52 | } 53 | 54 | private: 55 | 56 | // Bytes - current state 57 | unsigned char * m_bytes; 58 | 59 | // Length, in bytes 60 | int m_length; 61 | 62 | // Bytes - previous 8 states 63 | unsigned char * m_previous_states[8]; 64 | 65 | // Previous state index - write 66 | int m_previous_state_write; 67 | 68 | // Previous state index - read 69 | int m_previous_state_read; 70 | }; 71 | 72 | #endif // BIT_SHIFTING_BYTE_VECTOR_H 73 | -------------------------------------------------------------------------------- /lib/enhanced_shockburst_packet.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2016 Bastille Networks 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 25 | 26 | #include "enhanced_shockburst_packet.h" 27 | 28 | enhanced_shockburst_packet::enhanced_shockburst_packet(uint8_t address_length, 29 | uint8_t payload_length, 30 | uint8_t sequence_number, 31 | uint8_t no_ack, 32 | uint8_t crc_length, 33 | uint8_t * address, 34 | uint8_t * payload 35 | ) : 36 | m_address_length(address_length), 37 | m_payload_length(payload_length), 38 | m_sequence_number(sequence_number), 39 | m_no_ack(no_ack), 40 | m_crc_length(crc_length) 41 | { 42 | // Allocate buffers 43 | m_address = new uint8_t[m_address_length]; 44 | m_payload = new uint8_t[m_payload_length]; 45 | m_crc = new uint8_t[m_crc_length]; 46 | 47 | // Copy over address and payload 48 | memcpy(m_address, address, m_address_length); 49 | memcpy(m_payload, payload, m_payload_length); 50 | 51 | // Build the packet bytes 52 | const int blen = 3 /* preamble + PCF */ + 53 | m_crc_length + 54 | m_address_length + 55 | m_payload_length; 56 | m_packet_length_bytes = blen; 57 | m_packet_length_bits = blen*8; 58 | m_packet_bytes = new uint8_t[blen]; 59 | 60 | // Preamble 61 | if((address[0] & 0x80) == 0x80) m_packet_bytes[0] = 0xAA; 62 | else m_packet_bytes[0] = 0x55; 63 | 64 | // Address 65 | memcpy(&m_packet_bytes[1], address, m_address_length); 66 | 67 | // PCF 68 | m_packet_bytes[1 + m_address_length] = (m_payload_length & 0x3F) << 2; 69 | m_packet_bytes[1 + m_address_length] |= (m_sequence_number & 0x3); 70 | m_packet_bytes[2 + m_address_length] = m_no_ack << 7; 71 | 72 | // Payload 73 | for(int b = 0; b < m_payload_length; b++) 74 | { 75 | m_packet_bytes[2 + m_address_length + b] |= (payload[b] >> 1); 76 | m_packet_bytes[3 + m_address_length + b] |= (payload[b] << 7); 77 | } 78 | 79 | // Calculate the CRC 80 | uint16_t crc = 0xFFFF; 81 | for(int b = 1; b < 7 + m_payload_length; b++) crc = crc_update(crc, m_packet_bytes[b]); 82 | crc = crc_update(crc, m_packet_bytes[7 + m_payload_length] & 0x80, 1); 83 | memcpy(m_crc, &crc, m_crc_length); 84 | m_packet_bytes[2 + m_address_length + m_payload_length] |= ((crc >> 9) & 0xFF); 85 | m_packet_bytes[3 + m_address_length + m_payload_length] |= ((crc >> 1) & 0xFF); 86 | m_packet_bytes[4 + m_address_length + m_payload_length] |= ((crc << 7) & 0x80); 87 | } 88 | 89 | // Destructur 90 | enhanced_shockburst_packet::~enhanced_shockburst_packet() 91 | { 92 | delete[] m_address; 93 | delete[] m_payload; 94 | delete[] m_crc; 95 | } 96 | 97 | // Attempt to parse a packet from some incoming bytes 98 | bool enhanced_shockburst_packet::try_parse(const uint8_t * bytes, 99 | const uint8_t * bytes_shifted, 100 | uint8_t address_length, 101 | uint8_t crc_length, 102 | enhanced_shockburst_packet *& packet) 103 | { 104 | // Read the payload length 105 | uint8_t payload_length = bytes[6] >> 2; 106 | if(payload_length > 32) return false; 107 | 108 | // Read the address 109 | uint8_t * address = new uint8_t[address_length]; 110 | memcpy(address, &bytes[1], address_length); 111 | 112 | // Read the given CRC 113 | uint16_t crc_given; 114 | memcpy(&crc_given, &bytes_shifted[8 + payload_length], 2); 115 | 116 | // Calculate the CRC 117 | uint16_t crc = 0xFFFF; 118 | for(int b = 1; b < 7 + payload_length; b++) crc = crc_update(crc, bytes[b]); 119 | crc = crc_update(crc, bytes[7 + payload_length] & 0x80, 1); 120 | crc = htons(crc); 121 | 122 | // Validate the CRC 123 | if(memcmp(&crc, &crc_given, 2) != 0) 124 | { 125 | delete[] address; 126 | return false; 127 | } 128 | 129 | // Read the sequence number and no-ACK bit 130 | uint8_t seq = bytes[6] & 0x3; 131 | uint8_t no_ack = bytes[7] >> 7; 132 | 133 | // Read the payload 134 | uint8_t payload[32]; 135 | memcpy(payload, &bytes_shifted[8], payload_length); 136 | 137 | // Update the fields 138 | packet = new enhanced_shockburst_packet(address_length, payload_length, seq, no_ack, crc_length, address, payload); 139 | 140 | // Cleanup 141 | delete[] address; 142 | 143 | return true; 144 | } 145 | 146 | // Print the packet details to standard out 147 | void enhanced_shockburst_packet::print() 148 | { 149 | printf("Address: "); 150 | for(int x = 0; x < m_address_length; x++) printf("%02X ", m_address[x]); 151 | printf("\n"); 152 | 153 | printf("Payload: "); 154 | for(int x = 0; x < m_payload_length; x++) printf("%02X ", m_payload[x]); 155 | printf("\n"); 156 | 157 | printf("CRC: "); 158 | for(int x = 0; x < m_crc_length; x++) printf("%02X ", m_crc[x]); 159 | printf("\n"); 160 | 161 | printf("Bytes: "); 162 | for(int x = 0; x < m_packet_length_bytes; x++) printf("%02X ", m_packet_bytes[x]); 163 | printf("\n"); 164 | 165 | printf("\n"); 166 | } 167 | 168 | // Process a crc byte (or partial byte) 169 | uint16_t enhanced_shockburst_packet::crc_update (uint16_t crc, uint8_t data, uint8_t bits) 170 | { 171 | crc = crc ^ ((uint16_t)data << 8); 172 | for (int x = 0; x < bits; x++) 173 | { 174 | if(crc & 0x8000) crc = (crc << 1) ^ 0x1021; 175 | else crc <<= 1; 176 | } 177 | return crc; 178 | } -------------------------------------------------------------------------------- /lib/enhanced_shockburst_packet.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2016 Bastille Networks 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 ENHANCED_SHOCKBURST_PACKET_H 23 | #define ENHANCED_SHOCKBURST_PACKET_H 24 | 25 | #include 26 | 27 | class enhanced_shockburst_packet 28 | { 29 | public: 30 | 31 | // Constructor 32 | enhanced_shockburst_packet(uint8_t address_length, 33 | uint8_t payload_length, 34 | uint8_t sequence_number, 35 | uint8_t no_ack, 36 | uint8_t crc_length, 37 | uint8_t * address, 38 | uint8_t * payload 39 | ); 40 | 41 | // Destructur 42 | ~enhanced_shockburst_packet(); 43 | 44 | // Attempt to parse a packet from some incoming bytes 45 | static bool try_parse(const uint8_t * bytes, 46 | const uint8_t * bytes_shifted, 47 | uint8_t address_length, 48 | uint8_t crc_length, 49 | enhanced_shockburst_packet *& packet); 50 | 51 | // Process a crc byte (or partial byte) 52 | static uint16_t crc_update (uint16_t crc, uint8_t data, uint8_t bits=8); 53 | 54 | // Print the packet details to standard out 55 | void print(); 56 | 57 | // Getters 58 | const uint8_t payload_length() { return m_payload_length; } 59 | const uint8_t bytes_length() { return m_packet_length_bytes; } 60 | const uint8_t sequence_number() { return m_sequence_number; } 61 | const uint8_t no_ack() { return m_no_ack; } 62 | const uint8_t * address() { return m_address; } 63 | const uint8_t * payload() { return m_payload; } 64 | const uint8_t * crc() { return m_crc; } 65 | const uint8_t * bytes() { return m_packet_bytes; } 66 | 67 | private: 68 | 69 | // Address length 70 | uint8_t m_address_length; 71 | 72 | // Payload length 73 | uint8_t m_payload_length; 74 | 75 | // Packet length 76 | uint8_t m_packet_length_bytes; 77 | uint16_t m_packet_length_bits; 78 | 79 | // Sequence number (ESB) 80 | uint8_t m_sequence_number; 81 | 82 | // No ACK bit (ESB) 83 | bool m_no_ack; 84 | 85 | // CRC length 86 | uint8_t m_crc_length; 87 | 88 | // Address 89 | uint8_t * m_address; 90 | 91 | // Payload 92 | uint8_t * m_payload; 93 | 94 | // CRC 95 | uint8_t * m_crc; 96 | 97 | // Assembled packet bytes 98 | uint8_t * m_packet_bytes; 99 | 100 | } __attribute__((packed)); 101 | 102 | #endif // ENHANCED_SHOCKBURST_PACKET_H -------------------------------------------------------------------------------- /lib/nordic_rx_impl.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2016 Bastille Networks. 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 | #include "nordic_rx_impl.h" 29 | #include "nordictap.h" 30 | 31 | namespace gr { 32 | namespace nordic { 33 | 34 | nordic_rx::sptr 35 | nordic_rx::make(uint8_t channel, 36 | uint8_t address_length, 37 | uint8_t crc_length, 38 | uint8_t data_rate) 39 | { 40 | return gnuradio::get_initial_sptr 41 | (new nordic_rx_impl(channel, address_length, crc_length, data_rate)); 42 | } 43 | 44 | /* 45 | * The private constructor 46 | */ 47 | nordic_rx_impl::nordic_rx_impl(uint8_t channel, 48 | uint8_t address_length, 49 | uint8_t crc_length, 50 | uint8_t data_rate) 51 | : gr::sync_block("nordic_rx", 52 | gr::io_signature::make(1, 1, sizeof(uint8_t)), 53 | gr::io_signature::make(0, 0, 0)), 54 | m_decoded_bits_bytes(42*8 /* buffer sufficient for max ESB frame length */), 55 | m_crc_length(crc_length), 56 | m_address_length(address_length), 57 | m_channel(channel), 58 | m_data_rate(data_rate) 59 | { 60 | message_port_register_out(pmt::mp("nordictap_out")); 61 | } 62 | 63 | /* 64 | * Our virtual destructor. 65 | */ 66 | nordic_rx_impl::~nordic_rx_impl() 67 | { 68 | 69 | } 70 | 71 | int 72 | nordic_rx_impl::work(int noutput_items, 73 | gr_vector_const_void_star &input_items, 74 | gr_vector_void_star &output_items) 75 | { 76 | const uint8_t *in = (const uint8_t *) input_items[0]; 77 | 78 | // Step through the incoming demodulated bits 79 | for(int x = 0; x < noutput_items; x++) 80 | { 81 | // Add the incoming bit to the bit shifted byte array 82 | m_decoded_bits_bytes.add_bit(in[x]); 83 | const uint8_t * bytes = m_decoded_bits_bytes.bytes(); 84 | 85 | // Check for a valid preamble 86 | if(bytes[0] == 0xAA || bytes[0] == 0x55) 87 | { 88 | // Check for a valid first address bit 89 | if((bytes[0] & 0x80) == (bytes[1] & 0x80)) 90 | { 91 | // Attempt to decode a payload 92 | if(enhanced_shockburst_packet::try_parse(bytes, 93 | m_decoded_bits_bytes.bytes(0), 94 | m_address_length, 95 | m_crc_length, 96 | m_enhanced_shockburst)) 97 | { 98 | // Build the wireshark header 99 | nordictap_header header; 100 | header.channel = m_channel; 101 | header.data_rate = m_data_rate; 102 | header.address_length = m_address_length; 103 | header.payload_length = m_enhanced_shockburst->payload_length(); 104 | header.sequence_number = m_enhanced_shockburst->sequence_number(); 105 | header.no_ack = m_enhanced_shockburst->no_ack(); 106 | header.crc_length = m_crc_length; 107 | 108 | // Concatenate the header, address, payload, and CRC 109 | uint8_t buffer_length = sizeof(nordictap_header) + m_address_length + header.payload_length + m_crc_length; 110 | uint8_t * buffer = new uint8_t[buffer_length]; 111 | memcpy(&buffer[0], &header, sizeof(nordictap_header)); 112 | memcpy(&buffer[sizeof(nordictap_header)], m_enhanced_shockburst->address(), m_address_length); 113 | memcpy(&buffer[sizeof(nordictap_header) + m_address_length], m_enhanced_shockburst->payload(), header.payload_length); 114 | memcpy(&buffer[sizeof(nordictap_header) + m_address_length + header.payload_length], m_enhanced_shockburst->crc(), m_crc_length); 115 | 116 | // Send the packet to wireshark 117 | boost::asio::io_service io_service; 118 | boost::asio::ip::udp::resolver resolver(io_service); 119 | boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), "127.0.0.1", "9451"); 120 | boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); 121 | boost::asio::ip::udp::socket socket(io_service); 122 | socket.open(boost::asio::ip::udp::v4()); 123 | socket.send_to(boost::asio::buffer(buffer, buffer_length), receiver_endpoint); 124 | 125 | // Send the packet to nordictap_out 126 | message_port_pub(pmt::intern("nordictap_out"), pmt::init_u8vector(buffer_length, buffer)); 127 | 128 | // Cleanup 129 | delete[] buffer; 130 | } 131 | } 132 | } 133 | } 134 | 135 | return noutput_items; 136 | } 137 | 138 | // Channel getter 139 | uint8_t nordic_rx_impl::get_channel() 140 | { 141 | return m_channel; 142 | } 143 | 144 | // Channel setter 145 | void nordic_rx_impl::set_channel(uint8_t channel) 146 | { 147 | m_channel = channel; 148 | } 149 | 150 | } /* namespace nordic */ 151 | } /* namespace gr */ 152 | 153 | -------------------------------------------------------------------------------- /lib/nordic_rx_impl.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2016 Bastille Networks. 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_NORDIC_NORDIC_RX_IMPL_H 22 | #define INCLUDED_NORDIC_NORDIC_RX_IMPL_H 23 | 24 | #include 25 | #include 26 | #include "bit_shifting_byte_vector.h" 27 | #include "enhanced_shockburst_packet.h" 28 | 29 | namespace gr { 30 | namespace nordic { 31 | 32 | class nordic_rx_impl : public nordic_rx 33 | { 34 | private: 35 | 36 | // CRC16-CCITT 37 | boost::crc_optimal<16, 0x1021, 0, 0, false, false> m_crc; 38 | 39 | // Configuration 40 | uint8_t m_address_length; 41 | uint8_t m_crc_length; 42 | uint8_t m_channel; 43 | uint8_t m_data_rate; 44 | 45 | // Incoming bit/byte vector 46 | bit_shifting_byte_vector m_decoded_bits_bytes; 47 | 48 | // Enhanced shockburst packet 49 | enhanced_shockburst_packet * m_enhanced_shockburst; 50 | 51 | public: 52 | 53 | // Constructor/destructor 54 | nordic_rx_impl(uint8_t channel, 55 | uint8_t address_length, 56 | uint8_t crc_length, 57 | uint8_t data_rate); 58 | ~nordic_rx_impl(); 59 | 60 | // Main work method 61 | int work(int noutput_items, 62 | gr_vector_const_void_star &input_items, 63 | gr_vector_void_star &output_items); 64 | 65 | // Channel getter/setter 66 | uint8_t get_channel(); 67 | void set_channel(uint8_t channel); 68 | }; 69 | 70 | } // namespace nordic 71 | } // namespace gr 72 | 73 | #endif /* INCLUDED_NORDIC_NORDIC_RX_IMPL_H */ 74 | 75 | -------------------------------------------------------------------------------- /lib/nordic_tx_impl.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2016 Bastille Networks. 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 "nordic_tx_impl.h" 28 | #include "nordictap.h" 29 | #include "enhanced_shockburst_packet.h" 30 | 31 | namespace gr { 32 | namespace nordic { 33 | 34 | nordic_tx::sptr 35 | nordic_tx::make(uint8_t channel_count) 36 | { 37 | return gnuradio::get_initial_sptr 38 | (new nordic_tx_impl(channel_count)); 39 | } 40 | 41 | /* 42 | * The private constructor 43 | */ 44 | nordic_tx_impl::nordic_tx_impl(uint8_t channel_count) 45 | : gr::sync_block("nordic_tx", 46 | gr::io_signature::make(0, 0, 0), 47 | gr::io_signature::make(1, channel_count, sizeof(uint8_t))), 48 | m_channel_count(channel_count) 49 | { 50 | // Register nordictap input, which accepts packets to transmit 51 | message_port_register_in(pmt::intern("nordictap_in")); 52 | set_msg_handler(pmt::intern("nordictap_in"), boost::bind(&nordic_tx_impl::nordictap_message_handler, this, _1)); 53 | } 54 | 55 | /* 56 | * Our virtual destructor. 57 | */ 58 | nordic_tx_impl::~nordic_tx_impl() 59 | { 60 | } 61 | 62 | // Incoming message handler 63 | void nordic_tx_impl::nordictap_message_handler(pmt::pmt_t msg) 64 | { 65 | m_tx_queue.push(msg); 66 | } 67 | 68 | int 69 | nordic_tx_impl::work(int noutput_items, 70 | gr_vector_const_void_star &input_items, 71 | gr_vector_void_star &output_items) 72 | { 73 | // uint8_t *out = (uint8_t *) output_items[0]; 74 | 75 | // Check for new input 76 | if(m_tx_queue.size()) 77 | { 78 | // Get the blob 79 | std::vector vec = pmt::u8vector_elements(m_tx_queue.front()); 80 | uint8_t * blob = vec.data(); 81 | 82 | // Read the channel index 83 | uint8_t channel = blob[0]; 84 | 85 | // Read the nordictap header, address, and payload 86 | nordictap_header header; 87 | memcpy(&header, &blob[1], sizeof(nordictap_header)); 88 | 89 | // Read the address and payload 90 | const uint8_t alen = header.address_length; 91 | const uint8_t plen = header.payload_length; 92 | uint8_t * address = new uint8_t[alen]; 93 | uint8_t * payload = new uint8_t[plen]; 94 | memcpy(address, &blob[sizeof(nordictap_header)+1], alen); 95 | memcpy(payload, &blob[sizeof(nordictap_header)+1 + alen], plen); 96 | 97 | // Build the packet 98 | enhanced_shockburst_packet * packet = 99 | new enhanced_shockburst_packet(header.address_length, 100 | header.payload_length, 101 | header.sequence_number, 102 | header.no_ack, 103 | header.crc_length, 104 | address, 105 | payload); 106 | 107 | // Remove the blob from the queue 108 | m_tx_queue.pop(); 109 | 110 | // Write the output bytes 111 | uint8_t * out = (uint8_t *)output_items[channel]; 112 | for(int b = 0; b < packet->bytes_length(); b++) 113 | { 114 | out[b] = packet->bytes()[b]; 115 | out[packet->bytes_length()*2+b] = packet->bytes()[b]; 116 | } 117 | 118 | // Write zeros to the other channels' buffers 119 | for(int c = 0; c < m_channel_count; c++) 120 | { 121 | if(c != channel) 122 | { 123 | memset(output_items[c], 0, packet->bytes_length()*2); 124 | } 125 | } 126 | 127 | // Cleanup 128 | delete[] address; 129 | delete[] payload; 130 | delete packet; 131 | 132 | // Return the number of bytes produced 133 | return packet->bytes_length()*2; 134 | } 135 | else 136 | { 137 | return 0; 138 | } 139 | } 140 | 141 | // Process a crc byte (or partial byte) 142 | uint16_t nordic_tx_impl::crc_update (uint16_t crc, uint8_t data, uint8_t bits) 143 | { 144 | crc = crc ^ ((uint16_t)data << 8); 145 | for (int x = 0; x < bits; x++) 146 | { 147 | if(crc & 0x8000) crc = (crc << 1) ^ 0x1021; 148 | else crc <<= 1; 149 | } 150 | return crc; 151 | } 152 | 153 | } /* namespace nordic */ 154 | } /* namespace gr */ 155 | 156 | -------------------------------------------------------------------------------- /lib/nordic_tx_impl.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2016 Bastille Networks. 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_NORDIC_NORDIC_TX_IMPL_H 22 | #define INCLUDED_NORDIC_NORDIC_TX_IMPL_H 23 | 24 | #include 25 | #include 26 | 27 | namespace gr { 28 | namespace nordic { 29 | 30 | class nordic_tx_impl : public nordic_tx 31 | { 32 | private: 33 | 34 | // TX nordictap queue 35 | std::queue m_tx_queue; 36 | 37 | // Lookup table to unpack bytes to bits (NRZ) 38 | char m_unpack_table[256][8]; 39 | 40 | // Number of output channels 41 | uint8_t m_channel_count; 42 | 43 | // Incoming message handler 44 | void nordictap_message_handler(pmt::pmt_t msg); 45 | 46 | // Process a crc byte (or partial byte) 47 | uint16_t crc_update (uint16_t crc, uint8_t data, uint8_t bits=8); 48 | 49 | public: 50 | nordic_tx_impl(uint8_t channel_count); 51 | ~nordic_tx_impl(); 52 | 53 | // Where all the action really happens 54 | int work(int noutput_items, 55 | gr_vector_const_void_star &input_items, 56 | gr_vector_void_star &output_items); 57 | }; 58 | 59 | } // namespace nordic 60 | } // namespace gr 61 | 62 | #endif /* INCLUDED_NORDIC_NORDIC_TX_IMPL_H */ 63 | 64 | -------------------------------------------------------------------------------- /lib/nordictap.h: -------------------------------------------------------------------------------- 1 | #ifndef NORDICTAP_H 2 | #define NORDICTAP_H 3 | 4 | #define NORDICTAP_GSM_PORT 9451 5 | 6 | // Data rates 7 | #define NORDICTAP_RATE_250K 0 8 | #define NORDICTAP_RATE_1M 1 9 | #define NORDICTAP_RATE_2M 2 10 | 11 | struct nordictap_header 12 | { 13 | // Channel number 14 | uint8_t channel; 15 | 16 | // Data rate 17 | uint8_t data_rate; 18 | 19 | // Address length 20 | uint8_t address_length; 21 | 22 | // Payload length 23 | uint8_t payload_length; 24 | 25 | // Sequence number (ESB) 26 | uint8_t sequence_number; 27 | 28 | // No ACK bit (ESB) 29 | bool no_ack; 30 | 31 | // CRC length, in bytes 32 | uint8_t crc_length; 33 | 34 | } __attribute__((packed)); 35 | 36 | #endif // NORDICTAP_H -------------------------------------------------------------------------------- /lib/qa_nordic.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2016 Bastille Networks 3 | 4 | This program 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 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 15 | along with this program. If not, see . 16 | */ 17 | 18 | /* 19 | * This class gathers together all the test cases for the gr-filter 20 | * directory into a single test suite. As you create new test cases, 21 | * add them here. 22 | */ 23 | 24 | #include "qa_nordic.h" 25 | 26 | CppUnit::TestSuite * 27 | qa_nordic::suite() 28 | { 29 | CppUnit::TestSuite *s = new CppUnit::TestSuite("nordic"); 30 | 31 | return s; 32 | } 33 | -------------------------------------------------------------------------------- /lib/qa_nordic.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | Copyright (C) 2016 Bastille Networks 4 | 5 | This program 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 of the License, or 8 | (at your option) any later version. 9 | 10 | This program 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 program. If not, see . 17 | */ 18 | 19 | #ifndef _QA_NORDIC_H_ 20 | #define _QA_NORDIC_H_ 21 | 22 | #include 23 | #include 24 | 25 | //! collect all the tests for the gr-filter directory 26 | 27 | class __GR_ATTR_EXPORT qa_nordic 28 | { 29 | public: 30 | //! return suite of tests for all of gr-filter directory 31 | static CppUnit::TestSuite *suite(); 32 | }; 33 | 34 | #endif /* _QA_NORDIC_H_ */ 35 | -------------------------------------------------------------------------------- /lib/test_nordic.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | Copyright (C) 2016 Bastille Networks 4 | 5 | This program 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 of the License, or 8 | (at your option) any later version. 9 | 10 | This program 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 program. If not, see . 17 | */ 18 | 19 | #ifdef HAVE_CONFIG_H 20 | #include "config.h" 21 | #endif 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include "qa_nordic.h" 28 | #include 29 | #include 30 | 31 | int 32 | main (int argc, char **argv) 33 | { 34 | CppUnit::TextTestRunner runner; 35 | std::ofstream xmlfile(get_unittest_path("nordic.xml").c_str()); 36 | CppUnit::XmlOutputter *xmlout = new CppUnit::XmlOutputter(&runner.result(), xmlfile); 37 | 38 | runner.addTest(qa_nordic::suite()); 39 | runner.setOutputter(xmlout); 40 | 41 | bool was_successful = runner.run("", false); 42 | 43 | return was_successful ? 0 : 1; 44 | } 45 | -------------------------------------------------------------------------------- /python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | ######################################################################## 17 | # Include python install macros 18 | ######################################################################## 19 | include(GrPython) 20 | if(NOT PYTHONINTERP_FOUND) 21 | return() 22 | endif() 23 | 24 | ######################################################################## 25 | # Install python sources 26 | ######################################################################## 27 | GR_PYTHON_INSTALL( 28 | FILES 29 | __init__.py 30 | DESTINATION ${GR_PYTHON_DIR}/nordic 31 | ) 32 | 33 | ######################################################################## 34 | # Handle the unit tests 35 | ######################################################################## 36 | include(GrTest) 37 | 38 | set(GR_TEST_TARGET_DEPS gnuradio-nordic) 39 | set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig) 40 | -------------------------------------------------------------------------------- /python/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Copyright (C) 2016 Bastille Networks 3 | 4 | This program 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 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 15 | along with this program. If not, see . 16 | ''' 17 | 18 | # The presence of this file turns this directory into a Python package 19 | 20 | ''' 21 | This is the GNU Radio NORDIC module. Place your Python package 22 | description here (python/__init__.py). 23 | ''' 24 | 25 | # import swig generated symbols into the nordic namespace 26 | try: 27 | # this might fail if the module is python-only 28 | from nordic_swig import * 29 | except ImportError: 30 | pass 31 | 32 | # import any pure python here 33 | 34 | 35 | # 36 | -------------------------------------------------------------------------------- /python/build_utils.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Copyright (C) 2016 Bastille Networks 3 | 4 | This program 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 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 15 | along with this program. If not, see . 16 | ''' 17 | 18 | """Misc utilities used at build time 19 | """ 20 | 21 | import re, os, os.path 22 | from build_utils_codes import * 23 | 24 | 25 | # set srcdir to the directory that contains Makefile.am 26 | try: 27 | srcdir = os.environ['srcdir'] 28 | except KeyError, e: 29 | srcdir = "." 30 | srcdir = srcdir + '/' 31 | 32 | # set do_makefile to either true or false dependeing on the environment 33 | try: 34 | if os.environ['do_makefile'] == '0': 35 | do_makefile = False 36 | else: 37 | do_makefile = True 38 | except KeyError, e: 39 | do_makefile = False 40 | 41 | # set do_sources to either true or false dependeing on the environment 42 | try: 43 | if os.environ['do_sources'] == '0': 44 | do_sources = False 45 | else: 46 | do_sources = True 47 | except KeyError, e: 48 | do_sources = True 49 | 50 | name_dict = {} 51 | 52 | def log_output_name (name): 53 | (base, ext) = os.path.splitext (name) 54 | ext = ext[1:] # drop the leading '.' 55 | 56 | entry = name_dict.setdefault (ext, []) 57 | entry.append (name) 58 | 59 | def open_and_log_name (name, dir): 60 | global do_sources 61 | if do_sources: 62 | f = open (name, dir) 63 | else: 64 | f = None 65 | log_output_name (name) 66 | return f 67 | 68 | def expand_template (d, template_filename, extra = ""): 69 | '''Given a dictionary D and a TEMPLATE_FILENAME, expand template into output file 70 | ''' 71 | global do_sources 72 | output_extension = extract_extension (template_filename) 73 | template = open_src (template_filename, 'r') 74 | output_name = d['NAME'] + extra + '.' + output_extension 75 | log_output_name (output_name) 76 | if do_sources: 77 | output = open (output_name, 'w') 78 | do_substitution (d, template, output) 79 | output.close () 80 | template.close () 81 | 82 | def output_glue (dirname): 83 | output_makefile_fragment () 84 | output_ifile_include (dirname) 85 | 86 | def output_makefile_fragment (): 87 | global do_makefile 88 | if not do_makefile: 89 | return 90 | # overwrite the source, which must be writable; this should have been 91 | # checked for beforehand in the top-level Makefile.gen.gen . 92 | f = open (os.path.join (os.environ.get('gendir', os.environ.get('srcdir', '.')), 'Makefile.gen'), 'w') 93 | f.write ('#\n# This file is machine generated. All edits will be overwritten\n#\n') 94 | output_subfrag (f, 'h') 95 | output_subfrag (f, 'i') 96 | output_subfrag (f, 'cc') 97 | f.close () 98 | 99 | def output_ifile_include (dirname): 100 | global do_sources 101 | if do_sources: 102 | f = open ('%s_generated.i' % (dirname,), 'w') 103 | f.write ('//\n// This file is machine generated. All edits will be overwritten\n//\n') 104 | files = name_dict.setdefault ('i', []) 105 | files.sort () 106 | f.write ('%{\n') 107 | for file in files: 108 | f.write ('#include <%s>\n' % (file[0:-1] + 'h',)) 109 | f.write ('%}\n\n') 110 | for file in files: 111 | f.write ('%%include <%s>\n' % (file,)) 112 | 113 | def output_subfrag (f, ext): 114 | files = name_dict.setdefault (ext, []) 115 | files.sort () 116 | f.write ("GENERATED_%s =" % (ext.upper ())) 117 | for file in files: 118 | f.write (" \\\n\t%s" % (file,)) 119 | f.write ("\n\n") 120 | 121 | def extract_extension (template_name): 122 | # template name is something like: GrFIRfilterXXX.h.t 123 | # we return everything between the penultimate . and .t 124 | mo = re.search (r'\.([a-z]+)\.t$', template_name) 125 | if not mo: 126 | raise ValueError, "Incorrectly formed template_name '%s'" % (template_name,) 127 | return mo.group (1) 128 | 129 | def open_src (name, mode): 130 | global srcdir 131 | return open (os.path.join (srcdir, name), mode) 132 | 133 | def do_substitution (d, in_file, out_file): 134 | def repl (match_obj): 135 | key = match_obj.group (1) 136 | # print key 137 | return d[key] 138 | 139 | inp = in_file.read () 140 | out = re.sub (r"@([a-zA-Z0-9_]+)@", repl, inp) 141 | out_file.write (out) 142 | 143 | 144 | 145 | copyright = '''/* -*- c++ -*- */ 146 | /* 147 | Copyright (C) 2016 Bastille Networks 148 | 149 | This program is free software: you can redistribute it and/or modify 150 | it under the terms of the GNU General Public License as published by 151 | the Free Software Foundation, either version 3 of the License, or 152 | (at your option) any later version. 153 | 154 | This program is distributed in the hope that it will be useful, 155 | but WITHOUT ANY WARRANTY; without even the implied warranty of 156 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 157 | GNU General Public License for more details. 158 | 159 | You should have received a copy of the GNU General Public License 160 | along with this program. If not, see . 161 | */ 162 | ''' 163 | 164 | def is_complex (code3): 165 | if i_code (code3) == 'c' or o_code (code3) == 'c': 166 | return '1' 167 | else: 168 | return '0' 169 | 170 | 171 | def standard_dict (name, code3, package='gr'): 172 | d = {} 173 | d['NAME'] = name 174 | d['NAME_IMPL'] = name+'_impl' 175 | d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper()) 176 | d['GUARD_NAME_IMPL'] = 'INCLUDED_%s_%s_IMPL_H' % (package.upper(), name.upper()) 177 | d['BASE_NAME'] = re.sub ('^' + package + '_', '', name) 178 | d['SPTR_NAME'] = '%s_sptr' % name 179 | d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten' 180 | d['COPYRIGHT'] = copyright 181 | d['TYPE'] = i_type (code3) 182 | d['I_TYPE'] = i_type (code3) 183 | d['O_TYPE'] = o_type (code3) 184 | d['TAP_TYPE'] = tap_type (code3) 185 | d['IS_COMPLEX'] = is_complex (code3) 186 | return d 187 | 188 | 189 | def standard_dict2 (name, code3, package): 190 | d = {} 191 | d['NAME'] = name 192 | d['BASE_NAME'] = name 193 | d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper()) 194 | d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten' 195 | d['COPYRIGHT'] = copyright 196 | d['TYPE'] = i_type (code3) 197 | d['I_TYPE'] = i_type (code3) 198 | d['O_TYPE'] = o_type (code3) 199 | d['TAP_TYPE'] = tap_type (code3) 200 | d['IS_COMPLEX'] = is_complex (code3) 201 | return d 202 | 203 | def standard_impl_dict2 (name, code3, package): 204 | d = {} 205 | d['NAME'] = name 206 | d['IMPL_NAME'] = name 207 | d['BASE_NAME'] = name.rstrip("impl").rstrip("_") 208 | d['GUARD_NAME'] = 'INCLUDED_%s_%s_H' % (package.upper(), name.upper()) 209 | d['WARNING'] = 'WARNING: this file is machine generated. Edits will be overwritten' 210 | d['COPYRIGHT'] = copyright 211 | d['FIR_TYPE'] = "fir_filter_" + code3 212 | d['CFIR_TYPE'] = "fir_filter_" + code3[0:2] + 'c' 213 | d['TYPE'] = i_type (code3) 214 | d['I_TYPE'] = i_type (code3) 215 | d['O_TYPE'] = o_type (code3) 216 | d['TAP_TYPE'] = tap_type (code3) 217 | d['IS_COMPLEX'] = is_complex (code3) 218 | return d 219 | -------------------------------------------------------------------------------- /python/build_utils_codes.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Copyright (C) 2016 Bastille Networks 3 | 4 | This program 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 of the License, or 7 | (at your option) any later version. 8 | 9 | This program 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 15 | along with this program. If not, see . 16 | ''' 17 | 18 | def i_code (code3): 19 | return code3[0] 20 | 21 | def o_code (code3): 22 | if len (code3) >= 2: 23 | return code3[1] 24 | else: 25 | return code3[0] 26 | 27 | def tap_code (code3): 28 | if len (code3) >= 3: 29 | return code3[2] 30 | else: 31 | return code3[0] 32 | 33 | def i_type (code3): 34 | return char_to_type[i_code (code3)] 35 | 36 | def o_type (code3): 37 | return char_to_type[o_code (code3)] 38 | 39 | def tap_type (code3): 40 | return char_to_type[tap_code (code3)] 41 | 42 | 43 | char_to_type = {} 44 | char_to_type['s'] = 'short' 45 | char_to_type['i'] = 'int' 46 | char_to_type['f'] = 'float' 47 | char_to_type['c'] = 'gr_complex' 48 | char_to_type['b'] = 'unsigned char' 49 | -------------------------------------------------------------------------------- /swig/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2016 Bastille Networks 2 | # 3 | # This program 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 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program 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 program. If not, see . 15 | 16 | ######################################################################## 17 | # Check if there is C++ code at all 18 | ######################################################################## 19 | if(NOT nordic_sources) 20 | MESSAGE(STATUS "No C++ sources... skipping swig/") 21 | return() 22 | endif(NOT nordic_sources) 23 | 24 | ######################################################################## 25 | # Include swig generation macros 26 | ######################################################################## 27 | find_package(SWIG) 28 | find_package(PythonLibs 2) 29 | if(NOT SWIG_FOUND OR NOT PYTHONLIBS_FOUND) 30 | return() 31 | endif() 32 | include(GrSwig) 33 | include(GrPython) 34 | 35 | ######################################################################## 36 | # Setup swig generation 37 | ######################################################################## 38 | foreach(incdir ${GNURADIO_RUNTIME_INCLUDE_DIRS}) 39 | list(APPEND GR_SWIG_INCLUDE_DIRS ${incdir}/gnuradio/swig) 40 | endforeach(incdir) 41 | 42 | set(GR_SWIG_LIBRARIES gnuradio-nordic) 43 | set(GR_SWIG_DOC_FILE ${CMAKE_CURRENT_BINARY_DIR}/nordic_swig_doc.i) 44 | set(GR_SWIG_DOC_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../include) 45 | 46 | GR_SWIG_MAKE(nordic_swig nordic_swig.i) 47 | 48 | ######################################################################## 49 | # Install the build swig module 50 | ######################################################################## 51 | GR_SWIG_INSTALL(TARGETS nordic_swig DESTINATION ${GR_PYTHON_DIR}/nordic) 52 | 53 | ######################################################################## 54 | # Install swig .i files for development 55 | ######################################################################## 56 | install( 57 | FILES 58 | nordic_swig.i 59 | ${CMAKE_CURRENT_BINARY_DIR}/nordic_swig_doc.i 60 | DESTINATION ${GR_INCLUDE_DIR}/nordic/swig 61 | ) 62 | -------------------------------------------------------------------------------- /swig/nordic_swig.i: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | 3 | #define NORDIC_API 4 | 5 | %include "gnuradio.i" // the common stuff 6 | 7 | //load generated python docstrings 8 | %include "nordic_swig_doc.i" 9 | 10 | %{ 11 | #include "nordic/nordic_rx.h" 12 | #include "nordic/nordic_tx.h" 13 | %} 14 | 15 | 16 | %include "nordic/nordic_rx.h" 17 | GR_SWIG_BLOCK_MAGIC2(nordic, nordic_rx); 18 | %include "nordic/nordic_tx.h" 19 | GR_SWIG_BLOCK_MAGIC2(nordic, nordic_tx); 20 | -------------------------------------------------------------------------------- /wireshark/nordic_dissector.lua: -------------------------------------------------------------------------------- 1 | -- trivial protocol example 2 | -- declare our protocol 3 | nordic_proto = Proto("nordic","Nordic Semiconductor nRF24L") 4 | -- create a function to dissect it 5 | function nordic_proto.dissector(buffer,pinfo,tree) 6 | pinfo.cols.protocol = "NORDIC" 7 | local subtree = tree:add(nordic_proto,buffer(),"nRF24L Packet") 8 | pinfo.cols.info = "" 9 | 10 | -- channel 11 | local channel = buffer(0,1):uint() 12 | 13 | -- data rate 14 | local data_rate = buffer(1,1):uint() 15 | local data_rate_string = "" 16 | if data_rate == 0 then data_rate_string = "250K" end 17 | if data_rate == 1 then data_rate_string = "1M" end 18 | if data_rate == 2 then data_rate_string = "2M" end 19 | 20 | -- address length 21 | local address_length = buffer(2,1):uint() 22 | 23 | -- payload length 24 | local payload_length = buffer(3,1):uint() 25 | 26 | -- sequence number 27 | local sequence_number = buffer(4,1):uint() 28 | 29 | -- no ack bit 30 | local no_ack = buffer(5,1):uint() 31 | 32 | -- crc length 33 | local crc_length = buffer(6,1):uint() 34 | 35 | -- address 36 | local address = buffer(7,address_length) 37 | local address_bytes = address:bytes() 38 | 39 | -- payload 40 | local payload = buffer(7+address_length,payload_length) 41 | local payload_bytes = payload:bytes() 42 | 43 | -- crc 44 | local crc = buffer(7+address_length+payload_length, crc_length) 45 | 46 | subtree:add(buffer(0,1), "Channel: " .. (2400+channel) .. "MHz") 47 | subtree:add(buffer(1,1), "Data Rate: " .. data_rate_string) 48 | subtree:add(buffer(2,1), "Address Length: " .. address_length) 49 | subtree:add(buffer(3,1), "Payload Length: " .. payload_length) 50 | subtree:add(buffer(4,1), "Sequence Number: " .. sequence_number) 51 | subtree:add(buffer(5,1), "No ACK: " .. no_ack) 52 | subtree:add(buffer(6,1), "CRC Length: " .. crc_length) 53 | subtree:add(buffer(7,address_length), "Address: " .. address) 54 | subtree:add(buffer(7+address_length,payload_length), "Payload: " .. payload) 55 | subtree:add(buffer(7+address_length+payload_length, crc_length), "CRC: " .. crc) 56 | 57 | -- Keepalive (vendor agnostic) 58 | if payload_bytes:len() == 0 then 59 | pinfo.cols.info = "ACK" 60 | end 61 | 62 | -- Microsoft 63 | if bit.band(buffer(7,1):uint(), 0xF0) == 0xA0 and payload_length > 0 then 64 | 65 | -- Validate the checksum (AES encrypted series) 66 | local sum = 0xFF 67 | for x = 0, payload_bytes:len()-1 do 68 | sum = bit.bxor(sum, payload_bytes:get_index(x)) 69 | end 70 | sum = bit.band(sum, 0xFF) 71 | if sum == 0 then 72 | 73 | -- Microsoft keepalive 74 | if payload_bytes:get_index(1) == 0x38 and payload_bytes:len() == 8 then 75 | pinfo.cols.info = "Keepalive" 76 | end 77 | 78 | -- Microsoft mouse movement/click 79 | if payload_bytes:get_index(1) == 0x90 then 80 | 81 | local vtree = subtree:add(nordic_proto, buffer(), "Microsoft Movement/Click") 82 | pinfo.cols.info = "Microsoft Mouse Movement/Click" 83 | vtree:add(string.format("Device Type: 0x%02x", payload_bytes:get_index(2))) 84 | 85 | -- Movement X/Y 86 | local movement_x = payload(9, 2):le_int() 87 | local movement_y = payload(11, 2):le_int() 88 | local scroll = payload(13, 2):le_int() 89 | vtree:add(string.format("Movement X: %i", movement_x)) 90 | vtree:add(string.format("Movement Y: %i", movement_y)) 91 | vtree:add(string.format("Scroll Wheel: %i", scroll)) 92 | 93 | -- Button mask 94 | local button_mask = payload(8, 1):uint() 95 | vtree:add(string.format("Button 0: %i", bit.band(bit.rshift(button_mask, 0), 1))) 96 | vtree:add(string.format("Button 1: %i", bit.band(bit.rshift(button_mask, 1), 1))) 97 | vtree:add(string.format("Button 2: %i", bit.band(bit.rshift(button_mask, 2), 1))) 98 | vtree:add(string.format("Button 3: %i", bit.band(bit.rshift(button_mask, 3), 1))) 99 | vtree:add(string.format("Button 4: %i", bit.band(bit.rshift(button_mask, 4), 1))) 100 | vtree:add(string.format("Button 5: %i", bit.band(bit.rshift(button_mask, 5), 1))) 101 | vtree:add(string.format("Button 6: %i", bit.band(bit.rshift(button_mask, 6), 1))) 102 | vtree:add(string.format("Button 7: %i", bit.band(bit.rshift(button_mask, 7), 1))) 103 | 104 | end 105 | 106 | end 107 | end 108 | 109 | end 110 | 111 | -- load the udp.port table 112 | udp_table = DissectorTable.get("udp.port") 113 | 114 | -- register our protocol to handle udp port 7777 115 | udp_table:add(9451,nordic_proto) --------------------------------------------------------------------------------