├── AUTHORS ├── CMakeLists.txt ├── COPYING ├── ChangeLog ├── INSTALL ├── NEWS ├── README ├── TODO ├── apps ├── CMakeLists.txt ├── RDS RX.grc └── rds_rx.py ├── cmake ├── Modules │ ├── CMakeParseArgumentsCopy.cmake │ ├── FindGnuradioCore.cmake │ ├── FindGruel.cmake │ ├── FindLibXml2.cmake │ ├── GrMiscUtils.cmake │ ├── GrPlatform.cmake │ ├── GrPython.cmake │ ├── GrSwig.cmake │ └── GrTest.cmake └── cmake_uninstall.cmake.in ├── doc ├── IEC 62106-E_no print.pdf ├── rds.odg ├── rds.png ├── tmc_event_tables.ods └── tmc_locations_italy.ods └── src ├── CMakeLists.txt ├── grc ├── CMakeLists.txt ├── install-grc.sh ├── rds_bpsk_demod.xml ├── rds_data_decoder.xml ├── rds_data_encoder.xml ├── rds_freq_divider.xml ├── rds_panel.xml └── rds_rate_enforcer.xml ├── lib ├── CMakeLists.txt ├── gr_rds_bpsk_demod.cc ├── gr_rds_bpsk_demod.h ├── gr_rds_constants.h ├── gr_rds_data_decoder.cc ├── gr_rds_data_decoder.h ├── gr_rds_data_encoder.cc ├── gr_rds_data_encoder.h ├── gr_rds_freq_divider.cc ├── gr_rds_freq_divider.h ├── gr_rds_rate_enforcer.cc ├── gr_rds_rate_enforcer.h ├── gr_rds_tmc_events.h ├── gr_rds_tmc_locations_italy.h └── rds.i ├── python ├── CMakeLists.txt ├── README ├── __init__.py ├── offline_rds_rx.py ├── qa_rds.py ├── rds_data.xml ├── rds_no_gui.py ├── rdspanel.py ├── run_tests.in ├── usrp_rds_rx.py └── usrp_rds_tx.py └── utils ├── create_vector.grc ├── create_vector.py ├── detect_usrps.py ├── fm_tx_mono.py ├── fm_tx_stereo.py ├── group_statistics.sh ├── install_usrp.sh ├── make_colorgcc_default.sh ├── offline_samples.py ├── rds_data.xml ├── rds_rx.grc ├── rds_rx.py ├── rds_syndrome.py ├── rds_tx.grc ├── rds_tx.py ├── rds_vector.dat ├── testbb.grc ├── testbb.py ├── tmc_event_statistics.sh └── tmc_location_statistics.sh /AUTHORS: -------------------------------------------------------------------------------- 1 | Ronnie Gaensli 2 | Ryan Shoff 3 | Matteo Campanella 4 | Dimitrios Symeonidis 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | 21 | ######################################################################## 22 | # Project setup 23 | ######################################################################## 24 | cmake_minimum_required(VERSION 2.6) 25 | #include(CheckCXXSymbolExists) 26 | #include(CheckFunctionExists) 27 | include(CheckLibraryExists) 28 | project(gr-rds CXX C) 29 | enable_testing() 30 | 31 | #select the release build type by default to get optimization flags 32 | if(NOT CMAKE_BUILD_TYPE) 33 | set(CMAKE_BUILD_TYPE "Release") 34 | message(STATUS "Build type not specified: defaulting to release.") 35 | endif(NOT CMAKE_BUILD_TYPE) 36 | set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") 37 | 38 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) 39 | 40 | ######################################################################## 41 | # Compiler specific setup 42 | ######################################################################## 43 | if(CMAKE_COMPILER_IS_GNUCXX AND NOT WIN32) 44 | #http://gcc.gnu.org/wiki/Visibility 45 | # add_definitions(-fvisibility=hidden) 46 | endif() 47 | 48 | ######################################################################## 49 | # Find boost 50 | ######################################################################## 51 | if(UNIX AND EXISTS "/usr/lib64") 52 | list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix 53 | endif(UNIX AND EXISTS "/usr/lib64") 54 | set(Boost_ADDITIONAL_VERSIONS 55 | "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" 56 | "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" 57 | "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" 58 | "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" 59 | "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" 60 | "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" 61 | "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" 62 | ) 63 | find_package(Boost "1.35") 64 | 65 | if(NOT Boost_FOUND) 66 | message(FATAL_ERROR "Boost required to compile gr-rds") 67 | endif() 68 | 69 | ######################################################################## 70 | # gr-rds specific 71 | ######################################################################## 72 | find_package(LibXml2 REQUIRED) 73 | 74 | if(LIBXML2_FOUND) 75 | include_directories(${LIBXML2_INCLUDE_DIR}) 76 | #link_directories(${LIBXML2_LIBRARIES}) 77 | endif () 78 | 79 | #list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBXML2_LIBRARIES}) 80 | #list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBXML2_INCLUDE_DIR}) 81 | #CHECK_CXX_SYMBOL_EXISTS(xmlParseFile "libxml/xmlIO.h" HAVE_XML_PARSE_FILE) # Requires both 82 | CHECK_LIBRARY_EXISTS(xml2 xmlParseFile "" HAVE_XML_PARSE_FILE) # Requires neither 83 | #CHECK_FUNCTION_EXISTS(xmlParseFile HAVE_XML_PARSE_FILE) # Only requires CMAKE_REQUIRED_LIBRARIES ${LIBXML2_LIBRARIES} 84 | if (NOT HAVE_XML_PARSE_FILE) 85 | message(FATAL_ERROR "xmlParseFile function not found in libxml2") 86 | endif () 87 | 88 | SET(RDS_PYTHON_SUBDIR "/gnuradio") 89 | 90 | ######################################################################## 91 | # Install directories 92 | ######################################################################## 93 | include(GrPlatform) #define LIB_SUFFIX 94 | set(GR_RUNTIME_DIR bin) 95 | set(GR_LIBRARY_DIR lib${LIB_SUFFIX}) 96 | set(GR_INCLUDE_DIR include) 97 | set(GR_DATA_DIR share) 98 | set(GR_PKG_DATA_DIR ${GR_DATA_DIR}/${CMAKE_PROJECT_NAME}) 99 | set(GR_DOC_DIR ${GR_DATA_DIR}/doc) 100 | set(GR_PKG_DOC_DIR ${GR_DOC_DIR}/${CMAKE_PROJECT_NAME}) 101 | set(GR_CONF_DIR etc) 102 | set(GR_PKG_CONF_DIR ${GR_CONF_DIR}/${CMAKE_PROJECT_NAME}/conf.d) 103 | set(GR_LIBEXEC_DIR libexec) 104 | set(GR_PKG_LIBEXEC_DIR ${GR_LIBEXEC_DIR}/${CMAKE_PROJECT_NAME}) 105 | set(GRC_BLOCKS_DIR ${GR_PKG_DATA_DIR}/grc/blocks) 106 | 107 | ######################################################################## 108 | # Find gnuradio build dependencies 109 | ######################################################################## 110 | find_package(Gruel) 111 | find_package(GnuradioCore) 112 | 113 | if(NOT GRUEL_FOUND) 114 | message(FATAL_ERROR "Gruel required to compile gr-rds") 115 | endif() 116 | 117 | if(NOT GNURADIO_CORE_FOUND) 118 | message(FATAL_ERROR "GnuRadio Core required to compile gr-rds") 119 | endif() 120 | 121 | ######################################################################## 122 | # Setup the include and linker paths 123 | ######################################################################## 124 | include_directories( 125 | ${CMAKE_SOURCE_DIR}/include 126 | ${Boost_INCLUDE_DIRS} 127 | ${GRUEL_INCLUDE_DIRS} 128 | ${GNURADIO_CORE_INCLUDE_DIRS} 129 | ) 130 | 131 | link_directories( 132 | ${Boost_LIBRARY_DIRS} 133 | ${GRUEL_LIBRARY_DIRS} 134 | ${GNURADIO_CORE_LIBRARY_DIRS} 135 | ) 136 | 137 | # Set component parameters 138 | set(GR_RDS_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE INTERNAL "" FORCE) 139 | set(GR_RDS_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/swig CACHE INTERNAL "" FORCE) 140 | 141 | ######################################################################## 142 | # Create uninstall target 143 | ######################################################################## 144 | configure_file( 145 | ${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in 146 | ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake 147 | @ONLY) 148 | 149 | add_custom_target(uninstall 150 | ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake 151 | ) 152 | 153 | ######################################################################## 154 | # Add subdirectories 155 | ######################################################################## 156 | add_subdirectory(src) 157 | #add_subdirectory(include) 158 | #add_subdirectory(lib) 159 | #add_subdirectory(swig) 160 | #add_subdirectory(python) 161 | #add_subdirectory(grc) 162 | add_subdirectory(apps) 163 | #add_subdirectory(docs) 164 | 165 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2006-07-21 Eric Blossom 2 | * doc/howto-write-a-block.xml: clarified 1:1 i/o relationship 3 | versus size of input and output items. Thanks Jens! 4 | 5 | 2005-07-02 Eric Blossom 6 | 7 | * config/gr_no_undefined.m4, config/gr_x86_64.m4: new, x86_64 support. 8 | * config/gr_python.m4: backed out search for libpython, making 9 | x86_64 work and breaking Cygwin/MinGW. 10 | * configure.ac, src/lib/Makefile.am: mods for x86_64, $(NO_UNDEFINED) 11 | 12 | 2005-05-09 Stephane Fillod 13 | 14 | * config/gr_sysv_shm.m4: SysV shared memory not mandatory 15 | * config/gr_pwin32.m4, config/gr_python.m4, config/lf_cxx.m4: 16 | fixes for Cygwin, MinGW 17 | 18 | 2005-01-29 Eric Blossom 19 | 20 | * src/lib/Makefile.am: mods for SWIG 1.3.24 21 | 22 | 2005-01-20 Eric Blossom 23 | 24 | * doc/howto-write-a-block.xml: made release 0.1 25 | 26 | # 27 | # Copyright 2005 Free Software Foundation, Inc. 28 | # 29 | # This file is part of GNU Radio 30 | # 31 | # GNU Radio is free software; you can redistribute it and/or modify 32 | # it under the terms of the GNU General Public License as published by 33 | # the Free Software Foundation; either version 2, or (at your option) 34 | # any later version. 35 | # 36 | # GNU Radio is distributed in the hope that it will be useful, 37 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 38 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 39 | # GNU General Public License for more details. 40 | # 41 | # You should have received a copy of the GNU General Public License 42 | # along with GNU Radio; see the file COPYING. If not, write to 43 | # the Free Software Foundation, Inc., 51 Franklin Street, 44 | # Boston, MA 02110-1301, USA. 45 | # 46 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | 23.06.2010 2 | - added tmc_locations_italy, in .ods and .h 3 | - bug fixes 4 | 5 | 18.06.2010 6 | - fixed offline samples storing 7 | - added offline samples decoding 8 | - fixed frequency normalization 9 | 10 | 14.06.2010 11 | - added TMC event table in .ods format 12 | - added TMC optional content decoding 13 | 14 | 10.05.2010 15 | - added TMC event table (ISO 14819-2, table 2) 16 | 17 | 03.03.2010 18 | - improved decoding of 3A, 8A groups 19 | - simple 8a group tx 20 | 21 | 02.03.2010 22 | - added decoding of 3A groups 23 | - added a few statistics utils 24 | 25 | 22.02.2010 26 | - corrections in rds_decoder (all group types) 27 | - separate gr_rds_constants.h file 28 | 29 | 21.02.2010 30 | - separate functions in rds_encoder for preparing each group 31 | - fixed udev rule in install_usrp.sh 32 | 33 | 19.02.2010 34 | - fixed clocktime generation (and a few bugs in reception) 35 | 36 | 18.02.2010 37 | - added pulse shaping filter 38 | - added rds_tx grc flowgraph 39 | - changed encoder structure to output >1 groups 40 | - added group type 2, 4 transmission 41 | 42 | 17.02.2010 43 | - added Manchester encoding to the transmitter 44 | - added a few more utils 45 | 46 | 11.02.2010 47 | - diff-encoding, nrz are again separate from rds-encoder 48 | - fixed grc blocks install script 49 | 50 | 10.02.2010 51 | - worked on rds_syndrome.py 52 | - fixed the order of syndromes, now the receiver loses sync much much less 53 | 54 | 09.02.2010 55 | - matched the filters in tx with those in rx 56 | - removed freq_divider from tx 57 | - separated rds encoder from rate enforcer in tx 58 | - updated .png, added .odg of the flowgraphs 59 | 60 | 03.02.2010 61 | - more fixes in rds_encoder 62 | - reinstated some copyright assignments I shouldn't have removed 63 | - expanded blks2.wfm_rcv_pll in usrp_rds_rx to avoid duplications 64 | 65 | 24.01.2010 66 | - added offset word to syndrome.py 67 | - added offset word to rds encoder 68 | - small correction in offset word order 69 | 70 | 23.01.2010 71 | - finalized the rds panel layout 72 | - added/fixed grc blocks, added documentation 73 | - the RDS clock signal looks cleaner after a bandpass rather than lowpass 74 | - added check for libxml2-dev in configure.ac (again) 75 | 76 | 22.01.2010 77 | - completely changed the logic of the rds_encoder, to already output data at the correct rate, thus removing the need for the rational resampler. 78 | - fixed the buffer preparation in the rds_encoder 79 | - removed the freq_statistics code that was temporarily stored here 80 | - fixed compilation with libxml-dev 81 | - corrected fm modulation maximum deviation 82 | - toying around with the rds panel layout 83 | 84 | 19.11.2009 85 | - added gain range to detect_usrps.py 86 | - renamed grc blocks 87 | - some more cleaning up 88 | 89 | 14.10.2009 90 | - changed grc blocks installation 91 | - some more cleaning up 92 | 93 | 16.04.2009 94 | - GRC blocks and rds_tx grc graph 95 | - changed encoder output from char to bool 96 | - separate grc folder with installer script 97 | 98 | 02.04.2009 99 | - some more cleaning up 100 | - set exact rate conversion for wav file input in usrp_rds_tx 101 | - set audio_rate = usrp_rate in usrp_rds_tx 102 | - add loopback testing python code (for testing w/o USRP) 103 | - draft grc block for rds_encoder 104 | 105 | 12.03.2009 106 | - fixed interpolating filter in usrp_rds_tx 107 | - some tuning attempts in usrp_rds_rx 108 | - reset rdspanel fields now only when re-tuning 109 | 110 | 04.03.2009 111 | - upgraded and tested for python2.6 112 | - simplified usrp_rds_tx.py 113 | - added emph tau param to blks2.wfm_rcv_pll in usrp_rds_rx.py 114 | - fixed rds_tx.png 115 | - updated TODO 116 | 117 | 27.02.2009 118 | - changed rds_data_encoder output from bool to char 119 | - changed the way the 1187.5bps rate is achieved 120 | - replaced bpsk_mod with gr.chunks_to_symbols[1, -1] (NRZ) and a multiplier 121 | - removed the diff_decoder, diff_encoder & bpsk_mod blocks 122 | - removed omnithread dependency 123 | 124 | 23.02.2009 125 | - corrected the pre-emphasis time constant: 50μsec for EU, 75μsec for US 126 | - added highpass filter for 38kHz stereo carrier (remove 0Hz tone) 127 | - added lowpass filter for 1187.5bps RDS data clock 128 | 129 | 12.02.2009 130 | - transform_char was equivalent to ASCII, so it was removed 131 | - Minor fixes to AF calculations 132 | - rds_data_encoder now creates group0 133 | 134 | 07.02.2009 135 | - Added assignment of xml values to variables. 136 | 137 | 06.02.2009 138 | - Added rds_data_encoder, so far it only parses an XML file 139 | 140 | 05.02.2009 141 | - Replaced rds.diff_decoder with gr.diff_decoder_bb 142 | - Some more cleaning up 143 | 144 | 02.02.2009 145 | - Fixed the existing QA code (freq_divider, diff_decoder) 146 | - Added diff_encoder (with QA) 147 | - Renamed biphase_decoder to bpsk_demod 148 | 149 | 19.12.2008 150 | New features: 151 | - Added mono and stereo FM broadcast python code. Next step: add RDS. 152 | 153 | 17.10.2008 154 | New features: 155 | - Added display of CT, AF to panel 156 | Miscellaneous: 157 | - Minor bug fixes 158 | - Cleaned up diff decoder 159 | 160 | 16.10.2008 161 | New features: 162 | - Alternate Frequency (AF) decoding 163 | - Completed the RadioText implementation 164 | - Clocktime (CT) decoding 165 | - Enhanced Other Networks (EON) decoding 166 | Draft features: 167 | - Slow labelling codes (type 1) decoding 168 | - Traffic Message Control (TMC) decoding 169 | Miscellaneous: 170 | - Added lots of comments, matching existing features with their descriptions in the standard(s). 171 | - Several code changes all over the place, to make the code more manageable. 172 | 173 | 26.07.2008 174 | This code was adobted from Matteo Campanella. It is still available at http://digilander.libero.it/iz2eeq/ It was updated to compile with the latest versions of GnuRadio. Small corrections were made to fix warning messages during compilation. All mc4020-related code was removed. 175 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2005, 2006 Free Software Foundation, Inc. 3 | # 4 | # This file is part of GNU Radio 5 | # 6 | # GNU Radio is free software; you can redistribute it and/or modify 7 | # it under the terms of the GNU General Public License as published by 8 | # the Free Software Foundation; either version 2, or (at your option) 9 | # any later version. 10 | # 11 | # GNU Radio is distributed in the hope that it will be useful, 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | # GNU General Public License for more details. 15 | # 16 | # You should have received a copy of the GNU General Public License 17 | # along with GNU Radio; see the file COPYING. If not, write to 18 | # the Free Software Foundation, Inc., 51 Franklin Street, 19 | # Boston, MA 02110-1301, USA. 20 | # 21 | 22 | This directory (and the resulting tarball) contains a build tree 23 | of the RDS (Radio Data System) block for GNU Radio. 24 | 25 | This package requires that gnuradio-core is already installed. It 26 | also depends on some GNU Radio prerequisites, such as boost. 27 | 28 | To build the examples from the tarball use the normal recipe: 29 | 30 | $ ./configure 31 | $ make 32 | $ make check 33 | 34 | If you're building from CVS, you'll need to use this sequence, since 35 | CVS doesn't contain configure or the generated Makefiles. 36 | 37 | $ ./bootstrap 38 | $ ./configure 39 | $ make 40 | $ make check 41 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | - Complete TMC implementation 2 | - Add scanning for stations 3 | - Replace bpsk_demod??? 4 | - Add error correction, not only detection 5 | - Integrate with OpenStreetMap??? 6 | -------------------------------------------------------------------------------- /apps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | include(GrPython) 21 | 22 | GR_PYTHON_INSTALL( 23 | PROGRAMS 24 | rds_rx.py 25 | DESTINATION bin 26 | ) 27 | 28 | -------------------------------------------------------------------------------- /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/FindGnuradioCore.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_GNURADIO_CORE gnuradio-core) 3 | 4 | FIND_PATH( 5 | GNURADIO_CORE_INCLUDE_DIRS 6 | NAMES gr_random.h 7 | HINTS $ENV{GNURADIO_CORE_DIR}/include/gnuradio 8 | ${PC_GNURADIO_CORE_INCLUDEDIR} 9 | PATHS /usr/local/include/gnuradio 10 | /usr/include/gnuradio 11 | ) 12 | 13 | FIND_LIBRARY( 14 | GNURADIO_CORE_LIBRARIES 15 | NAMES gnuradio-core 16 | HINTS $ENV{GNURADIO_CORE_DIR}/lib 17 | ${PC_GNURADIO_CORE_LIBDIR} 18 | PATHS /usr/local/lib 19 | /usr/local/lib64 20 | /usr/lib 21 | /usr/lib64 22 | ) 23 | 24 | INCLUDE(FindPackageHandleStandardArgs) 25 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_CORE DEFAULT_MSG GNURADIO_CORE_LIBRARIES GNURADIO_CORE_INCLUDE_DIRS) 26 | MARK_AS_ADVANCED(GNURADIO_CORE_LIBRARIES GNURADIO_CORE_INCLUDE_DIRS) 27 | -------------------------------------------------------------------------------- /cmake/Modules/FindGruel.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_GRUEL gruel) 3 | 4 | FIND_PATH( 5 | GRUEL_INCLUDE_DIRS 6 | NAMES gruel/attributes.h 7 | HINTS $ENV{GRUEL_DIR}/include 8 | ${PC_GRUEL_INCLUDEDIR} 9 | PATHS /usr/local/include 10 | /usr/include 11 | ) 12 | 13 | FIND_LIBRARY( 14 | GRUEL_LIBRARIES 15 | NAMES gruel 16 | HINTS $ENV{GRUEL_DIR}/lib 17 | ${PC_GRUEL_LIBDIR} 18 | PATHS /usr/local/lib 19 | /usr/local/lib64 20 | /usr/lib 21 | /usr/lib64 22 | ) 23 | 24 | INCLUDE(FindPackageHandleStandardArgs) 25 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GRUEL DEFAULT_MSG GRUEL_LIBRARIES GRUEL_INCLUDE_DIRS) 26 | MARK_AS_ADVANCED(GRUEL_LIBRARIES GRUEL_INCLUDE_DIRS) 27 | -------------------------------------------------------------------------------- /cmake/Modules/FindLibXml2.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the LibXml2 xml processing library 2 | # Once done this will define 3 | # 4 | # LIBXML2_FOUND - System has LibXml2 5 | # LIBXML2_INCLUDE_DIR - The LibXml2 include directory 6 | # LIBXML2_LIBRARIES - The libraries needed to use LibXml2 7 | # LIBXML2_DEFINITIONS - Compiler switches required for using LibXml2 8 | # LIBXML2_XMLLINT_EXECUTABLE - The XML checking tool xmllint coming with LibXml2 9 | 10 | #============================================================================= 11 | # Copyright 2006-2009 Kitware, Inc. 12 | # Copyright 2006 Alexander Neundorf 13 | # 14 | # Distributed under the OSI-approved BSD License (the "License"); 15 | # see accompanying file Copyright.txt for details. 16 | # 17 | # This software is distributed WITHOUT ANY WARRANTY; without even the 18 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 19 | # See the License for more information. 20 | #============================================================================= 21 | # (To distributed this file outside of CMake, substitute the full 22 | # License text for the above reference.) 23 | 24 | # use pkg-config to get the directories and then use these values 25 | # in the FIND_PATH() and FIND_LIBRARY() calls 26 | FIND_PACKAGE(PkgConfig) 27 | PKG_CHECK_MODULES(PC_LIBXML libxml-2.0) 28 | SET(LIBXML2_DEFINITIONS ${PC_LIBXML_CFLAGS_OTHER}) 29 | 30 | FIND_PATH(LIBXML2_INCLUDE_DIR NAMES libxml/xpath.h 31 | HINTS 32 | ${PC_LIBXML_INCLUDEDIR} 33 | ${PC_LIBXML_INCLUDE_DIRS} 34 | PATH_SUFFIXES libxml2 35 | ) 36 | 37 | FIND_LIBRARY(LIBXML2_LIBRARIES NAMES xml2 libxml2 38 | HINTS 39 | ${PC_LIBXML_LIBDIR} 40 | ${PC_LIBXML_LIBRARY_DIRS} 41 | ) 42 | 43 | FIND_PROGRAM(LIBXML2_XMLLINT_EXECUTABLE xmllint) 44 | # for backwards compat. with KDE 4.0.x: 45 | SET(XMLLINT_EXECUTABLE "${LIBXML2_XMLLINT_EXECUTABLE}") 46 | 47 | # handle the QUIETLY and REQUIRED arguments and set LIBXML2_FOUND to TRUE if 48 | # all listed variables are TRUE 49 | INCLUDE(FindPackageHandleStandardArgs) 50 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibXml2 DEFAULT_MSG LIBXML2_LIBRARIES LIBXML2_INCLUDE_DIR) 51 | 52 | MARK_AS_ADVANCED(LIBXML2_INCLUDE_DIR LIBXML2_LIBRARIES LIBXML2_XMLLINT_EXECUTABLE) 53 | 54 | -------------------------------------------------------------------------------- /cmake/Modules/GrMiscUtils.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | if(DEFINED __INCLUDED_GR_MISC_UTILS_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_MISC_UTILS_CMAKE TRUE) 24 | 25 | ######################################################################## 26 | # Set global variable macro. 27 | # Used for subdirectories to export settings. 28 | # Example: include and library paths. 29 | ######################################################################## 30 | function(GR_SET_GLOBAL var) 31 | set(${var} ${ARGN} CACHE INTERNAL "" FORCE) 32 | endfunction(GR_SET_GLOBAL) 33 | 34 | ######################################################################## 35 | # Set the pre-processor definition if the condition is true. 36 | # - def the pre-processor definition to set and condition name 37 | ######################################################################## 38 | function(GR_ADD_COND_DEF def) 39 | if(${def}) 40 | add_definitions(-D${def}) 41 | endif(${def}) 42 | endfunction(GR_ADD_COND_DEF) 43 | 44 | ######################################################################## 45 | # Check for a header and conditionally set a compile define. 46 | # - hdr the relative path to the header file 47 | # - def the pre-processor definition to set 48 | ######################################################################## 49 | function(GR_CHECK_HDR_N_DEF hdr def) 50 | include(CheckIncludeFileCXX) 51 | CHECK_INCLUDE_FILE_CXX(${hdr} ${def}) 52 | GR_ADD_COND_DEF(${def}) 53 | endfunction(GR_CHECK_HDR_N_DEF) 54 | 55 | ######################################################################## 56 | # Include subdirectory macro. 57 | # Sets the CMake directory variables, 58 | # includes the subdirectory CMakeLists.txt, 59 | # resets the CMake directory variables. 60 | # 61 | # This macro includes subdirectories rather than adding them 62 | # so that the subdirectory can affect variables in the level above. 63 | # This provides a work-around for the lack of convenience libraries. 64 | # This way a subdirectory can append to the list of library sources. 65 | ######################################################################## 66 | macro(GR_INCLUDE_SUBDIRECTORY subdir) 67 | #insert the current directories on the front of the list 68 | list(INSERT _cmake_source_dirs 0 ${CMAKE_CURRENT_SOURCE_DIR}) 69 | list(INSERT _cmake_binary_dirs 0 ${CMAKE_CURRENT_BINARY_DIR}) 70 | 71 | #set the current directories to the names of the subdirs 72 | set(CMAKE_CURRENT_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/${subdir}) 73 | set(CMAKE_CURRENT_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${subdir}) 74 | 75 | #include the subdirectory CMakeLists to run it 76 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 77 | include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt) 78 | 79 | #reset the value of the current directories 80 | list(GET _cmake_source_dirs 0 CMAKE_CURRENT_SOURCE_DIR) 81 | list(GET _cmake_binary_dirs 0 CMAKE_CURRENT_BINARY_DIR) 82 | 83 | #pop the subdir names of the front of the list 84 | list(REMOVE_AT _cmake_source_dirs 0) 85 | list(REMOVE_AT _cmake_binary_dirs 0) 86 | endmacro(GR_INCLUDE_SUBDIRECTORY) 87 | 88 | ######################################################################## 89 | # Check if a compiler flag works and conditionally set a compile define. 90 | # - flag the compiler flag to check for 91 | # - have the variable to set with result 92 | ######################################################################## 93 | macro(GR_ADD_CXX_COMPILER_FLAG_IF_AVAILABLE flag have) 94 | include(CheckCXXCompilerFlag) 95 | CHECK_CXX_COMPILER_FLAG(${flag} ${have}) 96 | if(${have}) 97 | add_definitions(${flag}) 98 | endif(${have}) 99 | endmacro(GR_ADD_CXX_COMPILER_FLAG_IF_AVAILABLE) 100 | 101 | ######################################################################## 102 | # Generates the .la libtool file 103 | # This appears to generate libtool files that cannot be used by auto*. 104 | # Usage GR_LIBTOOL(TARGET [target] DESTINATION [dest]) 105 | # Notice: there is not COMPONENT option, these will not get distributed. 106 | ######################################################################## 107 | function(GR_LIBTOOL) 108 | if(NOT DEFINED GENERATE_LIBTOOL) 109 | set(GENERATE_LIBTOOL OFF) #disabled by default 110 | endif() 111 | 112 | if(GENERATE_LIBTOOL) 113 | include(CMakeParseArgumentsCopy) 114 | CMAKE_PARSE_ARGUMENTS(GR_LIBTOOL "" "TARGET;DESTINATION" "" ${ARGN}) 115 | 116 | find_program(LIBTOOL libtool) 117 | if(LIBTOOL) 118 | include(CMakeMacroLibtoolFile) 119 | CREATE_LIBTOOL_FILE(${GR_LIBTOOL_TARGET} /${GR_LIBTOOL_DESTINATION}) 120 | endif(LIBTOOL) 121 | endif(GENERATE_LIBTOOL) 122 | 123 | endfunction(GR_LIBTOOL) 124 | 125 | ######################################################################## 126 | # Do standard things to the library target 127 | # - set target properties 128 | # - make install rules 129 | # Also handle gnuradio custom naming conventions w/ extras mode. 130 | ######################################################################## 131 | function(GR_LIBRARY_FOO target) 132 | #parse the arguments for component names 133 | include(CMakeParseArgumentsCopy) 134 | CMAKE_PARSE_ARGUMENTS(GR_LIBRARY "" "RUNTIME_COMPONENT;DEVEL_COMPONENT" "" ${ARGN}) 135 | 136 | #set additional target properties 137 | set_target_properties(${target} PROPERTIES SOVERSION ${LIBVER}) 138 | 139 | #install the generated files like so... 140 | install(TARGETS ${target} 141 | LIBRARY DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} # .so/.dylib file 142 | ARCHIVE DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_DEVEL_COMPONENT} # .lib file 143 | RUNTIME DESTINATION ${GR_RUNTIME_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} # .dll file 144 | ) 145 | 146 | #extras mode enabled automatically on linux 147 | if(NOT DEFINED LIBRARY_EXTRAS) 148 | set(LIBRARY_EXTRAS ${LINUX}) 149 | endif() 150 | 151 | #special extras mode to enable alternative naming conventions 152 | if(LIBRARY_EXTRAS) 153 | 154 | #create .la file before changing props 155 | GR_LIBTOOL(TARGET ${target} DESTINATION ${GR_LIBRARY_DIR}) 156 | 157 | #give the library a special name with ultra-zero soversion 158 | set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_NAME ${target}-${LIBVER} SOVERSION "0.0.0") 159 | set(target_name lib${target}-${LIBVER}.so.0.0.0) 160 | 161 | #custom command to generate symlinks 162 | add_custom_command( 163 | TARGET ${target} 164 | POST_BUILD 165 | COMMAND ${CMAKE_COMMAND} -E create_symlink ${target_name} ${CMAKE_CURRENT_BINARY_DIR}/lib${target}.so 166 | COMMAND ${CMAKE_COMMAND} -E create_symlink ${target_name} ${CMAKE_CURRENT_BINARY_DIR}/lib${target}-${LIBVER}.so.0 167 | COMMAND ${CMAKE_COMMAND} -E touch ${target_name} #so the symlinks point to something valid so cmake 2.6 will install 168 | ) 169 | 170 | #and install the extra symlinks 171 | install( 172 | FILES 173 | ${CMAKE_CURRENT_BINARY_DIR}/lib${target}.so 174 | ${CMAKE_CURRENT_BINARY_DIR}/lib${target}-${LIBVER}.so.0 175 | DESTINATION ${GR_LIBRARY_DIR} COMPONENT ${GR_LIBRARY_RUNTIME_COMPONENT} 176 | ) 177 | 178 | endif(LIBRARY_EXTRAS) 179 | endfunction(GR_LIBRARY_FOO) 180 | 181 | ######################################################################## 182 | # Create a dummy custom command that depends on other targets. 183 | # Usage: 184 | # GR_GEN_TARGET_DEPS(unique_name target_deps ...) 185 | # ADD_CUSTOM_COMMAND( ${target_deps}) 186 | # 187 | # Custom command cant depend on targets, but can depend on executables, 188 | # and executables can depend on targets. So this is the process: 189 | ######################################################################## 190 | function(GR_GEN_TARGET_DEPS name var) 191 | file( 192 | WRITE ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp.in 193 | "int main(void){return 0;}\n" 194 | ) 195 | execute_process( 196 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 197 | ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp.in 198 | ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp 199 | ) 200 | add_executable(${name} ${CMAKE_CURRENT_BINARY_DIR}/${name}.cpp) 201 | if(ARGN) 202 | add_dependencies(${name} ${ARGN}) 203 | endif(ARGN) 204 | 205 | if(CMAKE_CROSSCOMPILING) 206 | set(${var} "DEPENDS;${name}" PARENT_SCOPE) #cant call command when cross 207 | else() 208 | set(${var} "DEPENDS;${name};COMMAND;${name}" PARENT_SCOPE) 209 | endif() 210 | endfunction(GR_GEN_TARGET_DEPS) 211 | -------------------------------------------------------------------------------- /cmake/Modules/GrPlatform.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | if(DEFINED __INCLUDED_GR_PLATFORM_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_PLATFORM_CMAKE TRUE) 24 | 25 | ######################################################################## 26 | # Setup additional defines for OS types 27 | ######################################################################## 28 | if(CMAKE_SYSTEM_NAME STREQUAL "Linux") 29 | set(LINUX TRUE) 30 | endif() 31 | 32 | if(LINUX AND EXISTS "/etc/debian_version") 33 | set(DEBIAN TRUE) 34 | endif() 35 | 36 | if(LINUX AND EXISTS "/etc/redhat-release") 37 | set(REDHAT TRUE) 38 | endif() 39 | 40 | ######################################################################## 41 | # when the library suffix should be 64 (applies to redhat linux family) 42 | ######################################################################## 43 | if(NOT DEFINED LIB_SUFFIX AND REDHAT AND CMAKE_SYSTEM_PROCESSOR MATCHES "64$") 44 | set(LIB_SUFFIX 64) 45 | endif() 46 | set(LIB_SUFFIX ${LIB_SUFFIX} CACHE STRING "lib directory suffix") 47 | -------------------------------------------------------------------------------- /cmake/Modules/GrPython.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | if(DEFINED __INCLUDED_GR_PYTHON_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_PYTHON_CMAKE TRUE) 24 | 25 | ######################################################################## 26 | # Setup the python interpreter: 27 | # This allows the user to specify a specific interpreter, 28 | # or finds the interpreter via the built-in cmake module. 29 | ######################################################################## 30 | #this allows the user to override PYTHON_EXECUTABLE 31 | if(PYTHON_EXECUTABLE) 32 | 33 | set(PYTHONINTERP_FOUND TRUE) 34 | 35 | #otherwise if not set, try to automatically find it 36 | else(PYTHON_EXECUTABLE) 37 | 38 | #use the built-in find script 39 | find_package(PythonInterp) 40 | 41 | #and if that fails use the find program routine 42 | if(NOT PYTHONINTERP_FOUND) 43 | find_program(PYTHON_EXECUTABLE NAMES python python2.7 python2.6 python2.5) 44 | if(PYTHON_EXECUTABLE) 45 | set(PYTHONINTERP_FOUND TRUE) 46 | endif(PYTHON_EXECUTABLE) 47 | endif(NOT PYTHONINTERP_FOUND) 48 | 49 | endif(PYTHON_EXECUTABLE) 50 | 51 | #make the path to the executable appear in the cmake gui 52 | set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} CACHE FILEPATH "python interpreter") 53 | 54 | #make sure we can use -B with python (introduced in 2.6) 55 | if(PYTHON_EXECUTABLE) 56 | execute_process( 57 | COMMAND ${PYTHON_EXECUTABLE} -B -c "" 58 | OUTPUT_QUIET ERROR_QUIET 59 | RESULT_VARIABLE PYTHON_HAS_DASH_B_RESULT 60 | ) 61 | if(PYTHON_HAS_DASH_B_RESULT EQUAL 0) 62 | set(PYTHON_DASH_B "-B") 63 | endif() 64 | endif(PYTHON_EXECUTABLE) 65 | 66 | ######################################################################## 67 | # Check for the existence of a python module: 68 | # - desc a string description of the check 69 | # - mod the name of the module to import 70 | # - cmd an additional command to run 71 | # - have the result variable to set 72 | ######################################################################## 73 | macro(GR_PYTHON_CHECK_MODULE desc mod cmd have) 74 | message(STATUS "") 75 | message(STATUS "Python checking for ${desc}") 76 | execute_process( 77 | COMMAND ${PYTHON_EXECUTABLE} -c " 78 | ######################################### 79 | try: import ${mod} 80 | except: exit(-1) 81 | try: assert ${cmd} 82 | except: exit(-1) 83 | #########################################" 84 | RESULT_VARIABLE ${have} 85 | ) 86 | if(${have} EQUAL 0) 87 | message(STATUS "Python checking for ${desc} - found") 88 | set(${have} TRUE) 89 | else(${have} EQUAL 0) 90 | message(STATUS "Python checking for ${desc} - not found") 91 | set(${have} FALSE) 92 | endif(${have} EQUAL 0) 93 | endmacro(GR_PYTHON_CHECK_MODULE) 94 | 95 | ######################################################################## 96 | # Sets the python installation directory GR_PYTHON_DIR 97 | ######################################################################## 98 | execute_process(COMMAND ${PYTHON_EXECUTABLE} -c " 99 | from distutils import sysconfig 100 | print sysconfig.get_python_lib(plat_specific=True, prefix='') 101 | " OUTPUT_VARIABLE GR_PYTHON_DIR OUTPUT_STRIP_TRAILING_WHITESPACE 102 | ) 103 | file(TO_CMAKE_PATH ${GR_PYTHON_DIR} GR_PYTHON_DIR) 104 | 105 | ######################################################################## 106 | # Create an always-built target with a unique name 107 | # Usage: GR_UNIQUE_TARGET( ) 108 | ######################################################################## 109 | function(GR_UNIQUE_TARGET desc) 110 | file(RELATIVE_PATH reldir ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) 111 | execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import re, hashlib 112 | unique = hashlib.md5('${reldir}${ARGN}').hexdigest()[:5] 113 | print(re.sub('\\W', '_', '${desc} ${reldir} ' + unique))" 114 | OUTPUT_VARIABLE _target OUTPUT_STRIP_TRAILING_WHITESPACE) 115 | add_custom_target(${_target} ALL DEPENDS ${ARGN}) 116 | endfunction(GR_UNIQUE_TARGET) 117 | 118 | ######################################################################## 119 | # Install python sources (also builds and installs byte-compiled python) 120 | ######################################################################## 121 | function(GR_PYTHON_INSTALL) 122 | include(CMakeParseArgumentsCopy) 123 | CMAKE_PARSE_ARGUMENTS(GR_PYTHON_INSTALL "" "DESTINATION;COMPONENT" "FILES;PROGRAMS" ${ARGN}) 124 | 125 | #################################################################### 126 | if(GR_PYTHON_INSTALL_FILES) 127 | #################################################################### 128 | install(${ARGN}) #installs regular python files 129 | 130 | #create a list of all generated files 131 | unset(pysrcfiles) 132 | unset(pycfiles) 133 | unset(pyofiles) 134 | foreach(pyfile ${GR_PYTHON_INSTALL_FILES}) 135 | get_filename_component(pyfile ${pyfile} ABSOLUTE) 136 | list(APPEND pysrcfiles ${pyfile}) 137 | 138 | #determine if this file is in the source or binary directory 139 | file(RELATIVE_PATH source_rel_path ${CMAKE_CURRENT_SOURCE_DIR} ${pyfile}) 140 | string(LENGTH "${source_rel_path}" source_rel_path_len) 141 | file(RELATIVE_PATH binary_rel_path ${CMAKE_CURRENT_BINARY_DIR} ${pyfile}) 142 | string(LENGTH "${binary_rel_path}" binary_rel_path_len) 143 | 144 | #and set the generated path appropriately 145 | if(${source_rel_path_len} GREATER ${binary_rel_path_len}) 146 | set(pygenfile ${CMAKE_CURRENT_BINARY_DIR}/${binary_rel_path}) 147 | else() 148 | set(pygenfile ${CMAKE_CURRENT_BINARY_DIR}/${source_rel_path}) 149 | endif() 150 | list(APPEND pycfiles ${pygenfile}c) 151 | list(APPEND pyofiles ${pygenfile}o) 152 | 153 | #ensure generation path exists 154 | get_filename_component(pygen_path ${pygenfile} PATH) 155 | file(MAKE_DIRECTORY ${pygen_path}) 156 | 157 | endforeach(pyfile) 158 | 159 | #the command to generate the pyc files 160 | add_custom_command( 161 | DEPENDS ${pysrcfiles} OUTPUT ${pycfiles} 162 | COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_BINARY_DIR}/python_compile_helper.py ${pysrcfiles} ${pycfiles} 163 | ) 164 | 165 | #the command to generate the pyo files 166 | add_custom_command( 167 | DEPENDS ${pysrcfiles} OUTPUT ${pyofiles} 168 | COMMAND ${PYTHON_EXECUTABLE} -O ${CMAKE_BINARY_DIR}/python_compile_helper.py ${pysrcfiles} ${pyofiles} 169 | ) 170 | 171 | #create install rule and add generated files to target list 172 | set(python_install_gen_targets ${pycfiles} ${pyofiles}) 173 | install(FILES ${python_install_gen_targets} 174 | DESTINATION ${GR_PYTHON_INSTALL_DESTINATION} 175 | COMPONENT ${GR_PYTHON_INSTALL_COMPONENT} 176 | ) 177 | 178 | 179 | #################################################################### 180 | elseif(GR_PYTHON_INSTALL_PROGRAMS) 181 | #################################################################### 182 | file(TO_NATIVE_PATH ${PYTHON_EXECUTABLE} pyexe_native) 183 | 184 | foreach(pyfile ${GR_PYTHON_INSTALL_PROGRAMS}) 185 | get_filename_component(pyfile_name ${pyfile} NAME) 186 | get_filename_component(pyfile ${pyfile} ABSOLUTE) 187 | string(REPLACE "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" pyexefile "${pyfile}.exe") 188 | list(APPEND python_install_gen_targets ${pyexefile}) 189 | 190 | get_filename_component(pyexefile_path ${pyexefile} PATH) 191 | file(MAKE_DIRECTORY ${pyexefile_path}) 192 | 193 | add_custom_command( 194 | OUTPUT ${pyexefile} DEPENDS ${pyfile} 195 | COMMAND ${PYTHON_EXECUTABLE} -c 196 | \"open('${pyexefile}', 'w').write('\#!${pyexe_native}\\n'+open('${pyfile}').read())\" 197 | COMMENT "Shebangin ${pyfile_name}" 198 | ) 199 | 200 | #on windows, python files need an extension to execute 201 | get_filename_component(pyfile_ext ${pyfile} EXT) 202 | if(WIN32 AND NOT pyfile_ext) 203 | set(pyfile_name "${pyfile_name}.py") 204 | endif() 205 | 206 | install(PROGRAMS ${pyexefile} RENAME ${pyfile_name} 207 | DESTINATION ${GR_PYTHON_INSTALL_DESTINATION} 208 | COMPONENT ${GR_PYTHON_INSTALL_COMPONENT} 209 | ) 210 | endforeach(pyfile) 211 | 212 | endif() 213 | 214 | GR_UNIQUE_TARGET("pygen" ${python_install_gen_targets}) 215 | 216 | endfunction(GR_PYTHON_INSTALL) 217 | 218 | ######################################################################## 219 | # Write the python helper script that generates byte code files 220 | ######################################################################## 221 | file(WRITE ${CMAKE_BINARY_DIR}/python_compile_helper.py " 222 | import sys, py_compile 223 | files = sys.argv[1:] 224 | srcs, gens = files[:len(files)/2], files[len(files)/2:] 225 | for src, gen in zip(srcs, gens): 226 | py_compile.compile(file=src, cfile=gen, doraise=True) 227 | ") 228 | -------------------------------------------------------------------------------- /cmake/Modules/GrSwig.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | if(DEFINED __INCLUDED_GR_SWIG_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_SWIG_CMAKE TRUE) 24 | 25 | include(GrPython) 26 | 27 | ######################################################################## 28 | # Builds a swig documentation file to be generated into python docstrings 29 | # Usage: GR_SWIG_MAKE_DOCS(output_file input_path input_path....) 30 | # 31 | # Set the following variable to specify extra dependent targets: 32 | # - GR_SWIG_DOCS_SOURCE_DEPS 33 | # - GR_SWIG_DOCS_TARGET_DEPS 34 | ######################################################################## 35 | function(GR_SWIG_MAKE_DOCS output_file) 36 | find_package(Doxygen) 37 | if(DOXYGEN_FOUND) 38 | 39 | #setup the input files variable list, quote formated 40 | set(input_files) 41 | unset(INPUT_PATHS) 42 | foreach(input_path ${ARGN}) 43 | if (IS_DIRECTORY ${input_path}) #when input path is a directory 44 | file(GLOB input_path_h_files ${input_path}/*.h) 45 | else() #otherwise its just a file, no glob 46 | set(input_path_h_files ${input_path}) 47 | endif() 48 | list(APPEND input_files ${input_path_h_files}) 49 | set(INPUT_PATHS "${INPUT_PATHS} \"${input_path}\"") 50 | endforeach(input_path) 51 | 52 | #determine the output directory 53 | get_filename_component(name ${output_file} NAME_WE) 54 | get_filename_component(OUTPUT_DIRECTORY ${output_file} PATH) 55 | set(OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}/${name}_swig_docs) 56 | make_directory(${OUTPUT_DIRECTORY}) 57 | 58 | #generate the Doxyfile used by doxygen 59 | configure_file( 60 | ${CMAKE_SOURCE_DIR}/docs/doxygen/Doxyfile.swig_doc.in 61 | ${OUTPUT_DIRECTORY}/Doxyfile 62 | @ONLY) 63 | 64 | #Create a dummy custom command that depends on other targets 65 | include(GrMiscUtils) 66 | GR_GEN_TARGET_DEPS(_${name}_tag tag_deps ${GR_SWIG_DOCS_TARGET_DEPS}) 67 | 68 | #call doxygen on the Doxyfile + input headers 69 | add_custom_command( 70 | OUTPUT ${OUTPUT_DIRECTORY}/xml/index.xml 71 | DEPENDS ${input_files} ${GR_SWIG_DOCS_SOURCE_DEPS} ${tag_deps} 72 | COMMAND ${DOXYGEN_EXECUTABLE} ${OUTPUT_DIRECTORY}/Doxyfile 73 | COMMENT "Generating doxygen xml for ${name} docs" 74 | ) 75 | 76 | #call the swig_doc script on the xml files 77 | add_custom_command( 78 | OUTPUT ${output_file} 79 | DEPENDS ${input_files} ${OUTPUT_DIRECTORY}/xml/index.xml 80 | COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} 81 | ${CMAKE_SOURCE_DIR}/docs/doxygen/swig_doc.py 82 | ${OUTPUT_DIRECTORY}/xml 83 | ${output_file} 84 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs/doxygen 85 | ) 86 | 87 | else(DOXYGEN_FOUND) 88 | file(WRITE ${output_file} "\n") #no doxygen -> empty file 89 | endif(DOXYGEN_FOUND) 90 | endfunction(GR_SWIG_MAKE_DOCS) 91 | 92 | ######################################################################## 93 | # Build a swig target for the common gnuradio use case. Usage: 94 | # GR_SWIG_MAKE(target ifile ifile ifile...) 95 | # 96 | # Set the following variables before calling: 97 | # - GR_SWIG_FLAGS 98 | # - GR_SWIG_INCLUDE_DIRS 99 | # - GR_SWIG_LIBRARIES 100 | # - GR_SWIG_SOURCE_DEPS 101 | # - GR_SWIG_TARGET_DEPS 102 | # - GR_SWIG_DOC_FILE 103 | # - GR_SWIG_DOC_DIRS 104 | ######################################################################## 105 | macro(GR_SWIG_MAKE name) 106 | set(ifiles ${ARGN}) 107 | 108 | #do swig doc generation if specified 109 | if (GR_SWIG_DOC_FILE) 110 | set(GR_SWIG_DOCS_SOURCE_DEPS ${GR_SWIG_SOURCE_DEPS}) 111 | set(GR_SWIG_DOCS_TAREGT_DEPS ${GR_SWIG_TARGET_DEPS}) 112 | GR_SWIG_MAKE_DOCS(${GR_SWIG_DOC_FILE} ${GR_SWIG_DOC_DIRS}) 113 | list(APPEND GR_SWIG_SOURCE_DEPS ${GR_SWIG_DOC_FILE}) 114 | endif() 115 | 116 | #append additional include directories 117 | find_package(PythonLibs) 118 | list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_PATH}) #deprecated name (now dirs) 119 | list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS}) 120 | list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) 121 | list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) 122 | 123 | #determine include dependencies for swig file 124 | execute_process( 125 | COMMAND ${PYTHON_EXECUTABLE} 126 | ${CMAKE_BINARY_DIR}/get_swig_deps.py 127 | "${ifiles}" "${GR_SWIG_INCLUDE_DIRS}" 128 | OUTPUT_STRIP_TRAILING_WHITESPACE 129 | OUTPUT_VARIABLE SWIG_MODULE_${name}_EXTRA_DEPS 130 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 131 | ) 132 | 133 | #Create a dummy custom command that depends on other targets 134 | include(GrMiscUtils) 135 | GR_GEN_TARGET_DEPS(_${name}_swig_tag tag_deps ${GR_SWIG_TARGET_DEPS}) 136 | set(tag_file ${CMAKE_CURRENT_BINARY_DIR}/${name}.tag) 137 | add_custom_command( 138 | OUTPUT ${tag_file} 139 | DEPENDS ${GR_SWIG_SOURCE_DEPS} ${tag_deps} 140 | COMMAND ${CMAKE_COMMAND} -E touch ${tag_file} 141 | ) 142 | 143 | #append the specified include directories 144 | include_directories(${GR_SWIG_INCLUDE_DIRS}) 145 | list(APPEND SWIG_MODULE_${name}_EXTRA_DEPS ${tag_file}) 146 | 147 | #setup the swig flags with flags and include directories 148 | set(CMAKE_SWIG_FLAGS -fvirtual -modern -keyword -w511 -module ${name} ${GR_SWIG_FLAGS}) 149 | foreach(dir ${GR_SWIG_INCLUDE_DIRS}) 150 | list(APPEND CMAKE_SWIG_FLAGS "-I${dir}") 151 | endforeach(dir) 152 | 153 | #set the C++ property on the swig .i file so it builds 154 | set_source_files_properties(${ifiles} PROPERTIES CPLUSPLUS ON) 155 | 156 | #setup the actual swig library target to be built 157 | include(UseSWIG) 158 | SWIG_ADD_MODULE(${name} python ${ifiles}) 159 | SWIG_LINK_LIBRARIES(${name} ${PYTHON_LIBRARIES} ${GR_SWIG_LIBRARIES}) 160 | 161 | endmacro(GR_SWIG_MAKE) 162 | 163 | ######################################################################## 164 | # Install swig targets generated by GR_SWIG_MAKE. Usage: 165 | # GR_SWIG_INSTALL( 166 | # TARGETS target target target... 167 | # [DESTINATION destination] 168 | # [COMPONENT component] 169 | # ) 170 | ######################################################################## 171 | macro(GR_SWIG_INSTALL) 172 | 173 | include(CMakeParseArgumentsCopy) 174 | CMAKE_PARSE_ARGUMENTS(GR_SWIG_INSTALL "" "DESTINATION;COMPONENT" "TARGETS" ${ARGN}) 175 | 176 | foreach(name ${GR_SWIG_INSTALL_TARGETS}) 177 | install(TARGETS ${SWIG_MODULE_${name}_REAL_NAME} 178 | DESTINATION ${GR_SWIG_INSTALL_DESTINATION} 179 | COMPONENT ${GR_SWIG_INSTALL_COMPONENT} 180 | ) 181 | 182 | include(GrPython) 183 | GR_PYTHON_INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${name}.py 184 | DESTINATION ${GR_SWIG_INSTALL_DESTINATION} 185 | COMPONENT ${GR_SWIG_INSTALL_COMPONENT} 186 | ) 187 | 188 | GR_LIBTOOL( 189 | TARGET ${SWIG_MODULE_${name}_REAL_NAME} 190 | DESTINATION ${GR_SWIG_INSTALL_DESTINATION} 191 | ) 192 | 193 | endforeach(name) 194 | 195 | endmacro(GR_SWIG_INSTALL) 196 | 197 | ######################################################################## 198 | # Generate a python file that can determine swig dependencies. 199 | # Used by the make macro above to determine extra dependencies. 200 | # When you build C++, CMake figures out the header dependencies. 201 | # This code essentially performs that logic for swig includes. 202 | ######################################################################## 203 | file(WRITE ${CMAKE_BINARY_DIR}/get_swig_deps.py " 204 | 205 | import os, sys, re 206 | 207 | include_matcher = re.compile('[#|%]include\\s*[<|\"](.*)[>|\"]') 208 | include_dirs = sys.argv[2].split(';') 209 | 210 | def get_swig_incs(file_path): 211 | file_contents = open(file_path, 'r').read() 212 | return include_matcher.findall(file_contents, re.MULTILINE) 213 | 214 | def get_swig_deps(file_path, level): 215 | deps = [file_path] 216 | if level == 0: return deps 217 | for inc_file in get_swig_incs(file_path): 218 | for inc_dir in include_dirs: 219 | inc_path = os.path.join(inc_dir, inc_file) 220 | if not os.path.exists(inc_path): continue 221 | deps.extend(get_swig_deps(inc_path, level-1)) 222 | return deps 223 | 224 | if __name__ == '__main__': 225 | ifiles = sys.argv[1].split(';') 226 | deps = sum([get_swig_deps(ifile, 3) for ifile in ifiles], []) 227 | #sys.stderr.write(';'.join(set(deps)) + '\\n\\n') 228 | print(';'.join(set(deps))) 229 | ") 230 | -------------------------------------------------------------------------------- /cmake/Modules/GrTest.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | if(DEFINED __INCLUDED_GR_TEST_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_TEST_CMAKE TRUE) 24 | 25 | ######################################################################## 26 | # Add a unit test and setup the environment for a unit test. 27 | # Takes the same arguments as the ADD_TEST function. 28 | # 29 | # Before calling set the following variables: 30 | # GR_TEST_TARGET_DEPS - built targets for the library path 31 | # GR_TEST_LIBRARY_DIRS - directories for the library path 32 | # GR_TEST_PYTHON_DIRS - directories for the python path 33 | ######################################################################## 34 | function(GR_ADD_TEST test_name) 35 | 36 | if(WIN32) 37 | #Ensure that the build exe also appears in the PATH. 38 | list(APPEND GR_TEST_TARGET_DEPS ${ARGN}) 39 | 40 | #In the land of windows, all libraries must be in the PATH. 41 | #Since the dependent libraries are not yet installed, 42 | #we must manually set them in the PATH to run tests. 43 | #The following appends the path of a target dependency. 44 | foreach(target ${GR_TEST_TARGET_DEPS}) 45 | get_target_property(location ${target} LOCATION) 46 | if(location) 47 | get_filename_component(path ${location} PATH) 48 | string(REGEX REPLACE "\\$\\(.*\\)" ${CMAKE_BUILD_TYPE} path ${path}) 49 | list(APPEND GR_TEST_LIBRARY_DIRS ${path}) 50 | endif(location) 51 | endforeach(target) 52 | 53 | #SWIG generates the python library files into a subdirectory. 54 | #Therefore, we must append this subdirectory into PYTHONPATH. 55 | #Only do this for the python directories matching the following: 56 | foreach(pydir ${GR_TEST_PYTHON_DIRS}) 57 | get_filename_component(name ${pydir} NAME) 58 | if(name MATCHES "^(swig|lib|src)$") 59 | list(APPEND GR_TEST_PYTHON_DIRS ${pydir}/${CMAKE_BUILD_TYPE}) 60 | endif() 61 | endforeach(pydir) 62 | endif(WIN32) 63 | 64 | file(TO_NATIVE_PATH ${CMAKE_CURRENT_SOURCE_DIR} srcdir) 65 | file(TO_NATIVE_PATH "${GR_TEST_LIBRARY_DIRS}" libpath) #ok to use on dir list? 66 | file(TO_NATIVE_PATH "${GR_TEST_PYTHON_DIRS}" pypath) #ok to use on dir list? 67 | 68 | set(environs "GR_DONT_LOAD_PREFS=1" "srcdir=${srcdir}") 69 | 70 | #http://www.cmake.org/pipermail/cmake/2009-May/029464.html 71 | #Replaced this add test + set environs code with the shell script generation. 72 | #Its nicer to be able to manually run the shell script to diagnose problems. 73 | #ADD_TEST(${ARGV}) 74 | #SET_TESTS_PROPERTIES(${test_name} PROPERTIES ENVIRONMENT "${environs}") 75 | 76 | if(UNIX) 77 | set(binpath "${CMAKE_CURRENT_BINARY_DIR}:$PATH") 78 | #set both LD and DYLD paths to cover multiple UNIX OS library paths 79 | list(APPEND libpath "$LD_LIBRARY_PATH" "$DYLD_LIBRARY_PATH") 80 | list(APPEND pypath "$PYTHONPATH") 81 | 82 | #replace list separator with the path separator 83 | string(REPLACE ";" ":" libpath "${libpath}") 84 | string(REPLACE ";" ":" pypath "${pypath}") 85 | list(APPEND environs "PATH=${binpath}" "LD_LIBRARY_PATH=${libpath}" "DYLD_LIBRARY_PATH=${libpath}" "PYTHONPATH=${pypath}") 86 | 87 | #generate a bat file that sets the environment and runs the test 88 | find_program(SHELL sh) 89 | set(sh_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.sh) 90 | file(WRITE ${sh_file} "#!${SHELL}\n") 91 | #each line sets an environment variable 92 | foreach(environ ${environs}) 93 | file(APPEND ${sh_file} "export ${environ}\n") 94 | endforeach(environ) 95 | #load the command to run with its arguments 96 | foreach(arg ${ARGN}) 97 | file(APPEND ${sh_file} "${arg} ") 98 | endforeach(arg) 99 | file(APPEND ${sh_file} "\n") 100 | 101 | #make the shell file executable 102 | execute_process(COMMAND chmod +x ${sh_file}) 103 | 104 | add_test(${test_name} ${SHELL} ${sh_file}) 105 | 106 | endif(UNIX) 107 | 108 | if(WIN32) 109 | list(APPEND libpath ${DLL_PATHS} "%PATH%") 110 | list(APPEND pypath "%PYTHONPATH%") 111 | 112 | #replace list separator with the path separator (escaped) 113 | string(REPLACE ";" "\\;" libpath "${libpath}") 114 | string(REPLACE ";" "\\;" pypath "${pypath}") 115 | list(APPEND environs "PATH=${libpath}" "PYTHONPATH=${pypath}") 116 | 117 | #generate a bat file that sets the environment and runs the test 118 | set(bat_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.bat) 119 | file(WRITE ${bat_file} "@echo off\n") 120 | #each line sets an environment variable 121 | foreach(environ ${environs}) 122 | file(APPEND ${bat_file} "SET ${environ}\n") 123 | endforeach(environ) 124 | #load the command to run with its arguments 125 | foreach(arg ${ARGN}) 126 | file(APPEND ${bat_file} "${arg} ") 127 | endforeach(arg) 128 | file(APPEND ${bat_file} "\n") 129 | 130 | add_test(${test_name} ${bat_file}) 131 | endif(WIN32) 132 | 133 | endfunction(GR_ADD_TEST) 134 | -------------------------------------------------------------------------------- /cmake/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 | -------------------------------------------------------------------------------- /doc/IEC 62106-E_no print.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balint256/gr-rds/83bc203d8d68168f6305b9af994e7ab2e82d0330/doc/IEC 62106-E_no print.pdf -------------------------------------------------------------------------------- /doc/rds.odg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balint256/gr-rds/83bc203d8d68168f6305b9af994e7ab2e82d0330/doc/rds.odg -------------------------------------------------------------------------------- /doc/rds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balint256/gr-rds/83bc203d8d68168f6305b9af994e7ab2e82d0330/doc/rds.png -------------------------------------------------------------------------------- /doc/tmc_event_tables.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balint256/gr-rds/83bc203d8d68168f6305b9af994e7ab2e82d0330/doc/tmc_event_tables.ods -------------------------------------------------------------------------------- /doc/tmc_locations_italy.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balint256/gr-rds/83bc203d8d68168f6305b9af994e7ab2e82d0330/doc/tmc_locations_italy.ods -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(grc) 2 | add_subdirectory(lib) 3 | add_subdirectory(python) 4 | 5 | -------------------------------------------------------------------------------- /src/grc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | install(FILES 21 | rds_bpsk_demod.xml 22 | rds_data_decoder.xml 23 | rds_data_encoder.xml 24 | rds_freq_divider.xml 25 | rds_rate_enforcer.xml 26 | rds_panel.xml 27 | DESTINATION share/gnuradio/grc/blocks 28 | ) 29 | 30 | -------------------------------------------------------------------------------- /src/grc/install-grc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | [ -a ~/.grc_gnuradio ] || mkdir ~/.grc_gnuradio 3 | install *.xml -t ~/.grc_gnuradio 4 | -------------------------------------------------------------------------------- /src/grc/rds_bpsk_demod.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | RDS BPSK Demodulator 9 | gr_rds_bpsk_demod 10 | RDS 11 | from gnuradio import gr, rds 12 | rds.bpsk_demod($sampling_rate) 13 | reset();$reset; 14 | 15 | Sampling Rate 16 | sampling_rate 17 | 256000 18 | real 19 | 20 | 21 | Reset 22 | reset 23 | 0 24 | raw 25 | part 26 | 27 | $sampling_rate > 0 28 | 29 | in 30 | float 31 | 32 | 33 | clock 34 | float 35 | 36 | 37 | out 38 | byte 39 | 40 | 41 | This is a BPSK demodulator and RDS sync'er. 42 | 43 | It takes as inputs an RDS clock (1187.5 Hz) and the baseband RDS signal, \ 44 | syncs them, does the BPSK-demodulation and outputs the bits of the \ 45 | RDS data stream (still diff-encoded). 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/grc/rds_data_decoder.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | RDS Data Sink 9 | gr_rds_data_decoder 10 | RDS 11 | from gnuradio import gr, rds 12 | rds.data_decoder($(id)_msgq_out) 13 | reset();$reset; 14 | 20 | 21 | Reset 22 | reset 23 | 0 24 | raw 25 | part 26 | 27 | 28 | in 29 | byte 30 | gr.sizeof_char 31 | 32 | 33 | out 34 | msg 35 | 36 | 37 | This block is the RDS data decoder. 38 | 39 | It takes as input a bitstream of RDS data after DBPSK-demodulation, in 40 | bytes containing single bits [0, 1]. It then does the framing, checksumming, 41 | and decoding of the data, which it then sends to a message gueue. 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/grc/rds_data_encoder.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | RDS Data Source 9 | gr_rds_data_encoder 10 | RDS 11 | from gnuradio import gr, rds 12 | rds.data_encoder($xmlfile) 13 | 14 | 15 | File 16 | xmlfile 17 | 18 | file_open 19 | 20 | len($xmlfile) > 0 21 | 22 | out 23 | byte 24 | 25 | 26 | This source block creates the baseband RDS signal. 27 | 28 | It reads its configuration from an XML file; composes the infowords; 29 | calculates checkwords; merges the two into blocks and then groups; 30 | and streams out the resulting buffer. 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/grc/rds_freq_divider.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | RDS Frequency Divider 9 | gr_rds_freq_divider 10 | RDS 11 | from gnuradio import gr, rds 12 | rds.freq_divider($divider) 13 | 14 | 15 | Divider 16 | divider 17 | 16 18 | int 19 | 20 | $divider > 0 21 | 22 | in 23 | float 24 | 25 | 26 | out 27 | float 28 | 29 | 30 | This block divides the frequency carried by the parameter given, by \ 31 | counting zero crossings. 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/grc/rds_panel.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | RDS Panel 9 | gr_rds_panel 10 | RDS 11 | from gnuradio import gr, rds 12 | #set $parent = $notebook() and 'self.%s.GetPage(%s)'%$notebook() or 'self' 13 | rds.rdsPanel($(id)_msgq_in, $freq, $(parent).GetWin()) 14 | #if not $grid_pos() 15 | $(parent).Add(self.$(id)) 16 | #else 17 | $(parent).GridAdd(self.$(id), $(', '.join(map(str, $grid_pos())))) 18 | #end if 19 | clear_data();$reset; 20 | set_frequency($freq); 21 | 22 | 23 | Frequency 24 | freq 25 | 0 26 | real 27 | part 28 | 29 | 30 | 31 | Reset 32 | reset 33 | 0 34 | raw 35 | part 36 | 37 | 38 | 45 | 46 | 47 | Grid Position 48 | grid_pos 49 | 50 | grid_pos 51 | 52 | 53 | 54 | Notebook 55 | notebook 56 | 57 | notebook 58 | 59 | 60 | 61 | 62 | 63 | in 64 | msg 65 | 66 | Any change in Reset parameter will trigger clearing of panel data. 67 | Frequency will manually set corresponding text on panel. 68 | 69 | -------------------------------------------------------------------------------- /src/grc/rds_rate_enforcer.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | RDS Rate Enforcer 9 | gr_rds_rate_enforcer 10 | RDS 11 | from gnuradio import gr, rds 12 | rds.rate_enforcer($sampling_rate) 13 | 14 | 15 | Sampling Rate 16 | sampling_rate 17 | 256000 18 | real 19 | 20 | $sampling_rate > 0 21 | 22 | data 23 | float 24 | 25 | 26 | clock 27 | float 28 | 29 | 30 | out 31 | float 32 | 33 | 34 | This block enforces the RDS data rate of 1187.5bps 35 | 36 | Input "Data" is an RDS bitstream (1 sample per symbol); Input "Clock" 37 | is a 19kHz sampled at the desired sampling rate. The output runs at 38 | the Clock's sampling rate carrying the same RDS bitstream ("Data") 39 | with a data rate of 1187.5bps. 40 | 41 | This is done by pushing the next RDS bit after 32 zero-crossings in the 42 | clock. 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | ######################################################################## 21 | # Setup library 22 | ######################################################################## 23 | include(GrPlatform) #define LIB_SUFFIX 24 | 25 | add_library(gnuradio-rds SHARED 26 | gr_rds_bpsk_demod.cc 27 | gr_rds_data_decoder.cc 28 | gr_rds_data_encoder.cc 29 | gr_rds_rate_enforcer.cc 30 | gr_rds_freq_divider.cc 31 | ) 32 | target_link_libraries(gnuradio-rds ${Boost_LIBRARIES} ${GRUEL_LIBRARIES} ${GNURADIO_CORE_LIBRARIES} ${LIBXML2_LIBRARIES}) 33 | set_target_properties(gnuradio-rds PROPERTIES DEFINE_SYMBOL "gnuradio_rds_EXPORTS") 34 | 35 | ######################################################################## 36 | # Install built library files 37 | ######################################################################## 38 | install(TARGETS gnuradio-rds 39 | LIBRARY DESTINATION lib${LIB_SUFFIX} # .so/.dylib file 40 | ARCHIVE DESTINATION lib${LIB_SUFFIX} # .lib file 41 | RUNTIME DESTINATION bin # .dll file 42 | ) 43 | 44 | ######################################################################## 45 | # Build and register unit test 46 | ######################################################################## 47 | find_package(Boost COMPONENTS unit_test_framework) 48 | 49 | include(GrTest) 50 | set(GR_TEST_TARGET_DEPS gnuradio-rds) 51 | #turn each test cpp file into an executable with an int main() function 52 | add_definitions(-DBOOST_TEST_DYN_LINK -DBOOST_TEST_MAIN) 53 | 54 | #add_executable(qa_howto_square_ff qa_howto_square_ff.cc) 55 | #target_link_libraries(qa_howto_square_ff gnuradio-howto ${Boost_LIBRARIES}) 56 | #GR_ADD_TEST(qa_howto_square_ff qa_howto_square_ff) 57 | 58 | #add_executable(qa_howto_square2_ff qa_howto_square2_ff.cc) 59 | #target_link_libraries(qa_howto_square2_ff gnuradio-howto ${Boost_LIBRARIES}) 60 | #GR_ADD_TEST(qa_howto_square2_ff qa_howto_square2_ff) 61 | 62 | ######################################################################## 63 | # Python/SWIG ########################################################## 64 | ######################################################################## 65 | 66 | ######################################################################## 67 | # Include swig generation macros 68 | ######################################################################## 69 | find_package(SWIG) 70 | find_package(PythonLibs) 71 | if(NOT SWIG_FOUND OR NOT PYTHONLIBS_FOUND) 72 | return() 73 | endif() 74 | include(GrSwig) 75 | include(GrPython) 76 | 77 | ######################################################################## 78 | # Setup swig generation 79 | ######################################################################## 80 | foreach(incdir ${GNURADIO_CORE_INCLUDE_DIRS}) 81 | list(APPEND GR_SWIG_INCLUDE_DIRS ${incdir}/swig) 82 | endforeach(incdir) 83 | 84 | foreach(incdir ${GRUEL_INCLUDE_DIRS}) 85 | list(APPEND GR_SWIG_INCLUDE_DIRS ${incdir}/gruel/swig) 86 | endforeach(incdir) 87 | 88 | set(GR_SWIG_LIBRARIES gnuradio-rds) 89 | #set(GR_SWIG_DOC_FILE ${CMAKE_CURRENT_BINARY_DIR}/rds_doc.i) # howto_rds_doc 90 | #set(GR_SWIG_DOC_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/../include) 91 | 92 | GR_SWIG_MAKE(rds rds.i) # rds_swig rds_swig.i 93 | 94 | ######################################################################## 95 | # Install the build swig module 96 | ######################################################################## 97 | GR_SWIG_INSTALL(TARGETS rds DESTINATION ${GR_PYTHON_DIR}${RDS_PYTHON_SUBDIR}/rds) # rds_swig 98 | 99 | ######################################################################## 100 | # Install swig .i files for development 101 | ######################################################################## 102 | install( 103 | FILES 104 | rds.i # rds_swig.i 105 | # ${CMAKE_CURRENT_BINARY_DIR}/rds_doc.i # rds_swig_doc.i 106 | DESTINATION ${GR_INCLUDE_DIR}/rds/swig # Not adding ${RDS_PYTHON_SUBDIR} here 107 | ) 108 | 109 | -------------------------------------------------------------------------------- /src/lib/gr_rds_bpsk_demod.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2004 Free Software Foundation, Inc. 4 | * 5 | * This file is part of GNU Radio 6 | * 7 | * GNU Radio is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2, or (at your option) 10 | * any later version. 11 | * 12 | * GNU Radio is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with GNU Radio; see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | /* 24 | * config.h is generated by configure. It contains the results 25 | * of probing for features, options etc. It should be the first 26 | * file included in your .cc file. 27 | */ 28 | #ifdef HAVE_CONFIG_H 29 | #include "config.h" 30 | #endif 31 | 32 | #ifdef DEBUG 33 | #define DBG(x) x 34 | #else 35 | #define DBG(x) 36 | #endif 37 | 38 | #include 39 | #include 40 | 41 | #include 42 | 43 | /* 44 | * Create a new instance of gr_rds_bpsk_demod and return 45 | * a boost shared_ptr. This is effectively the public constructor. 46 | */ 47 | gr_rds_bpsk_demod_sptr gr_rds_make_bpsk_demod (double sampling_rate) 48 | { 49 | return gr_rds_bpsk_demod_sptr (new gr_rds_bpsk_demod (sampling_rate)); 50 | } 51 | 52 | /* 53 | * The private constructor 54 | */ 55 | gr_rds_bpsk_demod::gr_rds_bpsk_demod (double input_sampling_rate) 56 | : gr_block("gr_rds_bpsk_demod", 57 | gr_make_io_signature (2, 2, sizeof(float)), 58 | gr_make_io_signature (1, 8, sizeof(bool))) 59 | { 60 | SYMBOL_LENGTH = (int)(input_sampling_rate/1187.5); 61 | 62 | /* set approximate output_rate/input_rate 63 | * for the buffer allocator and the scheduler */ 64 | set_relative_rate(1/SYMBOL_LENGTH); 65 | reset(); 66 | } 67 | 68 | /* 69 | * Our virtual destructor. 70 | */ 71 | gr_rds_bpsk_demod::~gr_rds_bpsk_demod (){ 72 | } 73 | 74 | void gr_rds_bpsk_demod::reset() { 75 | d_zc = 0; 76 | d_last_zc=0; 77 | d_sign_last = 0; 78 | synccounter=0; 79 | enter_looking(); 80 | } 81 | 82 | void gr_rds_bpsk_demod::enter_looking () { 83 | printf (">>> bpsk demodulator enter_looking\n"); 84 | d_state = ST_LOOKING; 85 | } 86 | 87 | void gr_rds_bpsk_demod::enter_locked () { 88 | printf(">>> bpsk demodulator enter_locked\n"); 89 | d_state = ST_LOCKED; 90 | d_symbol_integrator = 0; 91 | d_sign_last = 0; 92 | } 93 | 94 | 95 | //////////////////////////////////////////////////////////////// 96 | int gr_rds_bpsk_demod::general_work (int noutput_items, 97 | gr_vector_int &ninput_items, 98 | gr_vector_const_void_star &input_items, 99 | gr_vector_void_star &output_items) 100 | { 101 | const float *in = (const float *) input_items[0]; 102 | const float *clk = (const float *) input_items[1]; 103 | bool *out = (bool *) output_items[0]; 104 | int n_in = ninput_items[0]; 105 | int n_clk_in = ninput_items[1]; 106 | int i = 0; 107 | int nout = 0; 108 | int cons=0; 109 | int sign_current = 0; 110 | 111 | switch (d_state){ 112 | case ST_LOCKED: 113 | if(d_sign_last == 0) d_sign_last = (clk[0]>0?1:-1); 114 | for(i=0; (i0?1:-1); 116 | if(sign_current != d_sign_last) d_zc++; // a zero cross in clk 117 | d_sign_last = sign_current; 118 | d_symbol_integrator += (in[i]*clk[i]); 119 | if(d_zc >= 2) { 120 | // Two zero crossings in clock - that's a symbol 121 | out[nout] = (d_symbol_integrator>0?1:0); 122 | nout++; 123 | d_symbol_integrator = 0; 124 | d_zc = 0; 125 | } 126 | } 127 | consume_each (i); 128 | return nout; 129 | case ST_LOOKING: 130 | // adjust clock and signal; if fine: go locked 131 | if(d_sign_last == 0) d_sign_last = (in[0]>0?1:-1); 132 | for(i=0; i0?1:-1); 134 | if(sign_current != d_sign_last) { 135 | // Remember the zero crossing and check next time 136 | // if it was a half or a whole symbol 137 | if(d_last_zc!=0) { 138 | int delta = i-d_last_zc; 139 | if(abs(delta-SYMBOL_LENGTH)<10) { 140 | // That was a 1, 0 or 0, 1 in signal 141 | // i is now pointing at the middle of a symbol 142 | consume(0, i-(delta/2)); 143 | i=i-(delta/2); 144 | d_sign_last = (clk[i]>0?1:-1); 145 | for (; i0?1:-1); 147 | if(sign_current!=d_sign_last) { 148 | // zero crossing in clock 149 | consume(1,i); 150 | break; 151 | } 152 | d_sign_last = sign_current; 153 | if (synccounter++==100) enter_locked(); 154 | return 0; 155 | // No output produced, but now clock and signal are synced 156 | } 157 | } 158 | d_last_zc = i; 159 | } 160 | d_sign_last = sign_current; 161 | } 162 | } 163 | d_last_zc = d_last_zc-n_in; 164 | cons = (n_in>n_clk_in?n_clk_in:n_in); 165 | consume_each(cons); 166 | return 0; 167 | default: 168 | enter_looking(); 169 | return 0; 170 | break; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/lib/gr_rds_bpsk_demod.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2004 Free Software Foundation, Inc. 4 | * 5 | * This file is part of GNU Radio 6 | * 7 | * GNU Radio is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2, or (at your option) 10 | * any later version. 11 | * 12 | * GNU Radio is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with GNU Radio; see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | /* HSR - MOBKOM LABOR 24 | * Semesterarbeit GnuRadio Contributions 25 | * U. Schaufelberger and R. Gaensli 26 | * 27 | * Written by : Ronnie Gaensli 28 | * Created: 2005/05 29 | * 30 | */ 31 | 32 | 33 | #ifndef INCLUDED_gr_rds_bpsk_demod_H 34 | #define INCLUDED_gr_rds_bpsk_demod_H 35 | 36 | #include 37 | #include 38 | #include 39 | 40 | class gr_rds_bpsk_demod; 41 | 42 | /* 43 | * We use boost::shared_ptr's instead of raw pointers for all access 44 | * to gr_blocks (and many other data structures). The shared_ptr gets 45 | * us transparent reference counting, which greatly simplifies storage 46 | * management issues. This is especially helpful in our hybrid 47 | * 48 | * * C++ / Python system. 49 | * 50 | * See http://www.boost.org/libs/smart_ptr/smart_ptr.htm 51 | * 52 | * As a convention, the _sptr suffix indicates a boost::shared_ptr 53 | */ 54 | typedef boost::shared_ptr gr_rds_bpsk_demod_sptr; 55 | 56 | /*! 57 | * \brief Return a shared_ptr to a new instance of gr_rds_bpsk_demod. 58 | * 59 | * To avoid accidental use of raw pointers, gr_rds_bpsk_demod's 60 | * constructor is private. gr_rds_make_bpsk_demod is the public 61 | * interface for creating new instances. 62 | */ 63 | gr_rds_bpsk_demod_sptr gr_rds_make_bpsk_demod (double input_sample_rate); 64 | 65 | /*! 66 | * \brief Decodes a biphase or manchester coded signal to 1, 0 as bool 67 | * \ingroup RDS 68 | */ 69 | class gr_rds_bpsk_demod : public gr_block 70 | { 71 | private: 72 | enum state_t { ST_LOOKING, ST_LOCKED }; 73 | state_t d_state; 74 | int SYMBOL_LENGTH; 75 | int d_zc; // Zero crosses in clk 76 | int d_last_zc; 77 | int d_sign_last; 78 | float d_symbol_integrator; 79 | unsigned int synccounter; 80 | 81 | // The friend declaration allows gr_rds_make_bpsk_demod to 82 | // access the private constructor. 83 | friend gr_rds_bpsk_demod_sptr gr_rds_make_bpsk_demod (double input_sample_rate); 84 | gr_rds_bpsk_demod (double input_samping_rate); // private constructor 85 | void enter_looking(); 86 | void enter_locked(); 87 | 88 | public: 89 | ~gr_rds_bpsk_demod (); 90 | int general_work (int noutput_items, 91 | gr_vector_int &ninput_items, 92 | gr_vector_const_void_star &input_items, 93 | gr_vector_void_star &output_items); 94 | void reset(); 95 | }; 96 | 97 | #endif /* INCLUDED_gr_rds_bpsk_demod_H */ 98 | -------------------------------------------------------------------------------- /src/lib/gr_rds_constants.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2004 Free Software Foundation, Inc. 4 | * 5 | * This file is part of GNU Radio 6 | * 7 | * GNU Radio is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2, or (at your option) 10 | * any later version. 11 | * 12 | * GNU Radio is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with GNU Radio; see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | 24 | /* see page 59, Annex C, table C.1 in the standard 25 | * offset word C' has been put at the end */ 26 | static const unsigned int offset_pos[5]={0,1,2,3,2}; 27 | static const unsigned int offset_word[5]={252,408,360,436,848}; 28 | static const unsigned int syndrome[5]={383,14,303,663,748}; 29 | static const char * const offset_name[]={"A","B","C","D","C'"}; 30 | 31 | /* page 77, Annex F in the standard */ 32 | const std::string pty_table[32]={ 33 | "None", 34 | "News", 35 | "Current Affairs", 36 | "Information", 37 | "Sport", 38 | "Education", 39 | "Drama", 40 | "Cultures", 41 | "Science", 42 | "Varied Speech", 43 | "Pop Music", 44 | "Rock Music", 45 | "Easy Listening", 46 | "Light Classics M", 47 | "Serious Classics", 48 | "Other Music", 49 | "Weather & Metr", 50 | "Finance", 51 | "Children’s Progs", 52 | "Social Affairs", 53 | "Religion", 54 | "Phone In", 55 | "Travel & Touring", 56 | "Leisure & Hobby", 57 | "Jazz Music", 58 | "Country Music", 59 | "National Music", 60 | "Oldies Music", 61 | "Folk Music", 62 | "Documentary", 63 | "Alarm Test", 64 | "Alarm-Alarm!"}; 65 | 66 | /* page 71, Annex D, table D.1 in the standard */ 67 | const std::string pi_country_codes[15][5]={ 68 | {"DE","GR","MA","__","MD"}, 69 | {"DZ","CY","CZ","IE","EE"}, 70 | {"AD","SM","PL","TR","__"}, 71 | {"IL","CH","VA","MK","__"}, 72 | {"IT","JO","SK","__","__"}, 73 | {"BE","FI","SY","__","UA"}, 74 | {"RU","LU","TN","__","__"}, 75 | {"PS","BG","__","NL","PT"}, 76 | {"AL","DK","LI","LV","SI"}, 77 | {"AT","GI","IS","LB","__"}, 78 | {"HU","IQ","MC","__","__"}, 79 | {"MT","GB","LT","HR","__"}, 80 | {"DE","LY","YU","__","__"}, 81 | {"__","RO","ES","SE","__"}, 82 | {"EG","FR","NO","BY","BA"}}; 83 | 84 | /* page 72, Annex D, table D.2 in the standard */ 85 | const std::string coverage_area_codes[16]={ 86 | "Local", 87 | "International", 88 | "National", 89 | "Supra-regional", 90 | "Regional 1", 91 | "Regional 2", 92 | "Regional 3", 93 | "Regional 4", 94 | "Regional 5", 95 | "Regional 6", 96 | "Regional 7", 97 | "Regional 8", 98 | "Regional 9", 99 | "Regional 10", 100 | "Regional 11", 101 | "Regional 12"}; 102 | 103 | const std::string rds_group_acronyms[16]={ 104 | "BASIC", 105 | "PIN/SL", 106 | "RT", 107 | "AID", 108 | "CT", 109 | "TDC", 110 | "IH", 111 | "RP", 112 | "TMC", 113 | "EWS", 114 | "___", 115 | "___", 116 | "___", 117 | "___", 118 | "EON", 119 | "___"}; 120 | 121 | /* page 74, Annex E, table E.1 in the standard: that's the ASCII table!!! */ 122 | 123 | /* see page 84, Annex J in the standard */ 124 | const std::string language_codes[44]={ 125 | "Unkown/not applicable", 126 | "Albanian", 127 | "Breton", 128 | "Catalan", 129 | "Croatian", 130 | "Welsh", 131 | "Czech", 132 | "Danish", 133 | "German", 134 | "English", 135 | "Spanish", 136 | "Esperanto", 137 | "Estonian", 138 | "Basque", 139 | "Faroese", 140 | "French", 141 | "Frisian", 142 | "Irish", 143 | "Gaelic", 144 | "Galician", 145 | "Icelandic", 146 | "Italian", 147 | "Lappish", 148 | "Latin", 149 | "Latvian", 150 | "Luxembourgian", 151 | "Lithuanian", 152 | "Hungarian", 153 | "Maltese", 154 | "Dutch", 155 | "Norwegian", 156 | "Occitan", 157 | "Polish", 158 | "Portuguese", 159 | "Romanian", 160 | "Romansh", 161 | "Serbian", 162 | "Slovak", 163 | "Slovene", 164 | "Finnish", 165 | "Swedish", 166 | "Turkish", 167 | "Flemish", 168 | "Walloon"}; 169 | 170 | /* see page 12 in ISO 14819-1 */ 171 | const std::string tmc_duration[8][2]={ 172 | {"no duration given", "no duration given"}, 173 | {"15 minutes", "next few hours"}, 174 | {"30 minutes", "rest of the day"}, 175 | {"1 hour", "until tomorrow evening"}, 176 | {"2 hours", "rest of the week"}, 177 | {"3 hours", "end of next week"}, 178 | {"4 hours", "end of the month"}, 179 | {"rest of the day", "long period"}}; 180 | 181 | /* optional message content, data field lengths and labels 182 | * see page 15 in ISO 14819-1 */ 183 | const int optional_content_lengths[16]={3,3,5,5,5,8,8,8,8,11,16,16,16,16,0,0}; 184 | 185 | const std::string label_descriptions[16]={ 186 | "Duration", 187 | "Control code", 188 | "Length of route affected", 189 | "Speed limit advice", 190 | "Quantifier", 191 | "Quantifier", 192 | "Supplementary information code", 193 | "Explicit start time", 194 | "Explicit stop time", 195 | "Additional event", 196 | "Detailed diversion instructions", 197 | "Destination", 198 | "RFU (Reserved for future use)", 199 | "Cross linkage to source of problem, or another route", 200 | "Separator", 201 | "RFU (Reserved for future use)"}; 202 | -------------------------------------------------------------------------------- /src/lib/gr_rds_data_decoder.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2004 Free Software Foundation, Inc. 4 | * 5 | * This file is part of GNU Radio 6 | * 7 | * GNU Radio is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2, or (at your option) 10 | * any later version. 11 | * 12 | * GNU Radio is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with GNU Radio; see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | /* HSR - MOBKOM LABOR 24 | * Semesterarbeit GnuRadio Contributions 25 | * U. Schaufelberger and R. Gaensli 26 | * 27 | * Heavily modified by Dimitrios Symeonidis 28 | */ 29 | 30 | 31 | #ifndef INCLUDED_gr_rds_data_decoder_H 32 | #define INCLUDED_gr_rds_data_decoder_H 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | 42 | class gr_rds_data_decoder; 43 | 44 | /* 45 | * We use boost::shared_ptr's instead of raw pointers for all access 46 | * to gr_blocks (and many other data structures). The shared_ptr gets 47 | * us transparent reference counting, which greatly simplifies storage 48 | * management issues. This is especially helpful in our hybrid 49 | * 50 | * * C++ / Python system. 51 | * 52 | * See http://www.boost.org/libs/smart_ptr/smart_ptr.htm 53 | * 54 | * As a convention, the _sptr suffix indicates a boost::shared_ptr 55 | */ 56 | typedef boost::shared_ptr gr_rds_data_decoder_sptr; 57 | gr_rds_data_decoder_sptr gr_rds_make_data_decoder (gr_msg_queue_sptr msgq); 58 | 59 | class gr_rds_data_decoder : public gr_sync_block 60 | { 61 | private: 62 | unsigned long bit_counter, lastseen_offset_counter, reg; 63 | unsigned char lastseen_offset, block_number; 64 | unsigned int block_bit_counter, wrong_blocks_counter, blocks_counter, group_good_blocks_counter; 65 | bool presync,good_block, group_assembly_started; 66 | unsigned int group[4]; 67 | enum state_t { ST_NO_SYNC, ST_SYNC }; 68 | state_t d_state; 69 | char radiotext[65]; 70 | char clocktime_string[33]; 71 | char af1_string[10]; 72 | char af2_string[10]; 73 | char af_string[21]; 74 | bool radiotext_AB_flag; 75 | bool traffic_program; 76 | bool traffic_announcement; 77 | bool music_speech; 78 | bool mono_stereo; 79 | bool artificial_head; 80 | bool compressed; 81 | bool static_pty; 82 | unsigned char program_type; 83 | unsigned int program_identification; 84 | unsigned char pi_country_identification; 85 | unsigned char pi_area_coverage; 86 | unsigned char pi_program_reference_number; 87 | char program_service_name[9]; 88 | gr_msg_queue_sptr d_msgq; 89 | 90 | // Functions 91 | friend gr_rds_data_decoder_sptr gr_rds_make_data_decoder (gr_msg_queue_sptr); 92 | gr_rds_data_decoder (gr_msg_queue_sptr); // private constructor 93 | void enter_no_sync(); 94 | void enter_sync(unsigned int); 95 | void reset_rds_data(); 96 | unsigned int calc_syndrome(unsigned long, unsigned char); 97 | unsigned long bin2dec(char*); 98 | void send_message(long, std::string); 99 | void decode_group(unsigned int*); 100 | double decode_af(unsigned int); 101 | void decode_optional_content(int, unsigned long int *); 102 | void decode_type0(unsigned int*, bool); 103 | void decode_type1(unsigned int*, bool); 104 | void decode_type2(unsigned int*, bool); 105 | void decode_type3a(unsigned int*); 106 | void decode_type4a(unsigned int*); 107 | void decode_type8a(unsigned int*); 108 | void decode_type14(unsigned int*, bool); 109 | void decode_type15b(unsigned int*); 110 | 111 | 112 | public: 113 | ~gr_rds_data_decoder(); // public destructor 114 | void reset(void); 115 | int work (int noutput_items, 116 | gr_vector_const_void_star &input_items, 117 | gr_vector_void_star &output_items); 118 | }; 119 | 120 | #endif /* INCLUDED_gr_rds_data_decoder_H */ 121 | -------------------------------------------------------------------------------- /src/lib/gr_rds_data_encoder.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2004 Free Software Foundation, Inc. 4 | * 5 | * This file is part of GNU Radio 6 | * 7 | * GNU Radio is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2, or (at your option) 10 | * any later version. 11 | * 12 | * GNU Radio is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with GNU Radio; see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | 24 | #ifndef INCLUDED_gr_rds_data_encoder_H 25 | #define INCLUDED_gr_rds_data_encoder_H 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | /* adds dependency: libxml2-dev */ 32 | #include 33 | #include 34 | #include 35 | 36 | class gr_rds_data_encoder; 37 | 38 | /* 39 | * We use boost::shared_ptr's instead of raw pointers for all access 40 | * to gr_blocks (and many other data structures). The shared_ptr gets 41 | * us transparent reference counting, which greatly simplifies storage 42 | * management issues. This is especially helpful in our hybrid 43 | * 44 | * * C++ / Python system. 45 | * 46 | * See http://www.boost.org/libs/smart_ptr/smart_ptr.htm 47 | * 48 | * As a convention, the _sptr suffix indicates a boost::shared_ptr 49 | */ 50 | typedef boost::shared_ptr gr_rds_data_encoder_sptr; 51 | /*! 52 | * \ingroup RDS 53 | */ 54 | gr_rds_data_encoder_sptr gr_rds_make_data_encoder (const char *xmlfile); 55 | 56 | class gr_rds_data_encoder : public gr_sync_block 57 | { 58 | private: 59 | unsigned int infoword[4]; 60 | unsigned int checkword[4]; 61 | unsigned int block[4]; 62 | unsigned char **buffer; 63 | 64 | // FIXME make this a struct (or a class) 65 | unsigned int PI; 66 | bool TP; 67 | unsigned char PTY; 68 | bool TA; 69 | bool MuSp; 70 | bool MS; 71 | bool AH; 72 | bool compressed; 73 | bool static_pty; 74 | double AF1; 75 | double AF2; 76 | char PS[8]; 77 | unsigned char radiotext[64]; 78 | int DP; 79 | int extent; 80 | int event; 81 | int location; 82 | 83 | /* each type 0 group contains 2 out of 8 PS characters; 84 | * this is used to count 0..3 and send all PS characters */ 85 | int d_g0_counter; 86 | /* each type 2A group contains 4 out of 64 RadioText characters; 87 | * each type 2B group contains 2 out of 32 RadioText characters; 88 | * this is used to count 0..15 and send all RadioText characters */ 89 | int d_g2_counter; 90 | /* points to the current buffer being prepared/streamed 91 | * used in create_group() and in work() */ 92 | int d_current_buffer; 93 | /* loops through the buffer, pushing out the symbols */ 94 | int d_buffer_bit_counter; 95 | /* 0..16+A/B = 32 groups. 0A is groups[0], 0B is groups[16]. use %16. 96 | * ==0 means group not present, ==1 means group present */ 97 | int ngroups; 98 | int groups[32]; 99 | /* nbuffers might be != ngroups, e.g. group 0A needs 4 buffers */ 100 | int nbuffers; 101 | 102 | // Functions 103 | friend gr_rds_data_encoder_sptr gr_rds_make_data_encoder (const char*); 104 | gr_rds_data_encoder (const char*); // private constructor 105 | int read_xml(const char*); 106 | void print_element_names(xmlNode*); 107 | void assign_from_xml(const char*, const char*, const int); 108 | void reset_rds_data(); 109 | void count_groups(); 110 | void create_group(const int, const bool); 111 | void prepare_group0(const bool); 112 | void prepare_group2(const bool); 113 | void prepare_group4a(); 114 | void prepare_group8a(); 115 | void prepare_buffer(int); 116 | unsigned int encode_af(double); 117 | unsigned int calc_syndrome(unsigned long, unsigned char); 118 | 119 | public: 120 | ~gr_rds_data_encoder(); // public destructor 121 | int work (int noutput_items, 122 | gr_vector_const_void_star &input_items, 123 | gr_vector_void_star &output_items); 124 | }; 125 | 126 | #endif /* INCLUDED_gr_rds_data_encoder_H */ 127 | -------------------------------------------------------------------------------- /src/lib/gr_rds_freq_divider.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2004 Free Software Foundation, Inc. 4 | * 5 | * This file is part of GNU Radio 6 | * 7 | * GNU Radio is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2, or (at your option) 10 | * any later version. 11 | * 12 | * GNU Radio is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with GNU Radio; see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | 24 | /* 25 | * config.h is generated by configure. It contains the results 26 | * of probing for features, options etc. It should be the first 27 | * file included in your .cc file. 28 | */ 29 | #ifdef HAVE_CONFIG_H 30 | #include "config.h" 31 | #endif 32 | 33 | #include 34 | #include 35 | 36 | /* 37 | * Create a new instance of gr_rds_freq_divider and return 38 | * a boost shared_ptr. This is effectively the public constructor. 39 | */ 40 | gr_rds_freq_divider_sptr gr_rds_make_freq_divider (int divider) 41 | { 42 | return gr_rds_freq_divider_sptr (new gr_rds_freq_divider (divider)); 43 | } 44 | 45 | /* 46 | * The private constructor 47 | */ 48 | gr_rds_freq_divider::gr_rds_freq_divider (int divider) 49 | : gr_sync_block ("gr_rds_freq_divider", 50 | gr_make_io_signature (1, 1, sizeof (float)), 51 | gr_make_io_signature (1, 1, sizeof (float))) 52 | { 53 | d_divider = 0; 54 | DIVIDER = divider; 55 | d_sign_last = d_sign_current = false; 56 | d_out = 1; 57 | } 58 | 59 | /* 60 | * Our virtual destructor. 61 | */ 62 | gr_rds_freq_divider::~gr_rds_freq_divider (){ 63 | } 64 | 65 | int 66 | gr_rds_freq_divider::work (int noutput_items, 67 | gr_vector_const_void_star &input_items, 68 | gr_vector_void_star &output_items) 69 | { 70 | const float *in = (const float *) input_items[0]; 71 | float *out = (float *) output_items[0]; 72 | 73 | for (int i = 0; i < noutput_items; i++){ 74 | d_sign_current = (in[i] > 0 ? true : false); 75 | if(d_sign_current != d_sign_last) { // A zero cross 76 | if(++d_divider == DIVIDER) { 77 | d_out *= -1; 78 | d_divider = 0; 79 | } 80 | } 81 | out[i] = d_out; 82 | d_sign_last = d_sign_current; 83 | } 84 | 85 | // Tell runtime system how many output items we produced. 86 | return noutput_items; 87 | } 88 | -------------------------------------------------------------------------------- /src/lib/gr_rds_freq_divider.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2004 Free Software Foundation, Inc. 4 | * 5 | * This file is part of GNU Radio 6 | * 7 | * GNU Radio is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2, or (at your option) 10 | * any later version. 11 | * 12 | * GNU Radio is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with GNU Radio; see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | /* HSR - MOBKOM LABOR 24 | * Semesterarbeit GnuRadio Contributions 25 | * U. Schaufelberger and R. Gaensli 26 | * 27 | * Written by : WRITTEN_BY 28 | * Created: CREATED_DATE 29 | * 30 | */ 31 | 32 | 33 | #ifndef INCLUDED_gr_rds_freq_divider_H 34 | #define INCLUDED_gr_rds_freq_divider_H 35 | 36 | #include 37 | 38 | class gr_rds_freq_divider; 39 | 40 | /* 41 | * We use boost::shared_ptr's instead of raw pointers for all access 42 | * to gr_blocks (and many other data structures). The shared_ptr gets 43 | * us transparent reference counting, which greatly simplifies storage 44 | * management issues. This is especially helpful in our hybrid 45 | * 46 | * * C++ / Python system. 47 | * 48 | * See http://www.boost.org/libs/smart_ptr/smart_ptr.htm 49 | * 50 | * As a convention, the _sptr suffix indicates a boost::shared_ptr 51 | */ 52 | typedef boost::shared_ptr gr_rds_freq_divider_sptr; 53 | 54 | /*! 55 | * \ingroup RDS 56 | */ 57 | gr_rds_freq_divider_sptr gr_rds_make_freq_divider (int divider); 58 | 59 | class gr_rds_freq_divider : public gr_sync_block 60 | { 61 | private: 62 | // The friend declaration allows gr_rds_make_freq_divider to 63 | // access the private constructor. 64 | friend gr_rds_freq_divider_sptr gr_rds_make_freq_divider (int divider); 65 | 66 | int d_divider; 67 | int DIVIDER; 68 | float d_out; 69 | bool d_sign_current, d_sign_last; 70 | 71 | gr_rds_freq_divider (int divider); // private constructor 72 | 73 | public: 74 | ~gr_rds_freq_divider (); // public destructor 75 | int work (int noutput_items, 76 | gr_vector_const_void_star &input_items, 77 | gr_vector_void_star &output_items); 78 | }; 79 | 80 | #endif /* INCLUDED_gr_rds_freq_divider_H */ 81 | -------------------------------------------------------------------------------- /src/lib/gr_rds_rate_enforcer.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2004 Free Software Foundation, Inc. 4 | * 5 | * This file is part of GNU Radio 6 | * 7 | * GNU Radio is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2, or (at your option) 10 | * any later version. 11 | * 12 | * GNU Radio is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with GNU Radio; see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | 24 | /* 25 | * This block enforces the RDS data rate of 1187.5bps 26 | * 27 | * Input "Data" is an RDS bitstream (1 sample per symbol); Input "Clock" 28 | * is a 19kHz sampled at the desired sampling rate. The output runs at 29 | * the Clock's sampling rate carrying the same RDS bitstream ("Data") 30 | * with a data rate of 1187.5bps. 31 | * 32 | * This is done by pushing the next RDS bit after 32 zero-crossings in the 33 | * clock. 34 | */ 35 | 36 | 37 | #ifdef HAVE_CONFIG_H 38 | #include "config.h" 39 | #endif 40 | 41 | //#define DEBUG 42 | 43 | #ifdef DEBUG 44 | #define DBG(x) x 45 | #else 46 | #define DBG(x) 47 | #endif 48 | 49 | #include 50 | #include 51 | #include 52 | 53 | gr_rds_rate_enforcer_sptr gr_rds_make_rate_enforcer (double samp_rate) { 54 | return gr_rds_rate_enforcer_sptr (new gr_rds_rate_enforcer (samp_rate)); 55 | } 56 | 57 | gr_rds_rate_enforcer::gr_rds_rate_enforcer (double samp_rate) 58 | : gr_block ("gr_rds_rate_enforcer", 59 | gr_make_io_signature (2, 2, sizeof(float)), 60 | gr_make_io_signature (1, 1, sizeof(float))) 61 | { 62 | set_relative_rate(samp_rate/1187.5); 63 | } 64 | 65 | gr_rds_rate_enforcer::~gr_rds_rate_enforcer () { 66 | 67 | } 68 | 69 | int gr_rds_rate_enforcer::general_work (int noutput_items, 70 | gr_vector_int &ninput_items, 71 | gr_vector_const_void_star &input_items, 72 | gr_vector_void_star &output_items) 73 | { 74 | const float *data = (const float *) input_items[0]; 75 | const float *clock = (const float *) input_items[1]; 76 | float *out = (float *) output_items[0]; 77 | 78 | int sign_current=0; 79 | int current_out=0; 80 | 81 | static int symlen=0; // symbol length 82 | static int zero_cross=0; // count zero-crossings 83 | static int sign_last=(clock[0]>0?1:-1); 84 | 85 | for(int i=0; i0?1:-1); 88 | if(sign_current!=sign_last){ 89 | if(++zero_cross>15){ // push next bit 90 | current_out++; 91 | DBG(printf("%f (len=%i)", data[current_out], symlen);) 92 | zero_cross=symlen=0; 93 | } 94 | } 95 | out[i]=data[current_out]; 96 | sign_last=sign_current; 97 | } 98 | 99 | consume(0, current_out); 100 | consume(1, noutput_items); 101 | return noutput_items; 102 | } 103 | -------------------------------------------------------------------------------- /src/lib/gr_rds_rate_enforcer.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2004 Free Software Foundation, Inc. 4 | * 5 | * This file is part of GNU Radio 6 | * 7 | * GNU Radio is free software; you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation; either version 2, or (at your option) 10 | * any later version. 11 | * 12 | * GNU Radio is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with GNU Radio; see the file COPYING. If not, write to 19 | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | * Boston, MA 02111-1307, USA. 21 | */ 22 | 23 | 24 | #ifndef INCLUDED_gr_rds_rate_enforcer_H 25 | #define INCLUDED_gr_rds_rate_enforcer_H 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | class gr_rds_rate_enforcer; 32 | 33 | /* 34 | * We use boost::shared_ptr's instead of raw pointers for all access 35 | * to gr_blocks (and many other data structures). The shared_ptr gets 36 | * us transparent reference counting, which greatly simplifies storage 37 | * management issues. This is especially helpful in our hybrid 38 | * 39 | * * C++ / Python system. 40 | * 41 | * See http://www.boost.org/libs/smart_ptr/smart_ptr.htm 42 | * 43 | * As a convention, the _sptr suffix indicates a boost::shared_ptr 44 | */ 45 | typedef boost::shared_ptr gr_rds_rate_enforcer_sptr; 46 | /*! 47 | * \ingroup RDS 48 | */ 49 | gr_rds_rate_enforcer_sptr gr_rds_make_rate_enforcer (double samp_rate); 50 | 51 | class gr_rds_rate_enforcer : public gr_block 52 | { 53 | private: 54 | // Functions 55 | friend gr_rds_rate_enforcer_sptr gr_rds_make_rate_enforcer (double samp_rate); 56 | gr_rds_rate_enforcer (double samp_rate); // private constructor 57 | 58 | public: 59 | ~gr_rds_rate_enforcer(); // public destructor 60 | int general_work (int noutput_items, 61 | gr_vector_int &ninput_items, 62 | gr_vector_const_void_star &input_items, 63 | gr_vector_void_star &output_items); 64 | }; 65 | 66 | #endif /* INCLUDED_gr_rds_rate_enforcer_H */ 67 | -------------------------------------------------------------------------------- /src/lib/rds.i: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | 3 | %feature("autodoc", "1"); // generate python docstrings 4 | 5 | %{ 6 | #include 7 | %} 8 | 9 | %include "exception.i" 10 | %import "gnuradio.i" // the common stuff 11 | 12 | %{ 13 | #include "gnuradio_swig_bug_workaround.h" // mandatory bug fix 14 | #include "gr_rds_bpsk_demod.h" 15 | #include "gr_rds_data_decoder.h" 16 | #include "gr_rds_data_encoder.h" 17 | #include "gr_rds_rate_enforcer.h" 18 | #include "gr_rds_freq_divider.h" 19 | #include 20 | %} 21 | 22 | //------------------------------------------------------------------ 23 | 24 | GR_SWIG_BLOCK_MAGIC (gr_rds, data_decoder); 25 | 26 | gr_rds_data_decoder_sptr gr_rds_make_data_decoder(gr_msg_queue_sptr msgq); 27 | 28 | class gr_rds_data_decoder: public gr_sync_block 29 | { 30 | private: 31 | gr_rds_data_decoder(gr_msg_queue_sptr msgq); 32 | public: 33 | void reset(void); 34 | }; 35 | 36 | //------------------------------------------------------------------ 37 | 38 | GR_SWIG_BLOCK_MAGIC (gr_rds, data_encoder); 39 | 40 | gr_rds_data_encoder_sptr gr_rds_make_data_encoder(const char *xmlfile); 41 | 42 | class gr_rds_data_encoder: public gr_sync_block 43 | { 44 | private: 45 | gr_rds_data_encoder(const char *xmlfile); 46 | }; 47 | 48 | //------------------------------------------------------------------ 49 | 50 | GR_SWIG_BLOCK_MAGIC (gr_rds, rate_enforcer); 51 | 52 | gr_rds_rate_enforcer_sptr gr_rds_make_rate_enforcer(double samp_rate); 53 | 54 | class gr_rds_rate_enforcer: public gr_block 55 | { 56 | private: 57 | gr_rds_rate_enforcer(double samp_rate); 58 | }; 59 | 60 | // ------------------------------------------------------------------ 61 | 62 | GR_SWIG_BLOCK_MAGIC (gr_rds, freq_divider); 63 | 64 | gr_rds_freq_divider_sptr gr_rds_make_freq_divider (unsigned int divider); 65 | 66 | class gr_rds_freq_divider: public gr_sync_block 67 | { 68 | private: 69 | gr_rds_freq_divider (unsigned int divider); 70 | }; 71 | 72 | // ----------------------------------------------------------------- 73 | 74 | GR_SWIG_BLOCK_MAGIC (gr_rds, bpsk_demod); 75 | 76 | gr_rds_bpsk_demod_sptr gr_rds_make_bpsk_demod (double sampling_rate); 77 | 78 | class gr_rds_bpsk_demod: public gr_block 79 | { 80 | private: 81 | gr_rds_bpsk_demod (double sampling_rate); 82 | public: 83 | void reset(void); 84 | }; 85 | -------------------------------------------------------------------------------- /src/python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | ######################################################################## 21 | # Include python install macros 22 | ######################################################################## 23 | include(GrPython) 24 | if(NOT PYTHONINTERP_FOUND) 25 | return() 26 | endif() 27 | 28 | ######################################################################## 29 | # Install python sources 30 | ######################################################################## 31 | GR_PYTHON_INSTALL( 32 | FILES 33 | __init__.py 34 | rdspanel.py 35 | DESTINATION ${GR_PYTHON_DIR}${RDS_PYTHON_SUBDIR}/rds 36 | ) 37 | 38 | ######################################################################## 39 | # Handle the unit tests 40 | ######################################################################## 41 | include(GrTest) 42 | 43 | set(GR_TEST_TARGET_DEPS gnuradio-rds) 44 | set(GR_TEST_PYTHON_DIRS ${CMAKE_BINARY_DIR}/swig) 45 | GR_ADD_TEST(qa_rds ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_rds.py) 46 | 47 | -------------------------------------------------------------------------------- /src/python/README: -------------------------------------------------------------------------------- 1 | 1) The script usrp_rds_rx.py is the RDS receiver, which uses rdspanel.py and rdspanel.wxg for the GUI. 2 | 2) The script rds_no_gui.py is (obviously) the same receiver, without the GUI (prints everything to the console). 3 | 3) The scripts fm_tx_mono.py and fm_tx_stereo.py are FM transmitters taking their input from uncompressed audio files. 4 | 4) The script usrp_rds_tx.py is the RDS transmitter (INCOMPLETE). 5 | 5) The script txrx.py is a loopback TX-RX for testing purposes (no USRP required). 6 | 6) The script newrx.py is an attempt to replace the custom freq_divider and bpsk_demod blocks with gr blocks (INCOMPLETE). 7 | -------------------------------------------------------------------------------- /src/python/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2008,2009 Free Software Foundation, Inc. 3 | # 4 | # This application is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 3, or (at your option) 7 | # any later version. 8 | # 9 | # This application is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | # 18 | 19 | # The presence of this file turns this directory into a Python package 20 | 21 | ''' 22 | This is the GNU Radio HOWTO module. Place your Python package 23 | description here (python/__init__.py). 24 | ''' 25 | 26 | # import swig generated symbols into the howto namespace 27 | #from rds_swig import * 28 | from rds import * 29 | 30 | # import any pure python here 31 | # 32 | from rdspanel import * 33 | -------------------------------------------------------------------------------- /src/python/offline_rds_rx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from gnuradio import gr, rds, audio 4 | from gnuradio.eng_option import eng_option 5 | from gnuradio.wxgui import slider, form, stdgui2, fftsink2, scopesink2, constsink_gl 6 | from optparse import OptionParser 7 | from rdspanel import rdsPanel 8 | import sys, math, wx, time 9 | 10 | usrp_rate = 256e3 11 | audio_decim = 8 12 | audio_rate = usrp_rate / audio_decim # 32 kS/s 13 | offline_samples = "/home/sdr/rds_samples.dat" 14 | 15 | class rds_rx_graph (stdgui2.std_top_block): 16 | def __init__(self,frame,panel,vbox,argv): 17 | stdgui2.std_top_block.__init__ (self,frame,panel,vbox,argv) 18 | 19 | parser=OptionParser(option_class=eng_option) 20 | parser.add_option("-V", "--volume", type="eng_float", default=None, 21 | help="set volume (default is midpoint)") 22 | parser.add_option("-O", "--audio-output", type="string", default="plughw:0,0", 23 | help="pcm device name (default is plughw:0,0)") 24 | 25 | # print help when called with wrong arguments 26 | (options, args) = parser.parse_args() 27 | if len(args) != 0: 28 | parser.print_help() 29 | sys.exit(1) 30 | 31 | self.file_source = gr.file_source(gr.sizeof_gr_complex*1, offline_samples, True) 32 | 33 | chan_filter_coeffs = gr.firdes.low_pass( 34 | 1.0, # gain 35 | usrp_rate, # sampling rate 36 | 80e3, # passband cutoff 37 | 35e3, # transition width 38 | gr.firdes.WIN_HAMMING) 39 | self.chan_filter = gr.fir_filter_ccf(1, chan_filter_coeffs) 40 | print "# channel filter:", len(chan_filter_coeffs), "taps" 41 | 42 | # PLL-based WFM demod 43 | fm_alpha = 0.25 * 250e3 * math.pi / usrp_rate # 0.767 44 | fm_beta = fm_alpha * fm_alpha / 4.0 # 0.147 45 | fm_max_freq = 2.0 * math.pi * 90e3 / usrp_rate # 2.209 46 | self.fm_demod = gr.pll_freqdet_cf( 47 | fm_alpha, # phase gain 48 | fm_beta, # freq gain 49 | fm_max_freq, # in radians/sample 50 | -fm_max_freq) 51 | self.connect(self.file_source, self.chan_filter, self.fm_demod) 52 | 53 | # L+R, pilot, L-R, RDS filters 54 | lpr_filter_coeffs = gr.firdes.low_pass( 55 | 1.0, # gain 56 | usrp_rate, # sampling rate 57 | 15e3, # passband cutoff 58 | 1e3, # transition width 59 | gr.firdes.WIN_HAMMING) 60 | self.lpr_filter = gr.fir_filter_fff(audio_decim, lpr_filter_coeffs) 61 | pilot_filter_coeffs = gr.firdes.band_pass( 62 | 1.0, # gain 63 | usrp_rate, # sampling rate 64 | 19e3-500, # low cutoff 65 | 19e3+500, # high cutoff 66 | 1e3, # transition width 67 | gr.firdes.WIN_HAMMING) 68 | self.pilot_filter = gr.fir_filter_fff(1, pilot_filter_coeffs) 69 | dsbsc_filter_coeffs = gr.firdes.band_pass( 70 | 1.0, # gain 71 | usrp_rate, # sampling rate 72 | 38e3-15e3/2, # low cutoff 73 | 38e3+15e3/2, # high cutoff 74 | 1e3, # transition width 75 | gr.firdes.WIN_HAMMING) 76 | self.dsbsc_filter = gr.fir_filter_fff(1, dsbsc_filter_coeffs) 77 | rds_filter_coeffs = gr.firdes.band_pass( 78 | 1.0, # gain 79 | usrp_rate, # sampling rate 80 | 57e3-3e3, # low cutoff 81 | 57e3+3e3, # high cutoff 82 | 3e3, # transition width 83 | gr.firdes.WIN_HAMMING) 84 | self.rds_filter = gr.fir_filter_fff(1, rds_filter_coeffs) 85 | print "# lpr filter:", len(lpr_filter_coeffs), "taps" 86 | print "# pilot filter:", len(pilot_filter_coeffs), "taps" 87 | print "# dsbsc filter:", len(dsbsc_filter_coeffs), "taps" 88 | print "# rds filter:", len(rds_filter_coeffs), "taps" 89 | self.connect(self.fm_demod, self.lpr_filter) 90 | self.connect(self.fm_demod, self.pilot_filter) 91 | self.connect(self.fm_demod, self.dsbsc_filter) 92 | self.connect(self.fm_demod, self.rds_filter) 93 | 94 | # down-convert L-R, RDS 95 | self.stereo_baseband = gr.multiply_ff() 96 | self.connect(self.pilot_filter, (self.stereo_baseband, 0)) 97 | self.connect(self.pilot_filter, (self.stereo_baseband, 1)) 98 | self.connect(self.dsbsc_filter, (self.stereo_baseband, 2)) 99 | self.rds_baseband = gr.multiply_ff() 100 | self.connect(self.pilot_filter, (self.rds_baseband, 0)) 101 | self.connect(self.pilot_filter, (self.rds_baseband, 1)) 102 | self.connect(self.pilot_filter, (self.rds_baseband, 2)) 103 | self.connect(self.rds_filter, (self.rds_baseband, 3)) 104 | 105 | # low-pass and downsample L-R 106 | lmr_filter_coeffs = gr.firdes.low_pass( 107 | 1.0, # gain 108 | usrp_rate, # sampling rate 109 | 15e3, # passband cutoff 110 | 1e3, # transition width 111 | gr.firdes.WIN_HAMMING) 112 | self.lmr_filter = gr.fir_filter_fff(audio_decim, lmr_filter_coeffs) 113 | self.connect(self.stereo_baseband, self.lmr_filter) 114 | 115 | # create L, R from L-R, L+R 116 | self.left = gr.add_ff() 117 | self.right = gr.sub_ff() 118 | self.connect(self.lpr_filter, (self.left, 0)) 119 | self.connect(self.lmr_filter, (self.left, 1)) 120 | self.connect(self.lpr_filter, (self.right, 0)) 121 | self.connect(self.lmr_filter, (self.right, 1)) 122 | 123 | # send audio to null sink 124 | self.null0 = gr.null_sink(gr.sizeof_float) 125 | self.null1 = gr.null_sink(gr.sizeof_float) 126 | self.connect(self.left, self.null0) 127 | self.connect(self.right, self.null1) 128 | 129 | # low-pass the baseband RDS signal at 1.5kHz 130 | rds_bb_filter_coeffs = gr.firdes.low_pass( 131 | 1, # gain 132 | usrp_rate, # sampling rate 133 | 1.5e3, # passband cutoff 134 | 2e3, # transition width 135 | gr.firdes.WIN_HAMMING) 136 | self.rds_bb_filter = gr.fir_filter_fff(audio_decim, rds_bb_filter_coeffs) 137 | print "# rds bb filter:", len(rds_bb_filter_coeffs), "taps" 138 | self.connect(self.rds_baseband, self.rds_bb_filter) 139 | 140 | # 1187.5bps = 19kHz/16 141 | self.clock_divider = rds.freq_divider(16) 142 | rds_clock_taps = gr.firdes.low_pass( 143 | 1, # gain 144 | usrp_rate, # sampling rate 145 | 1.2e3, # passband cutoff 146 | 1.5e3, # transition width 147 | gr.firdes.WIN_HAMMING) 148 | self.rds_clock = gr.fir_filter_fff(audio_decim, rds_clock_taps) 149 | print "# rds clock filter:", len(rds_clock_taps), "taps" 150 | self.connect(self.pilot_filter, self.clock_divider, self.rds_clock) 151 | 152 | # bpsk_demod, diff_decoder, rds_decoder 153 | self.bpsk_demod = rds.bpsk_demod(audio_rate) 154 | self.differential_decoder = gr.diff_decoder_bb(2) 155 | self.msgq = gr.msg_queue() 156 | self.rds_decoder = rds.data_decoder(self.msgq) 157 | self.connect(self.rds_bb_filter, (self.bpsk_demod, 0)) 158 | self.connect(self.rds_clock, (self.bpsk_demod, 1)) 159 | self.connect(self.bpsk_demod, self.differential_decoder) 160 | self.connect(self.differential_decoder, self.rds_decoder) 161 | 162 | 163 | self.frame = frame 164 | self.panel = panel 165 | self._build_gui(vbox, usrp_rate, audio_rate) 166 | 167 | 168 | ####################### GUI ################################ 169 | 170 | def _set_status_msg(self, msg, which=0): 171 | self.frame.GetStatusBar().SetStatusText(msg, which) 172 | 173 | def _build_gui(self, vbox, usrp_rate, audio_rate): 174 | 175 | def _form_set_freq(kv): 176 | return self.set_freq(kv['freq']) 177 | 178 | if 1: 179 | self.fft = fftsink2.fft_sink_f (self.panel, title="Post FM Demod", 180 | fft_size=512, sample_rate=usrp_rate, y_per_div=10, ref_level=0) 181 | self.connect (self.fm_demod, self.fft) 182 | vbox.Add (self.fft.win, 4, wx.EXPAND) 183 | if 0: 184 | self.scope = scopesink2.scope_sink_f(self.panel, title="RDS timedomain", 185 | sample_rate=usrp_rate, num_inputs=2) 186 | self.connect (self.rds_bb_filter, (rds_scope,1)) 187 | self.connect (self.rds_clock, (rds_scope,0)) 188 | vbox.Add(self.scope.win, 4, wx.EXPAND) 189 | 190 | self.rdspanel = rdsPanel(self.msgq, self.panel) 191 | vbox.Add(self.rdspanel, 1, wx.EXPAND|wx.TOP|wx.BOTTOM, 20) 192 | 193 | # control area form at bottom 194 | self.myform = form.form() 195 | 196 | 197 | 198 | 199 | if __name__ == '__main__': 200 | app = stdgui2.stdapp (rds_rx_graph, "USRP RDS RX") 201 | app.MainLoop () 202 | -------------------------------------------------------------------------------- /src/python/qa_rds.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright 2004 Free Software Foundation, Inc. 4 | # 5 | # This file is part of GNU Radio 6 | # 7 | # GNU Radio is free software; you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation; either version 2, or (at your option) 10 | # any later version. 11 | # 12 | # GNU Radio is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with GNU Radio; see the file COPYING. If not, write to 19 | # the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 20 | # Boston, MA 02111-1307, USA. 21 | # 22 | 23 | from gnuradio import gr, gr_unittest, rds 24 | 25 | class qa_rds (gr_unittest.TestCase): 26 | 27 | def setUp (self): 28 | self.fg = gr.top_block () 29 | 30 | def tearDown (self): 31 | self.fg = None 32 | 33 | def test_001_freq_divider (self): 34 | src_data = (-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1) 35 | expected_result = (1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1) 36 | src = gr.vector_source_f (src_data) 37 | dut = rds.freq_divider(2) 38 | dst = gr.vector_sink_f () 39 | self.fg.connect(src, dut, dst) 40 | self.fg.run() 41 | result_data = dst.data() 42 | self.assertEqual (expected_result, result_data) 43 | 44 | 45 | if __name__ == '__main__': 46 | gr_unittest.main () 47 | -------------------------------------------------------------------------------- /src/python/rds_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 50FF 5 | 31 6 | true 7 | false 8 | true 9 | 89.8 10 | 102.3 11 | JRCRadio 12 | 13 | 18 | 19 | 3 20 | 2 21 | 724 22 | 6126 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/python/rds_no_gui.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from gnuradio import gr, usrp, optfir, blks2, rds, audio 4 | from gnuradio.eng_option import eng_option 5 | from usrpm import usrp_dbid 6 | from optparse import OptionParser 7 | import sys, math 8 | 9 | dblist = (usrp_dbid.TV_RX, usrp_dbid.TV_RX_REV_2, 10 | usrp_dbid.TV_RX_REV_3, usrp_dbid.BASIC_RX) 11 | 12 | class rds_rx_graph (gr.top_block): 13 | def __init__(self): 14 | gr.top_block.__init__ (self) 15 | 16 | parser=OptionParser(option_class=eng_option) 17 | parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, 18 | help="select USRP Rx side A or B (default=A)") 19 | parser.add_option("-f", "--freq", type="eng_float", default=91.2e6, 20 | help="set frequency to FREQ", metavar="FREQ") 21 | parser.add_option("-g", "--gain", type="eng_float", default=None, 22 | help="set gain in dB") 23 | parser.add_option("-s", "--squelch", type="eng_float", default=0, 24 | help="set squelch level (default is 0)") 25 | parser.add_option("-V", "--volume", type="eng_float", default=None, 26 | help="set volume (default is midpoint)") 27 | parser.add_option("-O", "--audio-output", type="string", default="plughw:0,0", 28 | help="pcm device name (default is plughw:0,0)") 29 | (options, args) = parser.parse_args() 30 | if len(args) != 0: 31 | parser.print_help() 32 | sys.exit(1) 33 | 34 | # connect to USRP 35 | usrp_decim = 250 36 | self.u = usrp.source_c(0, usrp_decim) 37 | print "USRP Serial: ", self.u.serial_number() 38 | demod_rate = self.u.adc_rate() / usrp_decim # 256 kS/s 39 | audio_decim = 8 40 | audio_rate = demod_rate / audio_decim # 32 kS/s 41 | if options.rx_subdev_spec is None: 42 | options.rx_subdev_spec = usrp.pick_subdev(self.u, dblist) 43 | 44 | self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) 45 | self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) 46 | print "Using d'board", self.subdev.side_and_name() 47 | 48 | 49 | # gain, volume, frequency 50 | self.gain = options.gain 51 | if options.gain is None: 52 | self.gain = self.subdev.gain_range()[1] 53 | 54 | self.vol = options.volume 55 | if self.vol is None: 56 | g = self.volume_range() 57 | self.vol = float(g[0]+g[1])/2 58 | 59 | self.freq = options.freq 60 | if abs(self.freq) < 1e6: 61 | self.freq *= 1e6 62 | 63 | print "Volume:%r, Gain:%r, Freq:%3.1f MHz" % (self.vol, self.gain, self.freq/1e6) 64 | 65 | # channel filter, wfm_rcv_pll 66 | chan_filt_coeffs = optfir.low_pass( 67 | 1, # gain 68 | demod_rate, # rate 69 | 80e3, # passband cutoff 70 | 115e3, # stopband cutoff 71 | 0.1, # passband ripple 72 | 60) # stopband attenuation 73 | self.chan_filt = gr.fir_filter_ccf (1, chan_filt_coeffs) 74 | self.guts = blks2.wfm_rcv_pll (demod_rate, audio_decim) 75 | self.connect(self.u, self.chan_filt, self.guts) 76 | 77 | # volume control, audio sink 78 | self.volume_control_l = gr.multiply_const_ff(self.vol) 79 | self.volume_control_r = gr.multiply_const_ff(self.vol) 80 | self.audio_sink = audio.sink(int(audio_rate), options.audio_output, False) 81 | self.connect ((self.guts, 0), self.volume_control_l, (self.audio_sink, 0)) 82 | self.connect ((self.guts, 1), self.volume_control_r, (self.audio_sink, 1)) 83 | 84 | # pilot channel filter (band-pass, 18.5-19.5kHz) 85 | pilot_filter_coeffs = gr.firdes.band_pass( 86 | 1, # gain 87 | demod_rate, # sampling rate 88 | 18.5e3, # low cutoff 89 | 19.5e3, # high cutoff 90 | 1e3, # transition width 91 | gr.firdes.WIN_HAMMING) 92 | self.pilot_filter = gr.fir_filter_fff(1, pilot_filter_coeffs) 93 | self.connect(self.guts.fm_demod, self.pilot_filter) 94 | 95 | # RDS channel filter (band-pass, 54-60kHz) 96 | rds_filter_coeffs = gr.firdes.band_pass( 97 | 1, # gain 98 | demod_rate, # sampling rate 99 | 54e3, # low cutoff 100 | 60e3, # high cutoff 101 | 3e3, # transition width 102 | gr.firdes.WIN_HAMMING) 103 | self.rds_filter = gr.fir_filter_fff(1, rds_filter_coeffs) 104 | self.connect(self.guts.fm_demod, self.rds_filter) 105 | 106 | # create 57kHz subcarrier from 19kHz pilot, downconvert RDS channel 107 | self.mixer = gr.multiply_ff() 108 | self.connect(self.pilot_filter, (self.mixer, 0)) 109 | self.connect(self.pilot_filter, (self.mixer, 1)) 110 | self.connect(self.pilot_filter, (self.mixer, 2)) 111 | self.connect(self.rds_filter, (self.mixer, 3)) 112 | 113 | # low-pass the baseband RDS signal at 1.5kHz 114 | rds_bb_filter_coeffs = gr.firdes.low_pass( 115 | 1, # gain 116 | demod_rate, # sampling rate 117 | 1.5e3, # passband cutoff 118 | 2e3, # transition width 119 | gr.firdes.WIN_HAMMING) 120 | self.rds_bb_filter = gr.fir_filter_fff(1, rds_bb_filter_coeffs) 121 | self.connect(self.mixer, self.rds_bb_filter) 122 | 123 | # 1187.5bps = 19kHz/16 124 | self.rds_clock = rds.freq_divider(16) 125 | clock_taps = gr.firdes.low_pass( 126 | 1, # gain 127 | demod_rate, # sampling rate 128 | 1.2e3, # passband cutoff 129 | 1.5e3, # transition width 130 | gr.firdes.WIN_HANN) 131 | self.clock_filter = gr.fir_filter_fff(1, clock_taps) 132 | self.connect(self.pilot_filter, self.rds_clock, self.clock_filter) 133 | 134 | # bpsk_demod, diff_decoder, rds_decoder 135 | self.bpsk_demod = rds.bpsk_demod(demod_rate) 136 | self.differential_decoder = gr.diff_decoder_bb(2) 137 | self.msgq = gr.msg_queue() 138 | self.rds_decoder = rds.data_decoder(self.msgq) 139 | self.connect(self.rds_bb_filter, (self.bpsk_demod, 0)) 140 | self.connect(self.clock_filter, (self.bpsk_demod, 1)) 141 | self.connect(self.bpsk_demod, self.differential_decoder) 142 | self.connect(self.differential_decoder, self.rds_decoder) 143 | 144 | # set initial values 145 | self.subdev.set_gain(self.gain) 146 | self.set_vol(self.vol) 147 | self.set_freq(self.freq) 148 | 149 | def volume_range(self): 150 | return (-20.0, 0.0, 0.5) # hardcoded values 151 | 152 | def set_vol (self, vol): 153 | self.volume_control_l.set_k(10**(vol/10)) 154 | self.volume_control_r.set_k(10**(vol/10)) 155 | 156 | def set_freq(self, target_freq): 157 | r = usrp.tune(self.u, 0, self.subdev, target_freq) 158 | if r: 159 | self.freq = target_freq 160 | self.bpsk_demod.reset() 161 | self.rds_decoder.reset() 162 | return True 163 | else: 164 | return False 165 | 166 | if __name__ == '__main__': 167 | tb =rds_rx_graph() 168 | try: 169 | tb.run() 170 | except KeyboardInterrupt: 171 | pass 172 | -------------------------------------------------------------------------------- /src/python/rdspanel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | 4 | import wx 5 | import gnuradio.gr.gr_threading as _threading 6 | 7 | wxDATA_EVENT = wx.NewEventType() 8 | 9 | def EVT_DATA_EVENT(win, func): 10 | win.Connect(-1, -1, wxDATA_EVENT, func) 11 | 12 | class DataEvent(wx.PyEvent): 13 | def __init__(self, data): 14 | wx.PyEvent.__init__(self) 15 | self.SetEventType (wxDATA_EVENT) 16 | self.data = data 17 | 18 | def Clone (self): 19 | self.__class__ (self.GetId()) 20 | 21 | class queue_watcher_thread(_threading.Thread): 22 | def __init__(self, rcvd_pktq, event_receiver): 23 | _threading.Thread.__init__(self) 24 | self.setDaemon(1) 25 | self.rcvd_pktq = rcvd_pktq 26 | self.event_receiver = event_receiver 27 | self.keep_running = True 28 | self.start() 29 | 30 | def stop(self): 31 | self.keep_running = False 32 | 33 | def run(self): 34 | while self.keep_running: 35 | msg = self.rcvd_pktq.delete_head() 36 | de = DataEvent (msg) 37 | wx.PostEvent (self.event_receiver, de) 38 | del de 39 | 40 | class rdsPanel(wx.Panel): 41 | def __init__(self, msgq, freq=None, *args, **kwds): 42 | kwds["style"] = wx.TAB_TRAVERSAL 43 | wx.Panel.__init__(self, *args, **kwds) 44 | self.label_1 = wx.StaticText(self, -1, "Frequency") 45 | self.label_2 = wx.StaticText(self, -1, "Station Name") 46 | self.label_3 = wx.StaticText(self, -1, "Program Type") 47 | self.label_4 = wx.StaticText(self, -1, "PI") 48 | self.label_5 = wx.StaticText(self, -1, "Radio Text") 49 | self.label_6 = wx.StaticText(self, -1, "Clock Time") 50 | self.label_7 = wx.StaticText(self, -1, "Alt. Frequencies") 51 | self.frequency = wx.StaticText(self, -1, "xxx.xx") 52 | self.station_name = wx.StaticText(self, -1, "xxxxxxxx") 53 | self.program_type = wx.StaticText(self, -1, "xxxxxxxxxxx") 54 | self.program_information = wx.StaticText(self, -1, "xxxx") 55 | self.tp_flag = wx.StaticText(self, -1, "TP") 56 | self.ta_flag = wx.StaticText(self, -1, "TA") 57 | self.musicspeech_flag = wx.StaticText(self, -1, "MUS/SPE") 58 | self.monostereo_flag = wx.StaticText(self, -1, "MN/ST") 59 | self.artificialhead_flag = wx.StaticText(self, -1, "AH") 60 | self.compressed_flag = wx.StaticText(self, -1, "CMP") 61 | self.staticpty_flag = wx.StaticText(self, -1, "stPTY") 62 | self.radiotext = wx.StaticText(self, -1, "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx") 63 | self.clocktime = wx.StaticText(self, -1, "xxxxxxxxxxxxxxxxxxxxx") 64 | self.alt_freq = wx.StaticText(self, -1, "xxxxxxxxxxxxxxx") 65 | 66 | self.__set_properties() 67 | self.__do_layout() 68 | if freq is not None: 69 | self.set_frequency(freq) 70 | EVT_DATA_EVENT (self, self.display_data) 71 | watcher=queue_watcher_thread(msgq,self) 72 | 73 | def __set_properties(self): 74 | font_bold = wx.Font(16, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "") 75 | font_normal = wx.Font(16, wx.DEFAULT, wx.NORMAL, wx.NORMAL, 0, "") 76 | font_small = wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.BOLD, 0, "") 77 | 78 | self.frequency.SetFont(font_bold) 79 | self.station_name.SetFont(font_bold) 80 | self.program_type.SetFont(font_bold) 81 | self.program_information.SetFont(font_bold) 82 | self.tp_flag.SetFont(font_normal) 83 | self.ta_flag.SetFont(font_normal) 84 | self.musicspeech_flag.SetFont(font_normal) 85 | self.monostereo_flag.SetFont(font_normal) 86 | self.artificialhead_flag.SetFont(font_normal) 87 | self.compressed_flag.SetFont(font_normal) 88 | self.staticpty_flag.SetFont(font_normal) 89 | self.radiotext.SetFont(font_small) 90 | self.clocktime.SetFont(font_small) 91 | self.alt_freq.SetFont(font_small) 92 | 93 | def __do_layout(self): 94 | sizer_0 = wx.BoxSizer(wx.VERTICAL) 95 | sizer_1 = wx.BoxSizer(wx.HORIZONTAL) 96 | sizer_2 = wx.BoxSizer(wx.HORIZONTAL) 97 | sizer_3 = wx.BoxSizer(wx.HORIZONTAL) 98 | 99 | flag = wx.ALIGN_CENTER_VERTICAL|wx.LEFT 100 | 101 | # arguments: window, proportion, flag, border 102 | sizer_1.Add(self.label_1, 0, flag) 103 | sizer_1.Add(self.frequency, 0, flag, 20) 104 | sizer_1.Add(self.label_2, 0, flag, 20) 105 | sizer_1.Add(self.station_name, 0, flag, 20) 106 | sizer_1.Add(self.label_3, 0, flag, 20) 107 | sizer_1.Add(self.program_type, 0, flag, 20) 108 | sizer_1.Add(self.label_4, 0, flag, 20) 109 | sizer_1.Add(self.program_information, 0, flag, 20) 110 | sizer_0.Add(sizer_1, 1, wx.ALIGN_CENTER) 111 | 112 | sizer_2.Add(self.tp_flag, 0, flag) 113 | sizer_2.Add(self.ta_flag, 0, flag, 30) 114 | sizer_2.Add(self.musicspeech_flag, 0, flag, 30) 115 | sizer_2.Add(self.monostereo_flag, 0, flag, 30) 116 | sizer_2.Add(self.artificialhead_flag, 0, flag, 30) 117 | sizer_2.Add(self.compressed_flag, 0, flag, 30) 118 | sizer_2.Add(self.staticpty_flag, 0, flag, 30) 119 | sizer_0.Add(sizer_2, 1, wx.ALIGN_CENTER) 120 | 121 | sizer_3.Add(self.label_5, 0, flag) 122 | sizer_3.Add(self.radiotext, 0, flag, 10) 123 | sizer_3.Add(self.label_6, 0, flag, 10) 124 | sizer_3.Add(self.clocktime, 0, flag, 10) 125 | sizer_3.Add(self.label_7, 0, flag, 10) 126 | sizer_3.Add(self.alt_freq, 0, flag, 10) 127 | sizer_0.Add(sizer_3, 0, wx.ALIGN_CENTER) 128 | 129 | self.SetSizer(sizer_0) 130 | 131 | def display_data(self,event): 132 | message = event.data 133 | if (message.type()==0): #program information 134 | self.program_information.SetLabel(message.to_string()) 135 | elif (message.type()==1): #station name 136 | self.station_name.SetLabel(message.to_string()) 137 | elif (message.type()==2): #program type 138 | self.program_type.SetLabel(message.to_string()) 139 | elif (message.type()==3): #flags 140 | flags=message.to_string() 141 | if (flags[0]=='1'): 142 | self.tp_flag.SetForegroundColour(wx.RED) 143 | else: 144 | self.tp_flag.SetForegroundColour(wx.LIGHT_GREY) 145 | if (flags[1]=='1'): 146 | self.ta_flag.SetForegroundColour(wx.RED) 147 | else: 148 | self.ta_flag.SetForegroundColour(wx.LIGHT_GREY) 149 | if (flags[2]=='1'): 150 | self.musicspeech_flag.SetLabel("Music") 151 | self.musicspeech_flag.SetForegroundColour(wx.RED) 152 | else: 153 | self.musicspeech_flag.SetLabel("Speech") 154 | self.musicspeech_flag.SetForegroundColour(wx.RED) 155 | if (flags[3]=='1'): 156 | self.monostereo_flag.SetLabel("Mono") 157 | self.monostereo_flag.SetForegroundColour(wx.RED) 158 | else: 159 | self.monostereo_flag.SetLabel("Stereo") 160 | self.monostereo_flag.SetForegroundColour(wx.RED) 161 | if (flags[4]=='1'): 162 | self.artificialhead_flag.SetForegroundColour(wx.RED) 163 | else: 164 | self.artificialhead_flag.SetForegroundColour(wx.LIGHT_GREY) 165 | if (flags[5]=='1'): 166 | self.compressed_flag.SetForegroundColour(wx.RED) 167 | else: 168 | self.compressed_flag.SetForegroundColour(wx.LIGHT_GREY) 169 | if (flags[6]=='1'): 170 | self.staticpty_flag.SetForegroundColour(wx.RED) 171 | else: 172 | self.staticpty_flag.SetForegroundColour(wx.LIGHT_GREY) 173 | elif (message.type()==4): #radiotext 174 | self.radiotext.SetLabel(message.to_string()) 175 | elif (message.type()==5): #clocktime 176 | self.clocktime.SetLabel(message.to_string()) 177 | elif (message.type()==6): #alternative frequencies 178 | self.alt_freq.SetLabel(message.to_string()) 179 | 180 | def clear_data(self): 181 | self.program_information.SetLabel("xxxx") 182 | self.station_name.SetLabel("xxxxxxxx") 183 | self.program_type.SetLabel("xxxxxxxxxxx") 184 | self.ta_flag.SetForegroundColour(wx.BLACK) 185 | self.tp_flag.SetForegroundColour(wx.BLACK) 186 | self.musicspeech_flag.SetLabel("MUS/SPE") 187 | self.musicspeech_flag.SetForegroundColour(wx.BLACK) 188 | self.monostereo_flag.SetLabel("MN/ST") 189 | self.monostereo_flag.SetForegroundColour(wx.BLACK) 190 | self.artificialhead_flag.SetForegroundColour(wx.BLACK) 191 | self.compressed_flag.SetForegroundColour(wx.BLACK) 192 | self.staticpty_flag.SetForegroundColour(wx.BLACK) 193 | self.radiotext.SetLabel("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx") 194 | self.clocktime.SetLabel("xxxxxxxxxxxx") 195 | self.alt_freq.SetLabel("xxxxxxxxxxxxxxxxx") 196 | 197 | def set_frequency(self, freq=None): 198 | freq_str = "xxx.xx" 199 | if freq is not None: 200 | if isinstance(freq, float) or isinstance(freq, int): 201 | freq_str = "%.2f" % (float(freq) / 1e6) 202 | else: 203 | freq_str = str(freq) 204 | self.frequency.SetLabel(freq_str) 205 | -------------------------------------------------------------------------------- /src/python/run_tests.in: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # All this strange PYTHONPATH manipulation is required to run our 4 | # tests using our just built shared library and swig-generated python 5 | # code prior to installation. 6 | 7 | # build tree == src tree unless you're doing a VPATH build. 8 | # If you don't know what a VPATH build is, you're not doing one. Relax... 9 | 10 | prefix=@prefix@ 11 | exec_prefix=@exec_prefix@ 12 | 13 | # Where to look in the build tree for our shared library 14 | libbld=@abs_top_builddir@/src/lib 15 | # Where to look in the src tree for swig generated python code 16 | libsrc=@abs_top_srcdir@/src/lib 17 | # Where to look in the src tree for hand written python code 18 | py=@abs_top_srcdir@/src/python 19 | 20 | # Where to look for installed GNU Radio python modules 21 | # FIXME this is wrong on a distcheck. We really need to ask gnuradio-core 22 | # where it put its python files. 23 | installed_pythondir=@pythondir@ 24 | installed_pyexecdir=@pyexecdir@ 25 | 26 | PYTHONPATH="$libbld:$libbld/.libs:$libsrc:$py:$installed_pythondir:$installed_pyexecdir:$PYTHONPATH" 27 | #PYTHONPATH="$libbld:$libbld/.libs:$libsrc:$py:$installed_pythondir:$installed_pyexecdir" 28 | 29 | export PYTHONPATH 30 | 31 | # 32 | # This is the simple part... 33 | # Run everything that matches qa_*.py and return the final result. 34 | # 35 | 36 | ok=yes 37 | for file in @srcdir@/qa_*.py 38 | do 39 | if ! $file 40 | then 41 | ok=no 42 | fi 43 | done 44 | 45 | if [ $ok = yes ] 46 | then 47 | exit 0 48 | else 49 | exit 1 50 | fi 51 | -------------------------------------------------------------------------------- /src/python/usrp_rds_rx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from gnuradio import gr, usrp, rds, audio, blks2 4 | from gnuradio.eng_option import eng_option 5 | from gnuradio.wxgui import slider, form, stdgui2, fftsink2, scopesink2, constsink_gl 6 | from optparse import OptionParser 7 | from rdspanel import rdsPanel 8 | #from usrpm import usrp_dbid 9 | import sys, math, wx, time 10 | 11 | #dblist = (usrp_dbid.TV_RX, usrp_dbid.TV_RX_REV_2, 12 | # usrp_dbid.TV_RX_REV_3, usrp_dbid.BASIC_RX) 13 | 14 | class rds_rx_graph (stdgui2.std_top_block): 15 | def __init__(self,frame,panel,vbox,argv): 16 | stdgui2.std_top_block.__init__ (self,frame,panel,vbox,argv) 17 | 18 | parser=OptionParser(option_class=eng_option) 19 | parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, 20 | help="select USRP Rx side A or B (default=A)") 21 | parser.add_option("-f", "--freq", type="eng_float", default=91.2e6, 22 | help="set frequency to FREQ", metavar="FREQ") 23 | parser.add_option("-g", "--gain", type="eng_float", default=None, 24 | help="set gain in dB") 25 | # FIXME add squelch 26 | parser.add_option("-s", "--squelch", type="eng_float", default=0, 27 | help="set squelch level (default is 0)") 28 | parser.add_option("-V", "--volume", type="eng_float", default=None, 29 | help="set volume (default is midpoint)") 30 | parser.add_option("-O", "--audio-output", type="string", default="plughw:0,0", 31 | help="pcm device name (default is plughw:0,0)") 32 | 33 | # print help when called with wrong arguments 34 | (options, args) = parser.parse_args() 35 | if len(args) != 0: 36 | parser.print_help() 37 | sys.exit(1) 38 | 39 | # connect to USRP 40 | usrp_decim = 250 41 | self.u = usrp.source_c(0, usrp_decim) 42 | print "USRP Serial:", self.u.serial_number() 43 | usrp_rate = self.u.adc_rate() / usrp_decim # 256 kS/s 44 | print "usrp_rate =", usrp_rate 45 | audio_decim = 8 46 | audio_rate = usrp_rate / audio_decim # 32 kS/s 47 | print "audio_rate =", audio_rate 48 | #if options.rx_subdev_spec is None: 49 | # options.rx_subdev_spec = usrp.pick_subdev(self.u, dblist) 50 | 51 | self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) 52 | self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) 53 | print "Using d'board", self.subdev.side_and_name() 54 | 55 | # gain, volume, frequency 56 | self.gain = options.gain 57 | if options.gain is None: 58 | g = self.subdev.gain_range() 59 | self.gain = float(g[0]+g[1])/2 60 | self.vol = options.volume 61 | if self.vol is None: 62 | g = self.volume_range() 63 | self.vol = float(g[0]+g[1])/2 64 | self.freq = options.freq 65 | print "Volume:%r, Gain:%r, Freq:%3.1f MHz" % (self.vol, self.gain, self.freq/1e6) 66 | 67 | # channel filter 68 | chan_filter_coeffs = gr.firdes.low_pass( 69 | 1.0, # gain 70 | usrp_rate, # sampling rate 71 | 80e3, # passband cutoff 72 | 35e3, # transition width 73 | gr.firdes.WIN_HAMMING) 74 | self.chan_filter = gr.fir_filter_ccf(1, chan_filter_coeffs) 75 | print "# channel filter:", len(chan_filter_coeffs), "taps" 76 | 77 | # PLL-based WFM demod 78 | fm_alpha = 0.25 * 250e3 * math.pi / usrp_rate # 0.767 79 | fm_beta = fm_alpha * fm_alpha / 4.0 # 0.147 80 | fm_max_freq = 2.0 * math.pi * 90e3 / usrp_rate # 2.209 81 | self.fm_demod = gr.pll_freqdet_cf( 82 | #fm_alpha, # phase gain 83 | #fm_beta, # freq gain 84 | 1.0, # Loop BW 85 | fm_max_freq, # in radians/sample 86 | -fm_max_freq) 87 | self.fm_demod.set_alpha(fm_alpha) 88 | self.fm_demod.set_beta(fm_beta) 89 | self.connect(self.u, self.chan_filter, self.fm_demod) 90 | 91 | # L+R, pilot, L-R, RDS filters 92 | lpr_filter_coeffs = gr.firdes.low_pass( 93 | 1.0, # gain 94 | usrp_rate, # sampling rate 95 | 15e3, # passband cutoff 96 | 1e3, # transition width 97 | gr.firdes.WIN_HAMMING) 98 | self.lpr_filter = gr.fir_filter_fff(audio_decim, lpr_filter_coeffs) 99 | pilot_filter_coeffs = gr.firdes.band_pass( 100 | 1.0, # gain 101 | usrp_rate, # sampling rate 102 | 19e3-500, # low cutoff 103 | 19e3+500, # high cutoff 104 | 1e3, # transition width 105 | gr.firdes.WIN_HAMMING) 106 | self.pilot_filter = gr.fir_filter_fff(1, pilot_filter_coeffs) 107 | dsbsc_filter_coeffs = gr.firdes.band_pass( 108 | 1.0, # gain 109 | usrp_rate, # sampling rate 110 | 38e3-15e3/2, # low cutoff 111 | 38e3+15e3/2, # high cutoff 112 | 1e3, # transition width 113 | gr.firdes.WIN_HAMMING) 114 | self.dsbsc_filter = gr.fir_filter_fff(1, dsbsc_filter_coeffs) 115 | rds_filter_coeffs = gr.firdes.band_pass( 116 | 1.0, # gain 117 | usrp_rate, # sampling rate 118 | 57e3-3e3, # low cutoff 119 | 57e3+3e3, # high cutoff 120 | 3e3, # transition width 121 | gr.firdes.WIN_HAMMING) 122 | self.rds_filter = gr.fir_filter_fff(1, rds_filter_coeffs) 123 | print "# lpr filter:", len(lpr_filter_coeffs), "taps" 124 | print "# pilot filter:", len(pilot_filter_coeffs), "taps" 125 | print "# dsbsc filter:", len(dsbsc_filter_coeffs), "taps" 126 | print "# rds filter:", len(rds_filter_coeffs), "taps" 127 | self.connect(self.fm_demod, self.lpr_filter) 128 | self.connect(self.fm_demod, self.pilot_filter) 129 | self.connect(self.fm_demod, self.dsbsc_filter) 130 | self.connect(self.fm_demod, self.rds_filter) 131 | 132 | # down-convert L-R, RDS 133 | self.stereo_baseband = gr.multiply_ff() 134 | self.connect(self.pilot_filter, (self.stereo_baseband, 0)) 135 | self.connect(self.pilot_filter, (self.stereo_baseband, 1)) 136 | self.connect(self.dsbsc_filter, (self.stereo_baseband, 2)) 137 | self.rds_baseband = gr.multiply_ff() 138 | self.connect(self.pilot_filter, (self.rds_baseband, 0)) 139 | self.connect(self.pilot_filter, (self.rds_baseband, 1)) 140 | self.connect(self.pilot_filter, (self.rds_baseband, 2)) 141 | self.connect(self.rds_filter, (self.rds_baseband, 3)) 142 | 143 | # low-pass and downsample L-R 144 | lmr_filter_coeffs = gr.firdes.low_pass( 145 | 1.0, # gain 146 | usrp_rate, # sampling rate 147 | 15e3, # passband cutoff 148 | 1e3, # transition width 149 | gr.firdes.WIN_HAMMING) 150 | self.lmr_filter = gr.fir_filter_fff(audio_decim, lmr_filter_coeffs) 151 | self.connect(self.stereo_baseband, self.lmr_filter) 152 | 153 | # create L, R from L-R, L+R 154 | self.left = gr.add_ff() 155 | self.right = gr.sub_ff() 156 | self.connect(self.lpr_filter, (self.left, 0)) 157 | self.connect(self.lmr_filter, (self.left, 1)) 158 | self.connect(self.lpr_filter, (self.right, 0)) 159 | self.connect(self.lmr_filter, (self.right, 1)) 160 | 161 | # volume control, complex2flot, audio sink 162 | self.volume_control_l = gr.multiply_const_ff(self.vol) 163 | self.volume_control_r = gr.multiply_const_ff(self.vol) 164 | output_audio_rate = 48000 165 | self.audio_sink = audio.sink(int(output_audio_rate), 166 | options.audio_output, False) 167 | #self.connect(self.left, self.volume_control_l, (self.audio_sink, 0)) 168 | #self.connect(self.right, self.volume_control_r, (self.audio_sink, 1)) 169 | self.resamp_L = blks2.rational_resampler_fff(interpolation=output_audio_rate,decimation=audio_rate,taps=None,fractional_bw=None,) 170 | self.resamp_R = blks2.rational_resampler_fff(interpolation=output_audio_rate,decimation=audio_rate,taps=None,fractional_bw=None,) 171 | self.connect(self.left, self.volume_control_l, self.resamp_L, (self.audio_sink, 0)) 172 | self.connect(self.right, self.volume_control_r, self.resamp_R, (self.audio_sink, 1)) 173 | 174 | # low-pass the baseband RDS signal at 1.5kHz 175 | rds_bb_filter_coeffs = gr.firdes.low_pass( 176 | 1, # gain 177 | usrp_rate, # sampling rate 178 | 1.5e3, # passband cutoff 179 | 2e3, # transition width 180 | gr.firdes.WIN_HAMMING) 181 | self.rds_bb_filter = gr.fir_filter_fff(audio_decim, rds_bb_filter_coeffs) 182 | print "# rds bb filter:", len(rds_bb_filter_coeffs), "taps" 183 | self.connect(self.rds_baseband, self.rds_bb_filter) 184 | 185 | # 1187.5bps = 19kHz/16 186 | self.clock_divider = rds.freq_divider(16) 187 | rds_clock_taps = gr.firdes.low_pass( 188 | 1, # gain 189 | usrp_rate, # sampling rate 190 | 1.2e3, # passband cutoff 191 | 1.5e3, # transition width 192 | gr.firdes.WIN_HAMMING) 193 | self.rds_clock = gr.fir_filter_fff(audio_decim, rds_clock_taps) 194 | print "# rds clock filter:", len(rds_clock_taps), "taps" 195 | self.connect(self.pilot_filter, self.clock_divider, self.rds_clock) 196 | 197 | # bpsk_demod, diff_decoder, rds_decoder 198 | self.bpsk_demod = rds.bpsk_demod(audio_rate) 199 | self.differential_decoder = gr.diff_decoder_bb(2) 200 | self.msgq = gr.msg_queue() 201 | self.rds_decoder = rds.data_decoder(self.msgq) 202 | self.connect(self.rds_bb_filter, (self.bpsk_demod, 0)) 203 | self.connect(self.rds_clock, (self.bpsk_demod, 1)) 204 | self.connect(self.bpsk_demod, self.differential_decoder) 205 | self.connect(self.differential_decoder, self.rds_decoder) 206 | 207 | 208 | self.frame = frame 209 | self.panel = panel 210 | self._build_gui(vbox, usrp_rate, audio_rate) 211 | self.set_gain(self.gain) 212 | self.set_vol(self.vol) 213 | if not(self.set_freq(self.freq)): 214 | self._set_status_msg("Failed to set initial frequency") 215 | 216 | 217 | ####################### GUI ################################ 218 | 219 | def _set_status_msg(self, msg, which=0): 220 | self.frame.GetStatusBar().SetStatusText(msg, which) 221 | 222 | def _build_gui(self, vbox, usrp_rate, audio_rate): 223 | 224 | def _form_set_freq(kv): 225 | return self.set_freq(kv['freq']) 226 | 227 | if 1: 228 | self.fft = fftsink2.fft_sink_f (self.panel, title="Post FM Demod", 229 | fft_size=512, sample_rate=usrp_rate, y_per_div=10, ref_level=0) 230 | self.connect (self.fm_demod, self.fft) 231 | vbox.Add (self.fft.win, 4, wx.EXPAND) 232 | if 0: 233 | self.scope = scopesink2.scope_sink_f(self.panel, title="RDS timedomain", 234 | sample_rate=usrp_rate, num_inputs=2) 235 | self.connect (self.rds_bb_filter, (rds_scope,1)) 236 | self.connect (self.rds_clock, (rds_scope,0)) 237 | vbox.Add(self.scope.win, 4, wx.EXPAND) 238 | 239 | self.rdspanel = rdsPanel(self.msgq, self.panel) 240 | vbox.Add(self.rdspanel, 1, wx.EXPAND|wx.TOP|wx.BOTTOM, 20) 241 | 242 | # control area form at bottom 243 | self.myform = form.form() 244 | 245 | # 1st line 246 | hbox = wx.BoxSizer(wx.HORIZONTAL) 247 | self.myform.btn_down = wx.Button(self.panel, -1, "<<") 248 | self.myform.btn_down.Bind(wx.EVT_BUTTON, self.Seek_Down) 249 | hbox.Add(self.myform.btn_down, 0) 250 | self.myform['freq'] = form.float_field( 251 | parent=self.panel, sizer=hbox, label="Freq", weight=0, 252 | callback=self.myform.check_input_and_call(_form_set_freq, self._set_status_msg)) 253 | hbox.Add((5,0), 0) 254 | self.myform.btn_up = wx.Button(self.panel, -1, ">>") 255 | self.myform.btn_up.Bind(wx.EVT_BUTTON, self.Seek_Up) 256 | hbox.Add(self.myform.btn_up, 0) 257 | self.myform['freq_slider'] = form.quantized_slider_field( 258 | parent=self.panel, sizer=hbox, weight=3, 259 | range=(87.5e6, 108e6, 0.1e6), callback=self.set_freq) 260 | hbox.Add((5,0), 0) 261 | vbox.Add(hbox, 0, wx.EXPAND) 262 | 263 | 264 | # 2nd line 265 | hbox = wx.BoxSizer(wx.HORIZONTAL) 266 | hbox.Add((5,0), 0) 267 | self.myform['volume'] = form.quantized_slider_field(parent=self.panel, sizer=hbox, 268 | label="Volume", weight=3, range=self.volume_range(), callback=self.set_vol) 269 | hbox.Add((5,0), 1) 270 | self.myform['gain'] = form.quantized_slider_field(parent=self.panel, sizer=hbox, 271 | label="Gain", weight=3, range=self.subdev.gain_range(), callback=self.set_gain) 272 | hbox.Add((5,0), 0) 273 | vbox.Add(hbox, 0, wx.EXPAND) 274 | 275 | 276 | 277 | ########################### EVENTS ############################ 278 | 279 | def set_vol (self, vol): 280 | self.volume_control_l.set_k(10**(vol/10)) 281 | self.volume_control_r.set_k(10**(vol/10)) 282 | self.myform['volume'].set_value(vol) 283 | self.update_status_bar () 284 | 285 | def set_freq(self, target_freq): 286 | if (88108e6): return False 288 | r = usrp.tune(self.u, 0, self.subdev, target_freq) 289 | if r: 290 | self.freq = target_freq 291 | self.myform['freq'].set_value(target_freq) 292 | self.myform['freq_slider'].set_value(target_freq) 293 | self.rdspanel.frequency.SetLabel('%3.2f' % (target_freq/1e6)) 294 | self.update_status_bar() 295 | self.bpsk_demod.reset() 296 | self.rds_decoder.reset() 297 | self.rdspanel.clear_data() 298 | self._set_status_msg("OK", 0) 299 | return True 300 | else: 301 | self._set_status_msg("Failed", 0) 302 | return False 303 | 304 | def set_gain(self, gain): 305 | self.myform['gain'].set_value(gain) 306 | self.subdev.set_gain(gain) 307 | 308 | 309 | def update_status_bar (self): 310 | msg = "Volume:%r, Gain:%r, Freq:%3.1f MHz" % (self.vol, self.gain, self.freq/1e6) 311 | self._set_status_msg(msg, 1) 312 | 313 | def volume_range(self): 314 | return (-20.0, 0.0, 0.5) # hardcoded values 315 | 316 | def Seek_Up(self, event): 317 | new_freq = self.freq + 1e5 318 | if new_freq > 108e6: 319 | new_freq=88e6 320 | self.set_freq(new_freq) 321 | 322 | def Seek_Down(self, event): 323 | new_freq = self.freq - 1e5 324 | if new_freq < 88e6: 325 | new_freq=108e6 326 | self.set_freq(new_freq) 327 | 328 | 329 | if __name__ == '__main__': 330 | app = stdgui2.stdapp (rds_rx_graph, "USRP RDS RX") 331 | app.MainLoop () 332 | -------------------------------------------------------------------------------- /src/python/usrp_rds_tx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from gnuradio import gr, usrp, blks2, rds 4 | from gnuradio.eng_option import eng_option 5 | from gnuradio.wxgui import stdgui2, fftsink2, scopesink2 6 | from optparse import OptionParser 7 | from usrpm import usrp_dbid 8 | import math, sys, wx 9 | 10 | class rds_tx_block(stdgui2.std_top_block): 11 | def __init__(self, frame, panel, vbox, argv): 12 | stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv) 13 | 14 | parser = OptionParser (option_class=eng_option) 15 | parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, 16 | help="select USRP Tx side A or B") 17 | parser.add_option("-f", "--freq", type="eng_float", default=107.2e6, 18 | help="set Tx frequency to FREQ [required]", metavar="FREQ") 19 | parser.add_option("--wavfile", type="string", default=None, 20 | help="open .wav audio file FILE") 21 | parser.add_option("--xml", type="string", default="rds_data.xml", 22 | help="open .xml RDS data FILE") 23 | (options, args) = parser.parse_args() 24 | if len(args) != 0: 25 | parser.print_help() 26 | sys.exit(1) 27 | 28 | usrp_interp = 500 29 | self.u = usrp.sink_c (0, usrp_interp) 30 | print "USRP Serial: ", self.u.serial_number() 31 | usrp_rate = self.u.dac_rate() / usrp_interp # 256 kS/s 32 | 33 | # determine the daughterboard subdevice we're using 34 | if options.tx_subdev_spec is None: 35 | options.tx_subdev_spec = usrp.pick_tx_subdevice(self.u) 36 | self.u.set_mux(usrp.determine_tx_mux_value(self.u, options.tx_subdev_spec)) 37 | self.subdev = usrp.selected_subdev(self.u, options.tx_subdev_spec) 38 | print "Using d'board", self.subdev.side_and_name() 39 | 40 | # set max Tx gain, tune frequency and enable transmitter 41 | gain = self.subdev.gain_range()[1] 42 | self.subdev.set_gain(gain) 43 | print "Gain set to", gain 44 | if self.u.tune(self.subdev.which(), self.subdev, options.freq): 45 | print "Tuned to", options.freq/1e6, "MHz" 46 | else: 47 | sys.exit(1) 48 | self.subdev.set_enable(True) 49 | 50 | # open wav file containing floats in the [-1, 1] range, repeat 51 | if options.wavfile is None: 52 | print "Please provide a wavfile to transmit! Exiting\n" 53 | sys.exit(1) 54 | self.src = gr.wavfile_source(options.wavfile, True) 55 | nchans = self.src.channels() 56 | sample_rate = self.src.sample_rate() 57 | bits_per_sample = self.src.bits_per_sample() 58 | print nchans, "channels,", sample_rate, "samples/sec,", \ 59 | bits_per_sample, "bits/sample" 60 | 61 | # resample to usrp rate 62 | self.resample_left = blks2.rational_resampler_fff(usrp_rate, sample_rate) 63 | self.resample_right = blks2.rational_resampler_fff(usrp_rate, sample_rate) 64 | self.connect ((self.src, 0), self.resample_left) 65 | self.connect ((self.src, 1), self.resample_right) 66 | 67 | # create L+R (mono) and L-R (stereo) 68 | self.audio_lpr = gr.add_ff() 69 | self.audio_lmr = gr.sub_ff() 70 | self.connect (self.resample_left, (self.audio_lpr, 0)) 71 | self.connect (self.resample_left, (self.audio_lmr, 0)) 72 | self.connect (self.resample_right, (self.audio_lpr, 1)) 73 | self.connect (self.resample_right, (self.audio_lmr, 1)) 74 | 75 | # low-pass filter for L+R 76 | audio_lpr_taps = gr.firdes.low_pass( 77 | 0.5, # gain 78 | usrp_rate, # sampling rate 79 | 15e3, # passband cutoff 80 | 1e3, # transition width 81 | gr.firdes.WIN_HAMMING) 82 | self.audio_lpr_filter = gr.fir_filter_fff (1, audio_lpr_taps) 83 | self.connect (self.audio_lpr, self.audio_lpr_filter) 84 | 85 | # create pilot tone at 19 kHz 86 | self.pilot = gr.sig_source_f( 87 | usrp_rate, # sampling rate 88 | gr.GR_SIN_WAVE, # waveform 89 | 19e3, # frequency 90 | 5e-2) # amplitude 91 | 92 | # upconvert L-R to 38 kHz and band-pass 93 | self.mix_stereo = gr.multiply_ff() 94 | audio_lmr_taps = gr.firdes.band_pass( 95 | 80, # gain 96 | usrp_rate, # sampling rate 97 | 38e3-15e3, # low cutoff 98 | 38e3+15e3, # high cutoff 99 | 1e3, # transition width 100 | gr.firdes.WIN_HAMMING) 101 | self.audio_lmr_filter = gr.fir_filter_fff (1, audio_lmr_taps) 102 | self.connect (self.audio_lmr, (self.mix_stereo, 0)) 103 | self.connect (self.pilot, (self.mix_stereo, 1)) 104 | self.connect (self.pilot, (self.mix_stereo, 2)) 105 | self.connect (self.mix_stereo, self.audio_lmr_filter) 106 | 107 | # create RDS bitstream 108 | # diff-encode, manchester-emcode, NRZ 109 | # enforce the 1187.5bps rate 110 | # pulse shaping filter (matched with receiver) 111 | # mix with 57kHz carrier (equivalent to BPSK) 112 | self.rds_enc = rds.data_encoder('rds_data.xml') 113 | self.diff_enc = gr.diff_encoder_bb(2) 114 | self.manchester1 = gr.map_bb([1,2]) 115 | self.manchester2 = gr.unpack_k_bits_bb(2) 116 | self.nrz = gr.map_bb([-1,1]) 117 | self.c2f = gr.char_to_float() 118 | self.rate_enforcer = rds.rate_enforcer(usrp_rate) 119 | pulse_shaping_taps = gr.firdes.low_pass( 120 | 1, # gain 121 | usrp_rate, # sampling rate 122 | 1.5e3, # passband cutoff 123 | 2e3, # transition width 124 | gr.firdes.WIN_HAMMING) 125 | self.pulse_shaping = gr.fir_filter_fff(1, pulse_shaping_taps) 126 | self.bpsk_mod = gr.multiply_ff() 127 | self.connect (self.rds_enc, self.diff_enc, self.manchester1, \ 128 | self.manchester2, self.nrz, self.c2f) 129 | self.connect (self.c2f, (self.rate_enforcer, 0)) 130 | self.connect (self.pilot, (self.rate_enforcer, 1)) 131 | self.connect (self.rate_enforcer, (self.bpsk_mod, 0)) 132 | self.connect (self.pilot, (self.bpsk_mod, 1)) 133 | self.connect (self.pilot, (self.bpsk_mod, 2)) 134 | self.connect (self.pilot, (self.bpsk_mod, 3)) 135 | 136 | # RDS band-pass filter 137 | rds_filter_taps = gr.firdes.band_pass( 138 | 50, # gain 139 | usrp_rate, # sampling rate 140 | 57e3-3e3, # low cutoff 141 | 57e3+3e3, # high cutoff 142 | 1e3, # transition width 143 | gr.firdes.WIN_HAMMING) 144 | self.rds_filter = gr.fir_filter_fff (1, rds_filter_taps) 145 | self.connect (self.bpsk_mod, self.rds_filter) 146 | 147 | # mix L+R, pilot, L-R and RDS 148 | self.mixer = gr.add_ff() 149 | self.connect (self.audio_lpr_filter, (self.mixer, 0)) 150 | self.connect (self.pilot, (self.mixer, 1)) 151 | self.connect (self.audio_lmr_filter, (self.mixer, 2)) 152 | self.connect (self.rds_filter, (self.mixer, 3)) 153 | 154 | # fm modulation, gain & TX 155 | max_dev = 75e3 156 | k = 2*math.pi*max_dev/usrp_rate # modulator sensitivity 157 | self.modulator = gr.frequency_modulator_fc (k) 158 | self.gain = gr.multiply_const_cc (5e3) 159 | self.connect (self.mixer, self.modulator, self.gain, self.u) 160 | 161 | # plot an FFT to verify we are sending what we want 162 | if 1: 163 | self.fft = fftsink2.fft_sink_f(panel, title="Pre FM modulation", 164 | fft_size=512*4, sample_rate=usrp_rate, y_per_div=20, ref_level=-20) 165 | self.connect (self.mixer, self.fft) 166 | vbox.Add (self.fft.win, 1, wx.EXPAND) 167 | if 0: 168 | self.scope = scopesink2.scope_sink_f(panel, title="RDS encoder output", 169 | sample_rate=usrp_rate) 170 | self.connect (self.rds_enc, self.scope) 171 | vbox.Add (self.scope.win, 1, wx.EXPAND) 172 | 173 | if __name__ == '__main__': 174 | app = stdgui2.stdapp(rds_tx_block, "RDS Tx") 175 | app.MainLoop () 176 | -------------------------------------------------------------------------------- /src/utils/create_vector.grc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Thu Feb 18 14:30:04 2010 4 | 5 | gr_file_source 6 | 7 | id 8 | gr_file_source_0 9 | 10 | 11 | _enabled 12 | False 13 | 14 | 15 | file 16 | /media/dimitris/mywork/gr/dimitris/rds/trunk/src/utils/rds_vector.dat 17 | 18 | 19 | type 20 | byte 21 | 22 | 23 | repeat 24 | True 25 | 26 | 27 | vlen 28 | 1 29 | 30 | 31 | _coordinate 32 | (20, 258) 33 | 34 | 35 | _rotation 36 | 0 37 | 38 | 39 | 40 | gr_rds_data_decoder 41 | 42 | id 43 | gr_rds_data_decoder_0 44 | 45 | 46 | _enabled 47 | False 48 | 49 | 50 | msgq 51 | gr.msg_queue() 52 | 53 | 54 | _coordinate 55 | (516, 278) 56 | 57 | 58 | _rotation 59 | 0 60 | 61 | 62 | 63 | gr_packed_to_unpacked_xx 64 | 65 | id 66 | gr_packed_to_unpacked_xx_0 67 | 68 | 69 | _enabled 70 | False 71 | 72 | 73 | type 74 | byte 75 | 76 | 77 | bits_per_chunk 78 | 1 79 | 80 | 81 | endianness 82 | gr.GR_MSB_FIRST 83 | 84 | 85 | num_ports 86 | 1 87 | 88 | 89 | _coordinate 90 | (265, 263) 91 | 92 | 93 | _rotation 94 | 0 95 | 96 | 97 | 98 | gr_rds_data_encoder 99 | 100 | id 101 | gr_rds_data_encoder_0 102 | 103 | 104 | _enabled 105 | True 106 | 107 | 108 | xmlfile 109 | /media/dimitris/mywork/gr/dimitris/rds/trunk/src/utils/rds_data.xml 110 | 111 | 112 | _coordinate 113 | (204, 112) 114 | 115 | 116 | _rotation 117 | 0 118 | 119 | 120 | 121 | gr_unpacked_to_packed_xx 122 | 123 | id 124 | gr_unpacked_to_packed_xx_0 125 | 126 | 127 | _enabled 128 | True 129 | 130 | 131 | type 132 | byte 133 | 134 | 135 | bits_per_chunk 136 | 1 137 | 138 | 139 | endianness 140 | gr.GR_MSB_FIRST 141 | 142 | 143 | num_ports 144 | 1 145 | 146 | 147 | _coordinate 148 | (466, 104) 149 | 150 | 151 | _rotation 152 | 0 153 | 154 | 155 | 156 | gr_file_sink 157 | 158 | id 159 | gr_file_sink_0 160 | 161 | 162 | _enabled 163 | True 164 | 165 | 166 | file 167 | /media/dimitris/mywork/gr/dimitris/rds/trunk/src/utils/rds_vector.dat 168 | 169 | 170 | type 171 | byte 172 | 173 | 174 | vlen 175 | 1 176 | 177 | 178 | _coordinate 179 | (207, 27) 180 | 181 | 182 | _rotation 183 | 180 184 | 185 | 186 | 187 | gr_head 188 | 189 | id 190 | gr_head_0 191 | 192 | 193 | _enabled 194 | True 195 | 196 | 197 | type 198 | byte 199 | 200 | 201 | num_items 202 | 13 203 | 204 | 205 | vlen 206 | 1 207 | 208 | 209 | _coordinate 210 | (462, 27) 211 | 212 | 213 | _rotation 214 | 180 215 | 216 | 217 | 218 | options 219 | 220 | id 221 | create_vector 222 | 223 | 224 | _enabled 225 | True 226 | 227 | 228 | title 229 | 230 | 231 | 232 | author 233 | 234 | 235 | 236 | description 237 | 238 | 239 | 240 | window_size 241 | 1280, 1024 242 | 243 | 244 | generate_options 245 | no_gui 246 | 247 | 248 | category 249 | Custom 250 | 251 | 252 | run_options 253 | prompt 254 | 255 | 256 | run 257 | True 258 | 259 | 260 | realtime_scheduling 261 | 262 | 263 | 264 | _coordinate 265 | (0, 0) 266 | 267 | 268 | _rotation 269 | 0 270 | 271 | 272 | 273 | gr_unpacked_to_packed_xx_0 274 | gr_head_0 275 | 0 276 | 0 277 | 278 | 279 | gr_packed_to_unpacked_xx_0 280 | gr_rds_data_decoder_0 281 | 0 282 | 0 283 | 284 | 285 | gr_file_source_0 286 | gr_packed_to_unpacked_xx_0 287 | 0 288 | 0 289 | 290 | 291 | gr_head_0 292 | gr_file_sink_0 293 | 0 294 | 0 295 | 296 | 297 | gr_rds_data_encoder_0 298 | gr_unpacked_to_packed_xx_0 299 | 0 300 | 0 301 | 302 | 303 | -------------------------------------------------------------------------------- /src/utils/create_vector.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ################################################## 3 | # Gnuradio Python Flow Graph 4 | # Title: Create Vector 5 | # Generated: Thu Feb 18 14:30:05 2010 6 | ################################################## 7 | 8 | from gnuradio import eng_notation 9 | from gnuradio import gr 10 | from gnuradio import gr, rds 11 | from gnuradio.eng_option import eng_option 12 | from gnuradio.gr import firdes 13 | from optparse import OptionParser 14 | 15 | class create_vector(gr.top_block): 16 | 17 | def __init__(self): 18 | gr.top_block.__init__(self, "Create Vector") 19 | 20 | ################################################## 21 | # Blocks 22 | ################################################## 23 | self.gr_file_sink_0 = gr.file_sink(gr.sizeof_char*1, "/media/dimitris/mywork/gr/dimitris/rds/trunk/src/utils/rds_vector.dat") 24 | self.gr_head_0 = gr.head(gr.sizeof_char*1, 13) 25 | self.gr_rds_data_encoder_0 = rds.data_encoder("/media/dimitris/mywork/gr/dimitris/rds/trunk/src/utils/rds_data.xml") 26 | self.gr_unpacked_to_packed_xx_0 = gr.unpacked_to_packed_bb(1, gr.GR_MSB_FIRST) 27 | 28 | ################################################## 29 | # Connections 30 | ################################################## 31 | self.connect((self.gr_unpacked_to_packed_xx_0, 0), (self.gr_head_0, 0)) 32 | self.connect((self.gr_head_0, 0), (self.gr_file_sink_0, 0)) 33 | self.connect((self.gr_rds_data_encoder_0, 0), (self.gr_unpacked_to_packed_xx_0, 0)) 34 | 35 | if __name__ == '__main__': 36 | parser = OptionParser(option_class=eng_option, usage="%prog: [options]") 37 | (options, args) = parser.parse_args() 38 | tb = create_vector() 39 | tb.start() 40 | raw_input('Press Enter to quit: ') 41 | tb.stop() 42 | 43 | -------------------------------------------------------------------------------- /src/utils/detect_usrps.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | """ 4 | This code detects any USRPs and USRP2s connected, 5 | identifies their daughterboards and prints their frequency range 6 | """ 7 | 8 | from gnuradio import usrp, usrp2 9 | from usrpm import usrp_dbid 10 | import subprocess 11 | 12 | def detect_usrp1(): 13 | for i in range(3): 14 | try: 15 | u = usrp.source_c(i) 16 | print "\033[1;31mUSRP found, serial:", u.serial_number(), "\033[1;m" 17 | a = usrp.selected_subdev(u, (0,0)) 18 | if(a.dbid()!=-1): 19 | print "\033[1;33mSide A, RX:", a.name(), "(dbid: 0x%04X)" % (a.dbid()), "\033[1;m" 20 | print "freq range: (", a.freq_min()/1e6, "MHz, ", a.freq_max()/1e6, "MHz )" 21 | print "gain rainge: (", a.gain_min(), "dB, ", a.gain_max(), "dB )" 22 | b = usrp.selected_subdev(u, (1,0)) 23 | if(b.dbid()!=-1): 24 | print "\033[1;33mSide B, RX:", b.name(), "(dbid: 0x%04X)" % (b.dbid()), "\033[1;m" 25 | print "freq range: (", b.freq_min()/1e6, "MHz, ", b.freq_max()/1e6, "MHz )" 26 | print "gain rainge: (", b.gain_min(), "dB, ", b.gain_max(), "dB )" 27 | u = usrp.sink_c(i) 28 | a = usrp.selected_subdev(u, (0,0)) 29 | if(a.dbid()!=-1): 30 | print "\033[1;33mSide A, TX:", a.name(), "(dbid: 0x%04X)" % (a.dbid()), "\033[1;m" 31 | print "freq range: (", a.freq_min()/1e6, "MHz, ", a.freq_max()/1e6, "MHz )" 32 | print "gain rainge: (", a.gain_min(), "dB, ", a.gain_max(), "dB )" 33 | b = usrp.selected_subdev(u, (1,0)) 34 | if(b.dbid()!=-1): 35 | print "\033[1;33mSide B, TX:", b.name(), "(dbid: 0x%04X)" % (b.dbid()), "\033[1;m" 36 | print "freq range: (", b.freq_min()/1e6, "MHz, ", b.freq_max()/1e6, "MHz )" 37 | print "gain rainge: (", b.gain_min(), "dB, ", b.gain_max(), "dB )" 38 | except: 39 | if(i==0): print "\033[1;31mno USRPs found" 40 | break 41 | 42 | def detect_usrp2(): 43 | interface = 'eth0' 44 | args = ['find_usrps', '-e', interface] 45 | p = subprocess.Popen(args=args, 46 | stdout=subprocess.PIPE, 47 | stderr=subprocess.STDOUT, 48 | shell=False, 49 | universal_newlines=True) 50 | msg = p.stdout.read() 51 | usrp2_macs = sorted(map(lambda l: l.split()[0], filter(lambda l: l.count(':') >= 5, msg.strip().splitlines()))) 52 | if(len(usrp2_macs)==0): 53 | print "\033[1;31mno USRP2's found\033[1;m" 54 | return -1 55 | for usrp2_mac in usrp2_macs: 56 | print "\033[1;31mUSRP2 found, mac address:", usrp2_mac, "\033[1;m" 57 | u2 = usrp2.source_32fc(interface, usrp2_mac) 58 | db = u2.daughterboard_id() 59 | if(db!=-1): 60 | db_name = [k for k, v in usrp_dbid.__dict__.iteritems() if v == db][0] 61 | print "\033[1;33mRX daughterboard:", db_name, "(dbid: 0x%04X)" % (db,), "\033[1;m" 62 | print "freq range: (", u2.freq_min()/1e6, "MHz, ", u2.freq_max()/1e6, "MHz )" 63 | u2 = usrp2.sink_32fc(interface, usrp2_mac) 64 | db = u2.daughterboard_id() 65 | if(db!=-1): 66 | db_name = [k for k, v in usrp_dbid.__dict__.iteritems() if v == db][0] 67 | print "\033[1;33mTX daughterboard:", db_name, "(dbid: 0x%04X)" % (db,), "\033[1;m" 68 | print "freq range: (", u2.freq_min()/1e6, "MHz, ", u2.freq_max()/1e6, "MHz )" 69 | 70 | detect_usrp1() 71 | detect_usrp2() 72 | 73 | -------------------------------------------------------------------------------- /src/utils/fm_tx_mono.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from gnuradio import gr, usrp, blks2 4 | from gnuradio.eng_option import eng_option 5 | from gnuradio.wxgui import stdgui2, fftsink2 6 | from optparse import OptionParser 7 | from usrpm import usrp_dbid 8 | import math, sys, wx 9 | 10 | class fm_tx_block(stdgui2.std_top_block): 11 | def __init__(self, frame, panel, vbox, argv): 12 | stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv) 13 | 14 | parser = OptionParser (option_class=eng_option) 15 | parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, 16 | help="select USRP Tx side A or B") 17 | parser.add_option("-f", "--freq", type="eng_float", default=107.2e6, 18 | help="set Tx frequency to FREQ [required]", metavar="FREQ") 19 | parser.add_option("-i", "--filename", type="string", default="", 20 | help="read input from FILE") 21 | (options, args) = parser.parse_args() 22 | if len(args) != 0: 23 | parser.print_help() 24 | sys.exit(1) 25 | 26 | self.u = usrp.sink_c () 27 | 28 | self.dac_rate = self.u.dac_rate() # 128 MS/s 29 | self.usrp_interp = 400 30 | self.u.set_interp_rate(self.usrp_interp) 31 | self.usrp_rate = self.dac_rate / self.usrp_interp # 320 kS/s 32 | self.sw_interp = 10 33 | self.audio_rate = self.usrp_rate / self.sw_interp # 32 kS/s 34 | 35 | # determine the daughterboard subdevice we're using 36 | if options.tx_subdev_spec is None: 37 | options.tx_subdev_spec = usrp.pick_tx_subdevice(self.u) 38 | self.u.set_mux(usrp.determine_tx_mux_value(self.u, options.tx_subdev_spec)) 39 | self.subdev = usrp.selected_subdev(self.u, options.tx_subdev_spec) 40 | print "Using TX d'board %s" % (self.subdev.side_and_name(),) 41 | 42 | # set max Tx gain, tune frequency and enable transmitter 43 | self.subdev.set_gain(self.subdev.gain_range()[1]) 44 | self.u.tune(self.subdev._which, self.subdev, options.freq) 45 | self.subdev.set_enable(True) 46 | 47 | # open file containing floats in the [-1, 1] range, repeat 48 | self.src = gr.wavfile_source (options.filename, True) 49 | nchans = self.src.channels() 50 | sample_rate = self.src.sample_rate() 51 | bits_per_sample = self.src.bits_per_sample() 52 | print nchans, "channels,", sample_rate, "kS/s,", bits_per_sample, "bits/sample" 53 | 54 | # resample to 32kS/s 55 | if sample_rate == 44100: 56 | self.resample = blks2.rational_resampler_fff(8,11) 57 | elif sample_rate == 48000: 58 | self.resample == blks2.rational_resampler_fff(2,3) 59 | else: 60 | print sample_rate, "is an unsupported sample rate" 61 | exit() 62 | 63 | # interpolation, preemphasis, fm modulation & gain 64 | self.fmtx = blks2.wfm_tx (self.audio_rate, self.usrp_rate, tau=75e-6, max_dev=15e3) 65 | self.gain = gr.multiply_const_cc (4e3) 66 | 67 | # connect it all 68 | self.connect (self.src, self.resample, self.fmtx, self.gain, self.u) 69 | 70 | # plot an FFT to verify we are sending what we want 71 | pre_mod = fftsink2.fft_sink_f(panel, title="Pre-Modulation", 72 | fft_size=512, sample_rate=self.usrp_rate, y_per_div=10, ref_level=0) 73 | self.connect (self.emph, pre_mod) 74 | vbox.Add (pre_mod.win, 1, wx.EXPAND) 75 | 76 | if __name__ == '__main__': 77 | app = stdgui2.stdapp(fm_tx_block, "FM Tx") 78 | app.MainLoop () 79 | -------------------------------------------------------------------------------- /src/utils/fm_tx_stereo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from gnuradio import gr, usrp, blks2 4 | from gnuradio.eng_option import eng_option 5 | from gnuradio.wxgui import stdgui2, fftsink2 6 | from optparse import OptionParser 7 | from usrpm import usrp_dbid 8 | import math, sys, wx 9 | 10 | class fm_tx_block(stdgui2.std_top_block): 11 | def __init__(self, frame, panel, vbox, argv): 12 | stdgui2.std_top_block.__init__ (self, frame, panel, vbox, argv) 13 | 14 | parser = OptionParser (option_class=eng_option) 15 | parser.add_option("-T", "--tx-subdev-spec", type="subdev", default=None, 16 | help="select USRP Tx side A or B") 17 | parser.add_option("-f", "--freq", type="eng_float", default=107.2e6, 18 | help="set Tx frequency to FREQ [required]", metavar="FREQ") 19 | parser.add_option("--wavfile", type="string", default="", 20 | help="read input from FILE") 21 | (options, args) = parser.parse_args() 22 | if len(args) != 0: 23 | parser.print_help() 24 | sys.exit(1) 25 | 26 | self.usrp_interp = 200 27 | self.u = usrp.sink_c (0, self.usrp_interp) 28 | print "USRP Serial: ", self.u.serial_number() 29 | self.dac_rate = self.u.dac_rate() # 128 MS/s 30 | self.usrp_rate = self.dac_rate / self.usrp_interp # 640 kS/s 31 | self.sw_interp = 5 32 | self.audio_rate = self.usrp_rate / self.sw_interp # 128 kS/s 33 | 34 | # determine the daughterboard subdevice we're using 35 | if options.tx_subdev_spec is None: 36 | options.tx_subdev_spec = usrp.pick_tx_subdevice(self.u) 37 | self.u.set_mux(usrp.determine_tx_mux_value(self.u, options.tx_subdev_spec)) 38 | self.subdev = usrp.selected_subdev(self.u, options.tx_subdev_spec) 39 | print "Using d'board: ", self.subdev.side_and_name() 40 | 41 | # set max Tx gain, tune frequency and enable transmitter 42 | self.subdev.set_gain(self.subdev.gain_range()[1]) 43 | if self.u.tune(self.subdev.which(), self.subdev, options.freq): 44 | print "Tuned to", options.freq/1e6, "MHz" 45 | else: 46 | sys.exit(1) 47 | self.subdev.set_enable(True) 48 | 49 | # open wav file containing floats in the [-1, 1] range, repeat 50 | if options.wavfile is None: 51 | print "Please provide a wavfile to transmit! Exiting\n" 52 | sys.exit(1) 53 | self.src = gr.wavfile_source (options.wavfile, True) 54 | nchans = self.src.channels() 55 | sample_rate = self.src.sample_rate() 56 | bits_per_sample = self.src.bits_per_sample() 57 | print nchans, "channels,", sample_rate, "kS/s,", bits_per_sample, "bits/sample" 58 | 59 | # resample to 128kS/s 60 | if sample_rate == 44100: 61 | self.resample_left = blks2.rational_resampler_fff(32,11) 62 | self.resample_right = blks2.rational_resampler_fff(32,11) 63 | elif sample_rate == 48000: 64 | self.resample_left == blks2.rational_resampler_fff(8,3) 65 | self.resample_right == blks2.rational_resampler_fff(8,3) 66 | elif sample_rate == 8000: 67 | self.resample_left == blks2.rational_resampler_fff(16,1) 68 | self.resample_right == blks2.rational_resampler_fff(16,1) 69 | else: 70 | print sample_rate, "is an unsupported sample rate" 71 | sys.exit(1) 72 | self.connect ((self.src, 0), self.resample_left) 73 | self.connect ((self.src, 1), self.resample_right) 74 | 75 | # create L+R (mono) and L-R (stereo) 76 | self.audio_lpr = gr.add_ff() 77 | self.audio_lmr = gr.sub_ff() 78 | self.connect (self.resample_left, (self.audio_lpr, 0)) 79 | self.connect (self.resample_left, (self.audio_lmr, 0)) 80 | self.connect (self.resample_right, (self.audio_lpr, 1)) 81 | self.connect (self.resample_right, (self.audio_lmr, 1)) 82 | 83 | # low-pass filter for L+R 84 | audio_lpr_taps = gr.firdes.low_pass (0.3, # gain 85 | self.audio_rate, # sampling rate 86 | 15e3, # passband cutoff 87 | 2e3, # transition width 88 | gr.firdes.WIN_HANN) 89 | self.audio_lpr_filter = gr.fir_filter_fff (1, audio_lpr_taps) 90 | self.connect (self.audio_lpr, self.audio_lpr_filter) 91 | 92 | # create pilot tone at 19 kHz 93 | self.pilot = gr.sig_source_f(self.audio_rate, # sampling freq 94 | gr.GR_SIN_WAVE, # waveform 95 | 19e3, # frequency 96 | 3e-2) # amplitude 97 | 98 | # create the L-R signal carrier at 38 kHz, high-pass to remove 0Hz tone 99 | self.stereo_carrier = gr.multiply_ff() 100 | self.connect (self.pilot, (self.stereo_carrier, 0)) 101 | self.connect (self.pilot, (self.stereo_carrier, 1)) 102 | stereo_carrier_taps = gr.firdes.high_pass (1, # gain 103 | self.audio_rate, # sampling rate 104 | 1e4, # cutoff freq 105 | 2e3, # transition width 106 | gr.firdes.WIN_HANN) 107 | self.stereo_carrier_filter = gr.fir_filter_fff(1, stereo_carrier_taps) 108 | self.connect (self.stereo_carrier, self.stereo_carrier_filter) 109 | 110 | # upconvert L-R to 23-53 kHz and band-pass 111 | self.mix_stereo = gr.multiply_ff() 112 | audio_lmr_taps = gr.firdes.band_pass (3e3, # gain 113 | self.audio_rate, # sampling rate 114 | 23e3, # low cutoff 115 | 53e3, # high cuttof 116 | 2e3, # transition width 117 | gr.firdes.WIN_HANN) 118 | self.audio_lmr_filter = gr.fir_filter_fff (1, audio_lmr_taps) 119 | self.connect (self.audio_lmr, (self.mix_stereo, 0)) 120 | self.connect (self.stereo_carrier_filter, (self.mix_stereo, 1)) 121 | self.connect (self.mix_stereo, self.audio_lmr_filter) 122 | 123 | # mix L+R, pilot and L-R 124 | self.mixer = gr.add_ff() 125 | self.connect (self.audio_lpr_filter, (self.mixer, 0)) 126 | self.connect (self.pilot, (self.mixer, 1)) 127 | self.connect (self.audio_lmr_filter, (self.mixer, 2)) 128 | 129 | # interpolation & pre-emphasis 130 | interp_taps = gr.firdes.low_pass (self.sw_interp, # gain 131 | self.audio_rate, # Fs 132 | 60e3, # cutoff freq 133 | 5e3, # transition width 134 | gr.firdes.WIN_HAMMING) 135 | self.interpolator = gr.interp_fir_filter_fff (self.sw_interp, interp_taps) 136 | self.pre_emph = blks2.fm_preemph(self.usrp_rate, tau=50e-6) 137 | self.connect (self.mixer, self.interpolator, self.pre_emph) 138 | 139 | # fm modulation, gain & TX 140 | max_dev = 100e3 141 | k = 2 * math.pi * max_dev / self.usrp_rate # modulator sensitivity 142 | self.modulator = gr.frequency_modulator_fc (k) 143 | self.gain = gr.multiply_const_cc (1e3) 144 | self.connect (self.pre_emph, self.modulator, self.gain, self.u) 145 | 146 | # plot an FFT to verify we are sending what we want 147 | pre_mod = fftsink2.fft_sink_f(panel, title="Before Interpolation", 148 | fft_size=512, sample_rate=self.audio_rate, y_per_div=20, ref_level=20) 149 | self.connect (self.mixer, pre_mod) 150 | vbox.Add (pre_mod.win, 1, wx.EXPAND) 151 | 152 | if __name__ == '__main__': 153 | app = stdgui2.stdapp(fm_tx_block, "FM Tx") 154 | app.MainLoop () 155 | -------------------------------------------------------------------------------- /src/utils/group_statistics.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # print group statistics for the rds decoder output log 3 | 4 | if [ $# -ne 1 ]; then 5 | echo "Usage: $0 logfile.log" 6 | exit 1; 7 | fi 8 | if [ ! -f $1 ]; then 9 | echo $1 'not found'; 10 | exit 1; 11 | fi 12 | for i in `seq 0 15`; do 13 | t=`grep $i'A' $1 -c` 14 | if [ $t -ne 0 ]; then echo 'group' $i'A:' $t 'instances'; fi 15 | done 16 | -------------------------------------------------------------------------------- /src/utils/install_usrp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sudo addgroup usrp 4 | sudo addgroup `whoami` usrp 5 | echo ACTION==\"add\",\ SUBSYSTEM==\"usb\",\ ATTR{idVendor}==\"fffe\",\ ATTR{idProduct}==\"0002\",\ GROUP:=\"usrp\",\ MODE:=\"0660\" > tmpfile 6 | sudo chown root.root tmpfile 7 | sudo mv tmpfile /etc/udev/rules.d/10-usrp.rules 8 | echo /usr/local/lib | sudo tee -a /etc/ld.so.conf 9 | sudo ldconfig 10 | 11 | sudo chmod u+s /usr/local/bin/usrp2_socket_opener 12 | [ -a /etc/security/limits.d ] || sudo mkdir /etc/security/limits.d 13 | echo '@usrp - rtprio 50' | sudo tee /etc/security/limits.d/usrp.conf 14 | -------------------------------------------------------------------------------- /src/utils/make_colorgcc_default.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sudo ln -s /usr/bin/colorgcc /usr/local/bin/gcc 3 | sudo ln -s /usr/bin/colorgcc /usr/local/bin/g++ 4 | sudo ln -s /usr/bin/colorgcc /usr/local/bin/cc 5 | sudo ln -s /usr/bin/colorgcc /usr/local/bin/c++ 6 | sudo ldconfig 7 | -------------------------------------------------------------------------------- /src/utils/offline_samples.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # record samples to a .wav file for replaying offline 3 | from gnuradio import gr, usrp 4 | from usrpm import usrp_dbid 5 | import sys 6 | 7 | usrp_decim = 250 8 | freq = 91.2e6 9 | dblist = (usrp_dbid.TV_RX, usrp_dbid.TV_RX_REV_2, usrp_dbid.TV_RX_REV_3, usrp_dbid.BASIC_RX) 10 | 11 | class rds_rx_graph (gr.top_block): 12 | def __init__(self): 13 | gr.top_block.__init__ (self) 14 | 15 | self.u = usrp.source_c(0, usrp_decim) 16 | print "USRP Serial: ", self.u.serial_number() 17 | usrp_rate = self.u.adc_rate() / usrp_decim # 256 kS/s 18 | rx_subdev_spec = usrp.pick_subdev(self.u, dblist) 19 | self.u.set_mux(usrp.determine_rx_mux_value(self.u, rx_subdev_spec)) 20 | self.subdev = usrp.selected_subdev(self.u, rx_subdev_spec) 21 | print "Using d'board", self.subdev.side_and_name() 22 | 23 | self.gain = self.subdev.gain_range()[1] 24 | self.subdev.set_gain(self.gain) 25 | r = usrp.tune(self.u, 0, self.subdev, freq) 26 | if r: 27 | print "Freq: ", freq/1e6, "MHz" 28 | else: 29 | print "Failed to set frequency, quitting!" 30 | sys.exit(1) 31 | 32 | chan_filter_coeffs = gr.firdes.low_pass( 33 | 1.0, # gain 34 | usrp_rate, # sampling rate 35 | 80e3, # passband cutoff 36 | 35e3, # transition width 37 | gr.firdes.WIN_HAMMING) 38 | self.chan_filter = gr.fir_filter_ccf(1, chan_filter_coeffs) 39 | print "# channel filter:", len(chan_filter_coeffs), "taps" 40 | 41 | self.file_sink = gr.file_sink(gr.sizeof_gr_complex*1, "/home/sdr/rds_samples.dat") 42 | 43 | self.connect(self.u, self.chan_filter, self.file_sink) 44 | 45 | if __name__ == '__main__': 46 | tb =rds_rx_graph() 47 | try: 48 | tb.run() 49 | except KeyboardInterrupt: 50 | pass 51 | -------------------------------------------------------------------------------- /src/utils/rds_data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 50FF 5 | 31 6 | true 7 | false 8 | true 9 | 89.8 10 | 102.3 11 | AZIMOUT. 12 | 13 | 18 | 19 | 3 20 | 2 21 | 724 22 | 6126 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/utils/rds_rx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ################################################## 3 | # Gnuradio Python Flow Graph 4 | # Title: Rds Rx 5 | # Generated: Thu Feb 18 13:51:40 2010 6 | ################################################## 7 | 8 | from gnuradio import audio 9 | from gnuradio import eng_notation 10 | from gnuradio import gr 11 | from gnuradio import gr, rds 12 | from gnuradio.eng_option import eng_option 13 | from gnuradio.gr import firdes 14 | from grc_gnuradio import usrp as grc_usrp 15 | from optparse import OptionParser 16 | 17 | class rds_rx(gr.top_block): 18 | 19 | def __init__(self): 20 | gr.top_block.__init__(self, "Rds Rx") 21 | 22 | ################################################## 23 | # Variables 24 | ################################################## 25 | self.usrp_rate = usrp_rate = 256000 26 | self.audio_decim = audio_decim = 8 27 | self.volume = volume = 0.1 28 | self.audio_rate = audio_rate = usrp_rate/audio_decim 29 | 30 | ################################################## 31 | # Blocks 32 | ################################################## 33 | self.audio_sink_0 = audio.sink(32000, "plughw:0,0", False) 34 | self.chan_filter = gr.fir_filter_ccf(1, firdes.low_pass( 35 | 1, usrp_rate, 80e3, 35e3, firdes.WIN_HAMMING, 6.76)) 36 | self.gr_diff_decoder_bb_0 = gr.diff_decoder_bb(2) 37 | self.gr_multiply_xx_0 = gr.multiply_vff(1) 38 | self.gr_multiply_xx_1 = gr.multiply_vff(1) 39 | self.gr_pll_freqdet_cf_0 = gr.pll_freqdet_cf(0.767, 0.147, 2.209, -2.209) 40 | self.gr_rds_bpsk_demod_0 = rds.bpsk_demod(audio_rate) 41 | self.gr_rds_data_decoder_0 = rds.data_decoder(gr.msg_queue()) 42 | self.gr_rds_freq_divider_0 = rds.freq_divider(16) 43 | self.left = gr.add_vff(1) 44 | self.lmr_bb_filter = gr.fir_filter_fff(audio_decim, firdes.low_pass( 45 | 1, usrp_rate, 15e3, 1e3, firdes.WIN_HAMMING, 6.76)) 46 | self.lmr_filter = gr.fir_filter_fff(1, firdes.band_pass( 47 | 1, usrp_rate, 23e3, 53e3, 1e3, firdes.WIN_HAMMING, 6.76)) 48 | self.lpr_filter = gr.fir_filter_fff(audio_decim, firdes.low_pass( 49 | 1, usrp_rate, 15e3, 1e3, firdes.WIN_HAMMING, 6.76)) 50 | self.pilot_filter = gr.fir_filter_fff(1, firdes.band_pass( 51 | 1, usrp_rate, 18.5e3, 19.5e3, 1e3, firdes.WIN_HAMMING, 6.76)) 52 | self.rds_bb_filter = gr.fir_filter_fff(audio_decim, firdes.low_pass( 53 | 1e3, audio_rate, 1.5e3, 2e3, firdes.WIN_HAMMING, 6.76)) 54 | self.rds_clk_filter = gr.fir_filter_fff(audio_decim, firdes.low_pass( 55 | 1, audio_rate, 1.2e3, 1.5e3, firdes.WIN_HAMMING, 6.76)) 56 | self.rds_filter = gr.fir_filter_fff(1, firdes.band_pass( 57 | 1, usrp_rate, 54e3, 60e3, 3e3, firdes.WIN_HAMMING, 6.76)) 58 | self.right = gr.sub_ff(1) 59 | self.usrp_simple_source_x_0 = grc_usrp.simple_source_c(which=0, side="B", rx_ant="RXA") 60 | self.usrp_simple_source_x_0.set_decim_rate(250) 61 | self.usrp_simple_source_x_0.set_frequency(102.2e6, verbose=True) 62 | self.usrp_simple_source_x_0.set_gain(20) 63 | self.vol_left = gr.multiply_const_vff((volume, )) 64 | self.vol_right = gr.multiply_const_vff((volume, )) 65 | 66 | ################################################## 67 | # Connections 68 | ################################################## 69 | self.connect((self.usrp_simple_source_x_0, 0), (self.chan_filter, 0)) 70 | self.connect((self.chan_filter, 0), (self.gr_pll_freqdet_cf_0, 0)) 71 | self.connect((self.gr_pll_freqdet_cf_0, 0), (self.lmr_filter, 0)) 72 | self.connect((self.gr_pll_freqdet_cf_0, 0), (self.pilot_filter, 0)) 73 | self.connect((self.gr_pll_freqdet_cf_0, 0), (self.rds_filter, 0)) 74 | self.connect((self.pilot_filter, 0), (self.gr_multiply_xx_0, 3)) 75 | self.connect((self.pilot_filter, 0), (self.gr_multiply_xx_0, 2)) 76 | self.connect((self.pilot_filter, 0), (self.gr_multiply_xx_0, 1)) 77 | self.connect((self.rds_filter, 0), (self.gr_multiply_xx_0, 0)) 78 | self.connect((self.gr_multiply_xx_0, 0), (self.rds_bb_filter, 0)) 79 | self.connect((self.pilot_filter, 0), (self.gr_rds_freq_divider_0, 0)) 80 | self.connect((self.gr_rds_freq_divider_0, 0), (self.rds_clk_filter, 0)) 81 | self.connect((self.pilot_filter, 0), (self.gr_multiply_xx_1, 0)) 82 | self.connect((self.pilot_filter, 0), (self.gr_multiply_xx_1, 1)) 83 | self.connect((self.lmr_filter, 0), (self.gr_multiply_xx_1, 2)) 84 | self.connect((self.gr_multiply_xx_1, 0), (self.lmr_bb_filter, 0)) 85 | self.connect((self.left, 0), (self.vol_left, 0)) 86 | self.connect((self.vol_left, 0), (self.audio_sink_0, 1)) 87 | self.connect((self.vol_right, 0), (self.audio_sink_0, 0)) 88 | self.connect((self.right, 0), (self.vol_right, 0)) 89 | self.connect((self.lmr_bb_filter, 0), (self.left, 0)) 90 | self.connect((self.lpr_filter, 0), (self.left, 1)) 91 | self.connect((self.lpr_filter, 0), (self.right, 1)) 92 | self.connect((self.lmr_bb_filter, 0), (self.right, 0)) 93 | self.connect((self.gr_pll_freqdet_cf_0, 0), (self.lpr_filter, 0)) 94 | self.connect((self.gr_diff_decoder_bb_0, 0), (self.gr_rds_data_decoder_0, 0)) 95 | self.connect((self.gr_rds_bpsk_demod_0, 0), (self.gr_diff_decoder_bb_0, 0)) 96 | self.connect((self.rds_clk_filter, 0), (self.gr_rds_bpsk_demod_0, 1)) 97 | self.connect((self.rds_bb_filter, 0), (self.gr_rds_bpsk_demod_0, 0)) 98 | 99 | def set_usrp_rate(self, usrp_rate): 100 | self.usrp_rate = usrp_rate 101 | self.set_audio_rate(self.usrp_rate/self.audio_decim) 102 | self.chan_filter.set_taps(firdes.low_pass(1, self.usrp_rate, 80e3, 35e3, firdes.WIN_HAMMING, 6.76)) 103 | self.rds_filter.set_taps(firdes.band_pass(1, self.usrp_rate, 54e3, 60e3, 3e3, firdes.WIN_HAMMING, 6.76)) 104 | self.pilot_filter.set_taps(firdes.band_pass(1, self.usrp_rate, 18.5e3, 19.5e3, 1e3, firdes.WIN_HAMMING, 6.76)) 105 | self.lmr_bb_filter.set_taps(firdes.low_pass(1, self.usrp_rate, 15e3, 1e3, firdes.WIN_HAMMING, 6.76)) 106 | self.lpr_filter.set_taps(firdes.low_pass(1, self.usrp_rate, 15e3, 1e3, firdes.WIN_HAMMING, 6.76)) 107 | self.lmr_filter.set_taps(firdes.band_pass(1, self.usrp_rate, 23e3, 53e3, 1e3, firdes.WIN_HAMMING, 6.76)) 108 | 109 | def set_audio_decim(self, audio_decim): 110 | self.audio_decim = audio_decim 111 | self.set_audio_rate(self.usrp_rate/self.audio_decim) 112 | 113 | def set_volume(self, volume): 114 | self.volume = volume 115 | self.vol_left.set_k((self.volume, )) 116 | self.vol_right.set_k((self.volume, )) 117 | 118 | def set_audio_rate(self, audio_rate): 119 | self.audio_rate = audio_rate 120 | self.rds_bb_filter.set_taps(firdes.low_pass(1e3, self.audio_rate, 1.5e3, 2e3, firdes.WIN_HAMMING, 6.76)) 121 | self.rds_clk_filter.set_taps(firdes.low_pass(1, self.audio_rate, 1.2e3, 1.5e3, firdes.WIN_HAMMING, 6.76)) 122 | 123 | if __name__ == '__main__': 124 | parser = OptionParser(option_class=eng_option, usage="%prog: [options]") 125 | (options, args) = parser.parse_args() 126 | tb = rds_rx() 127 | tb.start() 128 | raw_input('Press Enter to quit: ') 129 | tb.stop() 130 | 131 | -------------------------------------------------------------------------------- /src/utils/rds_syndrome.py: -------------------------------------------------------------------------------- 1 | #!/bin/python 2 | import math 3 | 4 | # an implementation of RDS's syndrome calculation 5 | def rds_syndrome(message, mlen): 6 | POLY = 0x5B9 # 10110111001, g(x)=x^10+x^8+x^7+x^5+x^4+x^3+1 7 | PLEN = 10 8 | OFFSET=[252, 408, 360, 436, 848] 9 | SYNDROME=[383, 14, 303, 663, 748] 10 | OFFSET_NAME=['A', 'B', 'C', 'D', 'C\''] 11 | reg = 0 12 | message=int(message, 16) 13 | if((mlen!=16)and(mlen!=26)): 14 | raise ValueError, "mlen must be either 16 or 26" 15 | # start calculation 16 | for i in range(mlen,0,-1): 17 | reg=(reg<<1)|((message>>(i-1))&0x1) 18 | if(reg&(1<> 1 43 | return bin_string 44 | -------------------------------------------------------------------------------- /src/utils/rds_tx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ################################################## 3 | # Gnuradio Python Flow Graph 4 | # Title: Rds Tx 5 | # Generated: Thu Feb 18 13:49:16 2010 6 | ################################################## 7 | 8 | from gnuradio import blks2 9 | from gnuradio import eng_notation 10 | from gnuradio import gr 11 | from gnuradio import gr, rds 12 | from gnuradio import window 13 | from gnuradio.eng_option import eng_option 14 | from gnuradio.gr import firdes 15 | from gnuradio.wxgui import fftsink2 16 | from grc_gnuradio import usrp as grc_usrp 17 | from grc_gnuradio import wxgui as grc_wxgui 18 | from optparse import OptionParser 19 | import math 20 | import wx 21 | 22 | class rds_tx(grc_wxgui.top_block_gui): 23 | 24 | def __init__(self): 25 | grc_wxgui.top_block_gui.__init__(self, title="Rds Tx") 26 | _icon_path = "/home/azimout/.local/share/icons/hicolor/32x32/apps/gnuradio-grc.png" 27 | self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) 28 | 29 | ################################################## 30 | # Variables 31 | ################################################## 32 | self.usrp_interp = usrp_interp = 500 33 | self.dac_rate = dac_rate = 128e6 34 | self.wav_rate = wav_rate = 44100 35 | self.usrp_rate = usrp_rate = int(dac_rate/usrp_interp) 36 | self.fm_max_dev = fm_max_dev = 120e3 37 | 38 | ################################################## 39 | # Blocks 40 | ################################################## 41 | self.band_pass_filter_0 = gr.interp_fir_filter_fff(1, firdes.band_pass( 42 | 1, usrp_rate, 54e3, 60e3, 3e3, firdes.WIN_HAMMING, 6.76)) 43 | self.band_pass_filter_1 = gr.interp_fir_filter_fff(1, firdes.band_pass( 44 | 1, usrp_rate, 23e3, 53e3, 2e3, firdes.WIN_HAMMING, 6.76)) 45 | self.blks2_rational_resampler_xxx_1 = blks2.rational_resampler_fff( 46 | interpolation=usrp_rate, 47 | decimation=wav_rate, 48 | taps=None, 49 | fractional_bw=None, 50 | ) 51 | self.blks2_rational_resampler_xxx_1_0 = blks2.rational_resampler_fff( 52 | interpolation=usrp_rate, 53 | decimation=wav_rate, 54 | taps=None, 55 | fractional_bw=None, 56 | ) 57 | self.gr_add_xx_0 = gr.add_vff(1) 58 | self.gr_add_xx_1 = gr.add_vff(1) 59 | self.gr_char_to_float_0 = gr.char_to_float() 60 | self.gr_diff_encoder_bb_0 = gr.diff_encoder_bb(2) 61 | self.gr_frequency_modulator_fc_0 = gr.frequency_modulator_fc(2*math.pi*fm_max_dev/usrp_rate) 62 | self.gr_map_bb_0 = gr.map_bb(([-1,1])) 63 | self.gr_map_bb_1 = gr.map_bb(([1,2])) 64 | self.gr_multiply_xx_0 = gr.multiply_vff(1) 65 | self.gr_multiply_xx_1 = gr.multiply_vff(1) 66 | self.gr_rds_data_encoder_0 = rds.data_encoder("/media/dimitris/mywork/gr/dimitris/rds/trunk/src/test/rds_data.xml") 67 | self.gr_rds_rate_enforcer_0 = rds.rate_enforcer(256000) 68 | self.gr_sig_source_x_0 = gr.sig_source_f(usrp_rate, gr.GR_COS_WAVE, 19e3, 0.3, 0) 69 | self.gr_sub_xx_0 = gr.sub_ff(1) 70 | self.gr_unpack_k_bits_bb_0 = gr.unpack_k_bits_bb(2) 71 | self.gr_wavfile_source_0 = gr.wavfile_source("/media/dimitris/mywork/gr/dimitris/rds/trunk/src/python/limmenso_stereo.wav", True) 72 | self.low_pass_filter_0 = gr.interp_fir_filter_fff(1, firdes.low_pass( 73 | 1, usrp_rate, 1.5e3, 2e3, firdes.WIN_HAMMING, 6.76)) 74 | self.low_pass_filter_0_0 = gr.interp_fir_filter_fff(1, firdes.low_pass( 75 | 1, usrp_rate, 15e3, 2e3, firdes.WIN_HAMMING, 6.76)) 76 | self.usrp_simple_sink_x_0 = grc_usrp.simple_sink_c(which=0, side="A") 77 | self.usrp_simple_sink_x_0.set_interp_rate(500) 78 | self.usrp_simple_sink_x_0.set_frequency(107.2e6, verbose=True) 79 | self.usrp_simple_sink_x_0.set_gain(0) 80 | self.usrp_simple_sink_x_0.set_enable(True) 81 | self.usrp_simple_sink_x_0.set_auto_tr(True) 82 | self.wxgui_fftsink2_0 = fftsink2.fft_sink_f( 83 | self.GetWin(), 84 | baseband_freq=0, 85 | y_per_div=20, 86 | y_divs=10, 87 | ref_level=0, 88 | ref_scale=2.0, 89 | sample_rate=usrp_rate, 90 | fft_size=1024, 91 | fft_rate=30, 92 | average=False, 93 | avg_alpha=None, 94 | title="FFT Plot", 95 | peak_hold=False, 96 | ) 97 | self.Add(self.wxgui_fftsink2_0.win) 98 | 99 | ################################################## 100 | # Connections 101 | ################################################## 102 | self.connect((self.gr_sig_source_x_0, 0), (self.gr_rds_rate_enforcer_0, 1)) 103 | self.connect((self.gr_char_to_float_0, 0), (self.gr_rds_rate_enforcer_0, 0)) 104 | self.connect((self.gr_map_bb_0, 0), (self.gr_char_to_float_0, 0)) 105 | self.connect((self.gr_frequency_modulator_fc_0, 0), (self.usrp_simple_sink_x_0, 0)) 106 | self.connect((self.gr_add_xx_1, 0), (self.gr_frequency_modulator_fc_0, 0)) 107 | self.connect((self.gr_sig_source_x_0, 0), (self.gr_add_xx_1, 1)) 108 | self.connect((self.gr_sub_xx_0, 0), (self.gr_multiply_xx_1, 2)) 109 | self.connect((self.gr_sig_source_x_0, 0), (self.gr_multiply_xx_1, 1)) 110 | self.connect((self.gr_sig_source_x_0, 0), (self.gr_multiply_xx_1, 0)) 111 | self.connect((self.gr_sig_source_x_0, 0), (self.gr_multiply_xx_0, 3)) 112 | self.connect((self.gr_sig_source_x_0, 0), (self.gr_multiply_xx_0, 2)) 113 | self.connect((self.blks2_rational_resampler_xxx_1_0, 0), (self.gr_add_xx_0, 1)) 114 | self.connect((self.blks2_rational_resampler_xxx_1, 0), (self.gr_add_xx_0, 0)) 115 | self.connect((self.blks2_rational_resampler_xxx_1_0, 0), (self.gr_sub_xx_0, 1)) 116 | self.connect((self.blks2_rational_resampler_xxx_1, 0), (self.gr_sub_xx_0, 0)) 117 | self.connect((self.gr_wavfile_source_0, 1), (self.blks2_rational_resampler_xxx_1_0, 0)) 118 | self.connect((self.gr_wavfile_source_0, 0), (self.blks2_rational_resampler_xxx_1, 0)) 119 | self.connect((self.gr_rds_data_encoder_0, 0), (self.gr_diff_encoder_bb_0, 0)) 120 | self.connect((self.gr_diff_encoder_bb_0, 0), (self.gr_map_bb_1, 0)) 121 | self.connect((self.gr_map_bb_1, 0), (self.gr_unpack_k_bits_bb_0, 0)) 122 | self.connect((self.gr_unpack_k_bits_bb_0, 0), (self.gr_map_bb_0, 0)) 123 | self.connect((self.gr_rds_rate_enforcer_0, 0), (self.low_pass_filter_0, 0)) 124 | self.connect((self.low_pass_filter_0, 0), (self.gr_multiply_xx_0, 0)) 125 | self.connect((self.gr_multiply_xx_0, 0), (self.band_pass_filter_0, 0)) 126 | self.connect((self.band_pass_filter_0, 0), (self.gr_add_xx_1, 0)) 127 | self.connect((self.gr_multiply_xx_1, 0), (self.band_pass_filter_1, 0)) 128 | self.connect((self.band_pass_filter_1, 0), (self.gr_add_xx_1, 3)) 129 | self.connect((self.gr_add_xx_1, 0), (self.wxgui_fftsink2_0, 0)) 130 | self.connect((self.gr_sig_source_x_0, 0), (self.gr_multiply_xx_0, 1)) 131 | self.connect((self.gr_add_xx_0, 0), (self.low_pass_filter_0_0, 0)) 132 | self.connect((self.low_pass_filter_0_0, 0), (self.gr_add_xx_1, 2)) 133 | 134 | def set_usrp_interp(self, usrp_interp): 135 | self.usrp_interp = usrp_interp 136 | self.set_usrp_rate(int(self.dac_rate/self.usrp_interp)) 137 | 138 | def set_dac_rate(self, dac_rate): 139 | self.dac_rate = dac_rate 140 | self.set_usrp_rate(int(self.dac_rate/self.usrp_interp)) 141 | 142 | def set_wav_rate(self, wav_rate): 143 | self.wav_rate = wav_rate 144 | 145 | def set_usrp_rate(self, usrp_rate): 146 | self.usrp_rate = usrp_rate 147 | self.band_pass_filter_0.set_taps(firdes.band_pass(1, self.usrp_rate, 54e3, 60e3, 3e3, firdes.WIN_HAMMING, 6.76)) 148 | self.band_pass_filter_1.set_taps(firdes.band_pass(1, self.usrp_rate, 23e3, 53e3, 2e3, firdes.WIN_HAMMING, 6.76)) 149 | self.gr_sig_source_x_0.set_sampling_freq(self.usrp_rate) 150 | self.low_pass_filter_0.set_taps(firdes.low_pass(1, self.usrp_rate, 1.5e3, 2e3, firdes.WIN_HAMMING, 6.76)) 151 | self.low_pass_filter_0_0.set_taps(firdes.low_pass(1, self.usrp_rate, 15e3, 2e3, firdes.WIN_HAMMING, 6.76)) 152 | self.wxgui_fftsink2_0.set_sample_rate(self.usrp_rate) 153 | 154 | def set_fm_max_dev(self, fm_max_dev): 155 | self.fm_max_dev = fm_max_dev 156 | 157 | if __name__ == '__main__': 158 | parser = OptionParser(option_class=eng_option, usage="%prog: [options]") 159 | (options, args) = parser.parse_args() 160 | tb = rds_tx() 161 | tb.Run(True) 162 | 163 | -------------------------------------------------------------------------------- /src/utils/rds_vector.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balint256/gr-rds/83bc203d8d68168f6305b9af994e7ab2e82d0330/src/utils/rds_vector.dat -------------------------------------------------------------------------------- /src/utils/testbb.grc: -------------------------------------------------------------------------------- 1 | 2 | 3 | Fri Feb 19 11:42:54 2010 4 | 5 | options 6 | 7 | id 8 | testbb 9 | 10 | 11 | _enabled 12 | True 13 | 14 | 15 | title 16 | 17 | 18 | 19 | author 20 | 21 | 22 | 23 | description 24 | 25 | 26 | 27 | window_size 28 | 1280, 1024 29 | 30 | 31 | generate_options 32 | no_gui 33 | 34 | 35 | category 36 | Custom 37 | 38 | 39 | run_options 40 | prompt 41 | 42 | 43 | run 44 | True 45 | 46 | 47 | realtime_scheduling 48 | 49 | 50 | 51 | _coordinate 52 | (10, 10) 53 | 54 | 55 | _rotation 56 | 0 57 | 58 | 59 | 60 | variable 61 | 62 | id 63 | samp_rate 64 | 65 | 66 | _enabled 67 | True 68 | 69 | 70 | value 71 | 32000 72 | 73 | 74 | _coordinate 75 | (215, 11) 76 | 77 | 78 | _rotation 79 | 0 80 | 81 | 82 | 83 | gr_rds_data_encoder 84 | 85 | id 86 | gr_rds_data_encoder_1 87 | 88 | 89 | _enabled 90 | True 91 | 92 | 93 | xmlfile 94 | /media/dimitris/mywork/gr/dimitris/rds/trunk/src/utils/rds_data.xml 95 | 96 | 97 | _coordinate 98 | (-1, 155) 99 | 100 | 101 | _rotation 102 | 0 103 | 104 | 105 | 106 | gr_rds_data_decoder 107 | 108 | id 109 | gr_rds_data_decoder_0_0 110 | 111 | 112 | _enabled 113 | True 114 | 115 | 116 | msgq 117 | gr.msg_queue() 118 | 119 | 120 | _coordinate 121 | (494, 241) 122 | 123 | 124 | _rotation 125 | 0 126 | 127 | 128 | 129 | gr_head 130 | 131 | id 132 | gr_head_0 133 | 134 | 135 | _enabled 136 | True 137 | 138 | 139 | type 140 | byte 141 | 142 | 143 | num_items 144 | 1040 145 | 146 | 147 | vlen 148 | 1 149 | 150 | 151 | _coordinate 152 | (256, 238) 153 | 154 | 155 | _rotation 156 | 0 157 | 158 | 159 | 160 | gr_add_xx 161 | 162 | id 163 | gr_add_xx_0_0 164 | 165 | 166 | _enabled 167 | False 168 | 169 | 170 | type 171 | float 172 | 173 | 174 | num_inputs 175 | 2 176 | 177 | 178 | vlen 179 | 1 180 | 181 | 182 | _coordinate 183 | (445, 79) 184 | 185 | 186 | _rotation 187 | 180 188 | 189 | 190 | 191 | audio_source 192 | 193 | id 194 | audio_source_0_0 195 | 196 | 197 | _enabled 198 | False 199 | 200 | 201 | samp_rate 202 | 16000 203 | 204 | 205 | device_name 206 | plughw:0,0 207 | 208 | 209 | ok_to_block 210 | True 211 | 212 | 213 | num_outputs 214 | 1 215 | 216 | 217 | _coordinate 218 | (330, 10) 219 | 220 | 221 | _rotation 222 | 0 223 | 224 | 225 | 226 | gr_char_to_float 227 | 228 | id 229 | gr_char_to_float_1 230 | 231 | 232 | _enabled 233 | False 234 | 235 | 236 | _coordinate 237 | (285, 149) 238 | 239 | 240 | _rotation 241 | 0 242 | 243 | 244 | 245 | gr_null_sink 246 | 247 | id 248 | gr_null_sink_0_0 249 | 250 | 251 | _enabled 252 | False 253 | 254 | 255 | type 256 | float 257 | 258 | 259 | vlen 260 | 1 261 | 262 | 263 | _coordinate 264 | (261, 95) 265 | 266 | 267 | _rotation 268 | 180 269 | 270 | 271 | 272 | gr_rds_data_encoder_1 273 | gr_char_to_float_1 274 | 0 275 | 0 276 | 277 | 278 | gr_rds_data_encoder_1 279 | gr_head_0 280 | 0 281 | 0 282 | 283 | 284 | gr_head_0 285 | gr_rds_data_decoder_0_0 286 | 0 287 | 0 288 | 289 | 290 | audio_source_0_0 291 | gr_add_xx_0_0 292 | 0 293 | 1 294 | 295 | 296 | gr_char_to_float_1 297 | gr_add_xx_0_0 298 | 0 299 | 0 300 | 301 | 302 | gr_add_xx_0_0 303 | gr_null_sink_0_0 304 | 0 305 | 0 306 | 307 | 308 | -------------------------------------------------------------------------------- /src/utils/testbb.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | ################################################## 3 | # Gnuradio Python Flow Graph 4 | # Title: Testbb 5 | # Generated: Fri Feb 19 11:42:56 2010 6 | ################################################## 7 | 8 | from gnuradio import eng_notation 9 | from gnuradio import gr 10 | from gnuradio import gr, rds 11 | from gnuradio.eng_option import eng_option 12 | from gnuradio.gr import firdes 13 | from optparse import OptionParser 14 | 15 | class testbb(gr.top_block): 16 | 17 | def __init__(self): 18 | gr.top_block.__init__(self, "Testbb") 19 | 20 | ################################################## 21 | # Variables 22 | ################################################## 23 | self.samp_rate = samp_rate = 32000 24 | 25 | ################################################## 26 | # Blocks 27 | ################################################## 28 | self.gr_head_0 = gr.head(gr.sizeof_char*1, 1040) 29 | self.gr_rds_data_decoder_0_0 = rds.data_decoder(gr.msg_queue()) 30 | self.gr_rds_data_encoder_1 = rds.data_encoder("/media/dimitris/mywork/gr/dimitris/rds/trunk/src/utils/rds_data.xml") 31 | 32 | ################################################## 33 | # Connections 34 | ################################################## 35 | self.connect((self.gr_rds_data_encoder_1, 0), (self.gr_head_0, 0)) 36 | self.connect((self.gr_head_0, 0), (self.gr_rds_data_decoder_0_0, 0)) 37 | 38 | def set_samp_rate(self, samp_rate): 39 | self.samp_rate = samp_rate 40 | 41 | if __name__ == '__main__': 42 | parser = OptionParser(option_class=eng_option, usage="%prog: [options]") 43 | (options, args) = parser.parse_args() 44 | tb = testbb() 45 | tb.start() 46 | raw_input('Press Enter to quit: ') 47 | tb.stop() 48 | 49 | -------------------------------------------------------------------------------- /src/utils/tmc_event_statistics.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # print rds-tmc event statistics 3 | 4 | if [ $# -ne 1 ]; then 5 | echo "Usage: $0 logfile.log" 6 | exit 1; 7 | fi 8 | if [ ! -f $1 ]; then 9 | echo $1 'not found'; 10 | exit 1; 11 | fi 12 | grep 'event:' $1|cut -d " " -f 3|sed -e 's/,//g'|sort|uniq -c|sort -g -r 13 | -------------------------------------------------------------------------------- /src/utils/tmc_location_statistics.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # print rds-tmc event statistics 3 | 4 | if [ $# -ne 1 ]; then 5 | echo "Usage: $0 logfile.log" 6 | exit 1; 7 | fi 8 | if [ ! -f $1 ]; then 9 | echo $1 'not found'; 10 | exit 1; 11 | fi 12 | grep 'event:' $1|cut -d " " -f 4|sort|uniq -c|sort -g -r 13 | --------------------------------------------------------------------------------