├── .gitignore ├── CMakeLists.txt ├── ChanList.csv ├── README.md ├── break.cc ├── cmake └── Modules │ ├── CMakeParseArgumentsCopy.cmake │ ├── FindGnuradioAudio.cmake │ ├── FindGnuradioCore.cmake │ ├── FindGnuradioFCD.cmake │ ├── FindGnuradioFCDPP.cmake │ ├── FindGnuradioIQBalance.cmake │ ├── FindGnuradioUHD.cmake │ ├── FindGruel.cmake │ ├── FindLibDSD.cmake │ ├── FindLibDSD.cmake~ │ ├── FindLibHackRF.cmake │ ├── FindLibMiriSDR.cmake │ ├── FindLibOsmoSDR.cmake │ ├── FindLibOsmoSDR.cmake~ │ ├── FindLibRTLSDR.cmake │ ├── FindLibbladeRF.cmake │ ├── FindOP25.cmake │ ├── FindOP25.cmake~ │ ├── FindUHD.cmake │ ├── GrComponent.cmake │ ├── GrMiscUtils.cmake │ ├── GrPlatform.cmake │ ├── GrPython.cmake │ ├── GrSwig.cmake │ ├── GrTest.cmake │ └── GrVersion.cmake ├── dsd_block_ff.h ├── fsk_demod.cc ├── fsk_demod.h ├── logging_receiver_dsd.cc ├── logging_receiver_dsd.h ├── logging_receiver_p25.cc ├── logging_receiver_p25.h ├── logging_receiver_pocsag.cc ├── logging_receiver_pocsag.h ├── op25-headers ├── data_unit.h ├── data_unit_handler.h ├── imbe_decoder.h └── op25_fsk4_demod_ff.h ├── smartnet.cc ├── smartnet_crc.cc ├── smartnet_crc.h ├── smartnet_deinterleave.cc ├── smartnet_deinterleave.h ├── smartnet_types.h ├── smartnet_wavfile.cc ├── smartnet_wavfile.h ├── smartnet_wavsink.cc ├── smartnet_wavsink.h ├── start_hack_rf_decode_856.sh ├── talkgroup.cc └── talkgroup.h /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | Makefile 4 | cmake_install.cmake 5 | install_manifest.txt 6 | -------------------------------------------------------------------------------- /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 | cmake_minimum_required(VERSION 2.6) 23 | project(gr-osmosdr CXX C) 24 | 25 | set(CMAKE_BUILD_TYPE Debug) 26 | 27 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) 28 | 29 | ######################################################################## 30 | # Find build dependencies 31 | ######################################################################## 32 | find_package(Gruel) 33 | find_package(GnuradioCore) 34 | find_package(GnuradioIQBalance) 35 | find_package(UHD) 36 | find_package(GnuradioUHD) 37 | find_package(GnuradioFCD) 38 | find_package(GnuradioFCDPP) 39 | find_package(LibOsmoSDR) 40 | find_package(LibRTLSDR) 41 | find_package(LibMiriSDR) 42 | find_package(LibHackRF) 43 | find_package(LibbladeRF) 44 | #find_package(LibDSD) 45 | 46 | if(NOT GRUEL_FOUND) 47 | message(FATAL_ERROR "Gruel required to build " ${CMAKE_PROJECT_NAME}) 48 | endif() 49 | 50 | if(NOT GNURADIO_CORE_FOUND) 51 | message(FATAL_ERROR "GnuRadio Core required to build " ${CMAKE_PROJECT_NAME}) 52 | endif() 53 | 54 | 55 | ######################################################################## 56 | # Setup boost 57 | ######################################################################## 58 | MESSAGE(STATUS "Configuring Boost C++ Libraries...") 59 | 60 | # Although not required on my system, some users have linking issues without 61 | SET(BOOST_REQUIRED_COMPONENTS 62 | thread 63 | system 64 | program_options 65 | filesystem 66 | ) 67 | 68 | if(UNIX AND NOT BOOST_ROOT AND EXISTS "/usr/lib64") 69 | list(APPEND BOOST_LIBRARYDIR "/usr/lib64") #fedora 64-bit fix 70 | endif(UNIX AND NOT BOOST_ROOT AND EXISTS "/usr/lib64") 71 | 72 | set(Boost_ADDITIONAL_VERSIONS 73 | "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" 74 | "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" 75 | "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" 76 | "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" 77 | "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" 78 | "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" 79 | "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" 80 | ) 81 | 82 | find_package(Boost COMPONENTS ${BOOST_REQUIRED_COMPONENTS}) 83 | 84 | if(NOT Boost_FOUND) 85 | message(FATAL_ERROR "Boost required to build " ${CMAKE_PROJECT_NAME}) 86 | endif() 87 | 88 | ADD_DEFINITIONS(-DBOOST_ALL_DYN_LINK) 89 | 90 | 91 | ######################################################################## 92 | # Register component 93 | ######################################################################## 94 | #include(GrComponent) 95 | #GR_REGISTER_COMPONENT("gr-audio" ENABLE_GR_AUDIO 96 | # Boost_FOUND 97 | # ENABLE_GR_CORE 98 | #) 99 | 100 | 101 | 102 | 103 | SET(GR_PKG_AUDIO_EXAMPLES_DIR .) 104 | 105 | 106 | 107 | ######################################################################## 108 | # Setup the include and linker paths 109 | ######################################################################## 110 | include_directories( 111 | ${Boost_INCLUDE_DIRS} 112 | ${GRUEL_INCLUDE_DIRS} 113 | ${GNURADIO_CORE_INCLUDE_DIRS} 114 | ${LIBOSMOSDR_INCLUDE_DIR} 115 | ${LIBDSD_INCLUDE_DIR} 116 | /usr/local/include/dsd/ 117 | ./lib/ 118 | ) 119 | 120 | link_directories( 121 | ${Boost_LIBRARY_DIRS} 122 | ${GRUEL_LIBRARY_DIRS} 123 | ${GNURADIO_CORE_LIBRARY_DIRS} 124 | ${LIBOSMOSDR_LIBRARIES} 125 | ${LIBDSD_LIBRARIES} 126 | /usr/local/lib/ 127 | /usr/local/lib/pyton2.7/dist-packages/gnuradio/ 128 | ) 129 | 130 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall") 131 | 132 | add_executable(smartnet smartnet.cc smartnet_crc.cc smartnet_deinterleave.cc logging_receiver_dsd.cc logging_receiver_pocsag.cc talkgroup.cc ) 133 | target_link_libraries(smartnet gnuradio-core gnuradio-audio gnuradio-blocks gnuradio-osmosdr gnuradio-filter gnuradio-digital gnuradio-analog ${GRUEL_LIBRARIES} ${Boost_LIBRARIES} gr-dsd itpp ncurses menu) 134 | 135 | 136 | INSTALL(TARGETS 137 | smartnet 138 | DESTINATION ${GR_PKG_AUDIO_EXAMPLES_DIR} 139 | COMPONENT "audio_examples" 140 | ) 141 | -------------------------------------------------------------------------------- /ChanList.csv: -------------------------------------------------------------------------------- 1 | 1616,65,D,DCFD 01 Disp,01 Dispatch,Fire Dispatch,Fire/EMS,1 2 | 1632,66,D,DCFD 02 Main,02 Main,Fire-Tac,Fire/EMS,1 3 | 1648,67,D,DCFD 03,03 Fireground 3,Fire-Tac,Fire/EMS,1 4 | 1680,69,D,DCFD 05,05 Fireground 5,Fire-Tac,Fire/EMS,1 5 | 1696,06a,D,DCFD 06,06 Fireground 1A,Fire-Tac,Fire/EMS,1 6 | 1712,06b,D,DCFD 07,07 Fireground 7,Fire-Tac,Fire/EMS,1 7 | 1744,6,D,DCFD 09,09 Fireground 9,Fire-Tac,Fire/EMS,1 8 | 1760,6,D,DCFD 010,010 Fireground 2A,Fire-Tac,Fire/EMS,1 9 | 1776,06f,D,DCFD 0A3,0A3 Fireground A3,Fire-Tac,Fire/EMS,1 10 | 1808,71,D,DCFD 0A5,0A5 Fireground A5,Fire-Tac,Fire/EMS,1 11 | 1824,72,D,DCFD 0A6,0A6 Fireground 3A,Fire-Tac,Fire/EMS,1 12 | 1840,73,D,DCFD 0A7,0A7 Fireground A7,Fire-Tac,Fire/EMS,1 13 | 1872,75,D,DCFD 0A9,0A9 Fireground A9,Fire-Tac,Fire/EMS,1 14 | 1888,76,D,DCFD 0A10,0A10 Fireground 4A,Fire-Tac,Fire/EMS,1 15 | 1904,77,D,DCFD 011 EM1,011 EMS 1,EMS-Tac,Fire/EMS,2 16 | 1920,78,D,DCFD 012 EM2,012 EMS 2,EMS-Tac,Fire/EMS,2 17 | 1936,79,D,DCFD 013 EM3,013 EMS 3,EMS-Talk,Fire/EMS,2 18 | 1952,07a,D,DCFD NCR,National Capital Region (sometimes encrypted),Interop,Fire/EMS,1 19 | 1968,07b,D,DCFD 0B10,0B10 Fireground 6A,Fire-Tac,Fire/EMS,1 20 | 2000,7,D,DCFD 0C4 App,0C4 Apparatus Division,Fire-Talk,Fire/EMS,1 21 | 2016,7,D,DCFD 0B3,0B3 Rescue Assignments,Fire-Tac,Fire/EMS,1 22 | 2048,80,D,DCFD 0C11 Tr,0C11 Training Administration,Fire-Tac,Fire/EMS,3 23 | 2064,81,D,DCFD 0C6 Inv,0C6 Investigations,Fire-Tac,Fire/EMS,2 24 | 2080,82,D,DCFD 0C5 Ins,0C5 Inspectors,Fire-Tac,Fire/EMS,2 25 | 2096,83,D,DCFD 0A11 SE,0A11 Special Event 1,Fire-Tac,Fire/EMS,1 26 | 2112,84,D,DCFD 0A12 SE,0A12 Special Event 2,Fire-Tac,Fire/EMS,1 27 | 2128,85,D,DCFD 0A13 SE,0A13 Special Event 3,Fire-Tac,Fire/EMS,1 28 | 2144,86,D,DCFD 0C3 Log,0C3 Logistics,Fire-Tac,Fire/EMS,3 29 | 2160,87,D,DCFD 0B12,0B12 Patch 1,Fire-Tac,Fire/EMS,1 30 | 2176,88,D,DCFD 0B13,0B13 Patch 2,Fire-Tac,Fire/EMS,1 31 | 2192,89,D,DCFD 0B14,0B14 Stage B,Fire-Tac,Fire/EMS,1 32 | 2224,08b,D,DCFD 0B9,0B9 Fireground,Fire-Tac,Fire/EMS,1 33 | 2240,08c,D,DCFD 0C8 SpO,0C8 Special Operations,Fire-Tac,Fire/EMS,1 34 | 2272,8,D,DCFD 0H1 UMC,0H1 United Medical Center,Hospital,Fire/EMS,2 35 | 2288,08f,D,DCFD 0H2 Chd,0H2 Children's,Hospital,Fire/EMS,2 36 | 2304,90,D,DCFD 0H3,0H3 Spare,Hospital,Fire/EMS,2 37 | 2320,91,D,DCFD 0H4 MST,0H4 MedSTAR,Hospital,Fire/EMS,2 38 | 2336,92,D,DCFD 0H5 How,0H5 Howard University,Hospital,Fire/EMS,2 39 | 2352,93,D,DCFD 0H6 DCG,0H6 DC General,Hospital,Fire/EMS,2 40 | 2368,94,D,DCFD 0H7 GU,0H7 Georgetown University,Hospital,Fire/EMS,2 41 | 2384,95,D,DCFD 0H8 GWU,0H8 George Washington University,Hospital,Fire/EMS,2 42 | 2400,96,D,DCFD 0H9,0H9 Children's at United Medical Center,Hospital,Fire/EMS,2 43 | 2416,97,D,DCFD 0H10 Pr,0H10 Providence,Hospital,Fire/EMS,2 44 | 2432,98,D,DCFD 0H11 PC,0H11 Poison Control,Hospital,Fire/EMS,2 45 | 2448,99,D,DCFD 0H12 Si,0H12 Sibley,Hospital,Fire/EMS,2 46 | 2464,09a,D,DCFD 0H13 WH,0H13 Washington Hospital Center,Hospital,Fire/EMS,2 47 | 2480,09b,D,DCFD 0H14 WR,0H14 Walter Reed,Hospital,Fire/EMS,2 48 | 2496,09c,D,DCFD 0H15 Vt,0H15 Veterans,Hospital,Fire/EMS,2 49 | 2512,9,D,DCFD 0H16 PG,0H16 Prince George's,Hospital,Fire/EMS,2 50 | 2592,0a2,D,DCFD 0C7 MPD,0C7 MPD-DCFD Interoperability,Interop,Fire/EMS,1 51 | 2608,0a3,D,DCFD 0C15,0C15 Admin 2,Fire-Talk,Fire/EMS,2 52 | 2640,0a5,D,DCFD 0C9 FBt,0C9 Fire Boat,Fire-Tac,Fire/EMS,1 53 | 2656,0a6,D,DC-01 UCC1,Unified Communications Center 1,Interop,DC Common,1 54 | 2672,0a7,D,DC-02 UCC2,Unified Communications Center 2,Interop,DC Common,1 55 | 2720,0aa,D,DCFD 014,014 Stage 0,Fire-Tac,Fire/EMS,1 56 | 2736,0ab,D,DCFD 0A14,0A14 Stage A,Fire-Tac,Fire/EMS,1 57 | 2752,0ac,E,DCFD Encrypt,Encrypted,Fire-Talk,Fire/EMS,3 58 | 2848,0b2,D,DCFD 0C16,0C16 Admin 3,Fire-Tac,Fire/EMS,3 59 | 2864,0b3,D,DCFD 0B5,0B5 Fireground,Fire-Tac,Fire/EMS,1 60 | 2880,0b4,D,DCFD 0B6,0B6 Fireground 5A,Fire-Tac,Fire/EMS,1 61 | 9808,265,D,DCFD 0G12,0G12 Tactical/Fireground 3,Fire-Tac,Fire/EMS,1 62 | 9824,266,D,DCFD 0C10,0C10 Spare,Fire-Tac,Fire/EMS,2 63 | 9840,267,D,DCFD 0B7,0B7 Ops Command,Fire-Tac,Fire/EMS,1 64 | 9872,269,D,DCFD 0C14,0C14 Admin 1,Fire-Tac,Fire/EMS,1 65 | 9936,26,D,DC-07 MAF1,Mutual Aid Fire 1,Interop,DC Common,1 66 | 9968,26f,D,DC-08 MAF2,Mutual Aid Fire 2,Interop,DC Common,1 67 | 9984,270,D,DCFD MAF3,Mutual Aid Fire 3,Interop,Fire/EMS,1 68 | 10032,273,D,DCFD MAF4,Mutual Aid Fire 4,Interop,Fire/EMS,1 69 | 16624,40f,D,DC-16 CW-1,MPD Citywide 1,Law Dispatch,DC Common,2 70 | 19248,4b3,D,DC-13 PMARS,Police Mutual Aid Radio System (PMARS),Interop,DC Common,1 71 | 33584,833,D,DC Hlth Tac,Department of Health Tac,Public Works,Services,3 72 | 33616,835,D,DC-11 Exec1,Executive 1 (Senior City Officials),Interop,DC Common,1 73 | 33648,837,D,DC-12 Exec2,Executive 2 (City Officials),Interop,DC Common,1 74 | 33840,843,D,DC ReeveSec?,Reeves Center Security,Public Works,Services,3 75 | 33872,845,D,DC EMA 33872,Emergency Management Agency,Emergency Ops,Services,1 76 | 33904,847,D,DC EMA 33904,Emergency Management Agency,Emergency Ops,Services,1 77 | 34128,855,D,DC Sch Sec,School Security,Security,Services,3 78 | 34192,859,D,DC Sec 34192,Medical Holding Unit (jail-hospital-court prisoner transports) ,Security,Services,3 79 | 34288,85f,D,StEliz Mnt,St. Elizabeth's Hospital - Maintenance/Motor Pool,Public Works,Services,3 80 | 34320,861,D,StEliz Ops 1,St. Elizabeth's Hospital - Ops 1,Public Works,Services,3 81 | 34352,863,D,StEliz Sec,St. Elizabeth's Hospital - Security,Security,Services,3 82 | 34384,865,D,StEliz Ops 2,St. Elizabeth's Hospital - Ops 2,Public Works,Services,3 83 | 34368,864,D,StEliz Mnt,St. Elizabeth's Hospital - Maintenance,Public Works,Services,3 84 | 34416,867,D,DC DHS Ops,Department of Human Services Ops,Public Works,Services,3 85 | 34448,869,D,DC Health,Department of Health,Public Works,Services,3 86 | 34480,86b,D,DC Med Exam,Office of the Chief Medical Examiner (OCME),Public Works,Services,1 87 | 34512,86,D,DDOT TCO,Department of Transportation Traffic Control Officers,Public Works,Services,3 88 | 34576,871,D,DDOT 34576,Department of Transportation,Public Works,Services,3 89 | 34608,873,D,DC Pkg Enf,Parking Enforcement,Public Works,Services,3 90 | 34672,877,D,DC 34672,(Active during 2006 National Marathon),Public Works,Services,3 91 | 34800,87f,D,DC PkgEnf T2,Parking Enforcement Tac 2,Public Works,Services,3 92 | 34832,881,D,DC PrtSrvDsp,DC Protective Services Dispatch,Security,Services,3 93 | 34864,883,D,DC PrtSrvTac,DC Protective Services Tactical,Security,Services,3 94 | 35024,88,D,DC WASA 1,Water and Sewer Authority 1,Public Works,Services,3 95 | 35056,88f,D,DC WASA 2,Water and Sewer Authority 2,Public Works,Services,3 96 | 35088,891,D,DC WASA 3,Water and Sewer Authority 3,Public Works,Services,3 97 | 35152,895,D,DC Lib Sec,Library Security,Security,Services,3 98 | 35184,897,D,DC 35184,Coordination of 2007 National Fire and Emergency Services Dinner and Seminars,Public Works,Services,3 99 | 35216,899,D,DC 35216,Coordination of 2007 National Fire and Emergency Services Dinner and Seminars--Shuttle,Public Works,Services,3 100 | 35248,89b,D,DC Parks&Rec,Parks and Recreation,Public Works,Services,2 101 | 35408,8a5,D,DC UDC PD,University of the District of Columbia Police,Law Dispatch,Services,3 102 | 35440,8a7,D,DC UDC PD T,University of the District of Columbia Police Tac,Law Tac,Services,3 103 | 35536,8ad,D,DC-09 EMA1,Emergency Management Agency 1,Interop,DC Common,1 104 | 35568,8af,D,DC-10 EMA2,Emergency Management Agency 2,Interop,DC Common,1 105 | 35600,8b1,D,DC TraffMgmt,Traffic Management Center (TMC),Public Works,Services,3 106 | 35632,8b3,D,DC Streetcar Streetcar Testing,Transportation,Services ,Services,3 107 | 35664,8b5,D,DC Paratrans,Paratransit,Transportation,Services,3 108 | 36880,901,D,DC Hosp Roll,Hospital Roll Call,Hospital,Services,3 109 | 37040,90b,D,DC MotorPool,Motor Pool,Public Works,Services,3 110 | 37200,915,D,DC MPD FlMt?,MPD Fleet Maintenance?,Public Works,Services,3 111 | 37232,917,D,DC UDC PD T,University of the District of Columbia Police Tac,Law Tac,Services,3 112 | 37328,91,D,DC Pub Work?,Public Works?,Public Works,Services,3 113 | 37456,925,D,DC-03 UCC3,Unified Communications Center 3,Interop,DC Common,3 114 | 37488,927,D,DC-04 UCC4,Unified Communications Center 4,Interop,DC Common,3 115 | 37648,931,D,DC-14 Spare1,Spare 1,Interop,DC Common,3 116 | 37680,933,D,DC-15 Spare2,Spare 2,Interop,DC Common,3 117 | 40000,9c4,D,DCFD AutoDsp,Automated Dispatch,Fire Dispatch,Fire/EMS,1 118 | 40032,9c6,D,DCFD AutoDsp,Automated Dispatch,Fire Dispatch,Fire/EMS,1 119 | 40080,9c9,D,DC Taxi Comm,Taxicab Commission,Public Works,Services,2 120 | 56000,dac,A,DCFD MOSCAD,MOSCAD Data,Data,Fire/EMS,3 121 | 56016,dad,A,DCFD MOSCAD,MOSCAD Data,Data,Fire/EMS,3 122 | 59952,ea3,D,DC-05 MA1,Mutual Aid 1,Interop,DC Common,1 123 | 59968,ea4,D,DC-06 MA2,Mutual Aid 2 (also DC Interop 1),Interop,DC Common,1 124 | 59984,ea5,D,MPD MA3,Mutual Aid 3,Interop,Police,1 125 | 60000,ea6,D,MPD MA4,Mutual Aid 4,Interop,Police,1 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | smartnet-recorder 2 | ================= 3 | 4 | Uses a HackRF to record all of the radio transmissions on a SmartNet II system. 5 | 6 | After a bit of work, I have put together a system that lets you monitor the radio system for DC Fire, EMS & City Services. Everything gets recorded and can be played back through a website. Thanks to the magic websockets, any new call that comes in gets added to the top of the list. Better yet, if you hit the Autoplay button in the upper left hand corner, it will automatically play through the list of calls. You can narrow the list of calls display to specific group using the filters. 7 | 8 | Anyhow, give it a try at openmhz.com and let me know what you think. If you want more background on how it works, read on… 9 | 10 | ###HackRF 11 | Software Defined Radios are pretty awesome. For the uninitiated, they let you receive a lot of radio signal over a wide range of frequencies. It pass you the raw information and you use a computer to filter out a transmission and process the signal. There is tons of flexibility… and that is also a challenge. There are a lot of rough edges and ways you can mess things up. That is half the fun though, boldly coding where few have coded before. 12 | 13 | I was one of the lucky recipients of a great SDR, the HackRF Jawbreaker. It is an amazing piece of Hardware, capable of sending or receiving 20MHz of spectrum between ~13MHz – 6GHz. It is Open Source Hardware too, so if you are wicked smart you can build your own boards. The design work for it was funded by DARPA and I got one of the boards from that pre-production run. In order to go into production, a Kickstarter project was put together. The original target amount was $80k and it quickly blew through that and ended up raising $600k. One of the most impressive stats is that the target price is $300. 14 | 15 | You can do a lot of things with this board. It use the popular Osmosdr drivers, so it works with a lot of existing things and plugs right into GNURadio. 16 | 17 | There are an endless number of things to try. What I have been focusing on is trying to monitor the radio system that the Washington, DC Fire/EMS & City Services use. Of course monitoring a radio system is nothing new. Radio Shack sells a bunch of different scanners that can do it. However, these scanners can only follow one conversation on a system on a time. Since an SDR can receive a wide swatch a spectrum at once and all the processing happens on the computer, you can decode multiple transmissions. Since you are doing the processing on a computer you can easily save and archive the transmissions, which you sort of needed since you could be getting a couple at once. 18 | 19 | Luckily for me, a couple of people have already setup systems that do exactly that. The code from Nick Foster, GR-SmartNet, seems to be the first out there, and the only publicly available code. The Super Trunking Scanner took it a step further and made it playable over the web. It monitor a trunked system with analog channels. The Radio Capture system took a similar approach, except made it work for a system with digital voice channels. 20 | 21 | ###How Trunking Works 22 | Here is a little background on trunking radio systems, for those not familiar. In a Trunking system, one of the radio channels is set aside for to manage the assignment of radio channels to talkgroups. When someone wants to talk, they send a message on the control channel. The system then assigns them a channel and sends a Channel Grant message on the control channel. This lets the talker know what channel to transmit on and anyone who is a member of the talkgroup know that they should listen to that channel. 23 | 24 | In order to follow all of the transmissions, this system constantly listens to and decodes the control channel. When a channel is granted to a talkgroup, the system creates a monitoring process. This process will start to process and decode the part of the radio spectrum for that channel which the SDR is already pulling in. In the DC system, the audio is digitally encoded using the P25 CAI process. Decoding it is a bit of pain. I am taking a quick and dirty approach right now and have shoe horned in the DSD program. In the future I would like to try using the code from the OP25 project and a hardware dongle for the decoding. Unfortunately, the dongle is $500, so that might not be happening too soon. 25 | 26 | No message is transmitted on the control channel when a talkgroup’s conversation is over. So instead the monitoring process keeps track of transmissions and if there has been no activity for 5 seconds, it ends the recording and uploads to the webserver. I convert the WAV file that gets recorder into an MP3 file. Since the audio is original converted to digital by the radio system, put it through another lossy digital conversion is probably not a good idea, but sending the full-size WAV file ate up too much space. 27 | 28 | ###My Setup 29 | The monitoring and recording is being run off of a laptop in my apartment and uses a crappy antenna. The website is run off a VPS I have running up in the magical cloud. 30 | 31 | The webserver is pretty simple. It is written in NodeJs. The audio is stored as WAV files and indexed using MongoDB. The server simply watches for new files being placed in a directory and then moves them and adds them to the DB. Socket.io is used to updated all of the browsers visiting the site that a new transmission has been added. 32 | 33 | ###The Code 34 | The recorder portion of the system is C++ code that uses GnuRadio 3.6.5.1. 35 | 36 | The recorder uses DSD to decode the digital audio. Unfortunately DSD isn’t supported or being developed, isn’t designed to work with GNURadio or SDR. Luckily someone wrapped DSD into a GNURadio block. It works, but it isn’t pretty. I had to futz with it a bit to run concurrently. My version is here. 37 | 38 | The final portion is the website for listening to the recordings. The code for that is available here. 39 | 40 | ###The Punch List 41 | Right now, I think everything is pretty much stable. I have a small memory leak somewhere, but I can keep it up for long time without it being a problem. 42 | 43 | * Upgrade everything to GNURadio 3.7. Right now I am on the 3.6 branch and it will take a bit of work to switch. 44 | * Get OP25 working nicely. 45 | 46 | The code is designed to work with GR and is being actively developed. 47 | -------------------------------------------------------------------------------- /break.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | static void connect(gr_top_block_sptr block, gr_sig_source_f_sptr 8 | source, 9 | gr_hier_block2_sptr block2) { 10 | fprintf(stderr, "connect: calling lock, connect, unlock\n"); 11 | block->lock(); 12 | block->connect(source, 0, block2, 0); 13 | block->unlock(); 14 | fprintf(stderr, "connect: done\n"); 15 | } 16 | 17 | static void disconnect(gr_top_block_sptr block, gr_sig_source_f_sptr 18 | source, 19 | gr_hier_block2_sptr block2) { 20 | fprintf(stderr, "disconnect: calling block->lock\n"); 21 | block->lock(); 22 | 23 | fprintf(stderr, "disconnect: calling block->disconnect\n"); 24 | block->disconnect(source, 0, block2, 0); 25 | 26 | fprintf(stderr, "disconnect: calling block->unlock\n"); 27 | block->unlock(); // It usually hangs here. 28 | 29 | fprintf(stderr, "disconnect: done\n"); 30 | } 31 | 32 | int main(int argc, char** argv) { 33 | // Inner block: block to sink. 34 | gr_hier_block2_sptr inner; 35 | inner = gr_make_hier_block2("inner", 36 | gr_make_io_signature(1, 1, sizeof(float)), 37 | gr_make_io_signature(0, 0, 0)); 38 | 39 | gr_file_sink_sptr sink; 40 | sink = gr_make_file_sink(sizeof(float), "/dev/null"); 41 | inner->connect(inner, 0, sink, 0); 42 | 43 | // Outer block: signal source to inner block. 44 | gr_top_block_sptr outer = gr_make_top_block("outer"); 45 | gr_sig_source_f_sptr src = gr_make_sig_source_f(11025, GR_COS_WAVE, 46 | 400, .1, 0); 47 | 48 | // Hook it up and get it going. 49 | connect(outer, src, inner); 50 | outer->start(); 51 | 52 | // Frob it until we die. 53 | while (true) { 54 | disconnect(outer, src, inner); 55 | fprintf(stderr, "\n\n------------------------\n\n"); 56 | 57 | connect(outer, src, inner); 58 | } 59 | 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /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/FindGnuradioAudio.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_GNURADIO_AUDIO gnuradio-audio) 3 | 4 | FIND_PATH( 5 | GNURADIO_AUDIO_INCLUDE_DIRS 6 | NAMES gnuradio/gr_audio_api.h 7 | HINTS $ENV{GNURADIO_AUDIO_DIR}/include 8 | ${PC_GNURADIO_AUDIO_INCLUDEDIR} 9 | PATHS /usr/local/include 10 | /usr/include 11 | ) 12 | 13 | FIND_LIBRARY( 14 | GNURADIO_AUDIO_LIBRARIES 15 | NAMES gnuradio-audio 16 | HINTS $ENV{GNURADIO_AUDIO_DIR}/lib 17 | ${PC_GNURADIO_AUDIO_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_AUDIO DEFAULT_MSG GNURADIO_AUDIO_LIBRARIES GNURADIO_AUDIO_INCLUDE_DIRS) 26 | MARK_AS_ADVANCED(GNURADIO_AUDIO_LIBRARIES GNURADIO_AUDIO_INCLUDE_DIRS) 27 | -------------------------------------------------------------------------------- /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_core_api.h 7 | HINTS $ENV{GNURADIO_CORE_DIR}/include/gnuradio 8 | ${PC_GNURADIO_CORE_INCLUDEDIR} 9 | ${CMAKE_INSTALL_PREFIX}/include/gnuradio 10 | PATHS /usr/local/include/gnuradio 11 | /usr/include/gnuradio 12 | ) 13 | 14 | FIND_LIBRARY( 15 | GNURADIO_CORE_LIBRARIES 16 | NAMES gnuradio-core 17 | HINTS $ENV{GNURADIO_CORE_DIR}/lib 18 | ${PC_GNURADIO_CORE_LIBDIR} 19 | ${CMAKE_INSTALL_PREFIX}/lib64 20 | ${CMAKE_INSTALL_PREFIX}/lib 21 | PATHS /usr/local/lib 22 | /usr/local/lib64 23 | /usr/lib 24 | /usr/lib64 25 | ) 26 | 27 | INCLUDE(FindPackageHandleStandardArgs) 28 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_CORE DEFAULT_MSG GNURADIO_CORE_LIBRARIES GNURADIO_CORE_INCLUDE_DIRS) 29 | MARK_AS_ADVANCED(GNURADIO_CORE_LIBRARIES GNURADIO_CORE_INCLUDE_DIRS) 30 | -------------------------------------------------------------------------------- /cmake/Modules/FindGnuradioFCD.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_GNURADIO_FCD gnuradio-fcd) 3 | 4 | FIND_PATH( 5 | GNURADIO_FCD_INCLUDE_DIRS 6 | NAMES gnuradio/fcd_api.h 7 | HINTS $ENV{GNURADIO_FCD_DIR}/include 8 | ${PC_GNURADIO_FCD_INCLUDEDIR} 9 | PATHS /usr/local/include 10 | /usr/include 11 | ) 12 | 13 | FIND_LIBRARY( 14 | GNURADIO_FCD_LIBRARIES 15 | NAMES gnuradio-fcd 16 | HINTS $ENV{GNURADIO_FCD_DIR}/lib 17 | ${PC_GNURADIO_FCD_LIBDIR} 18 | PATHS /usr/local/lib 19 | /usr/local/lib64 20 | /usr/lib 21 | /usr/lib64 22 | ) 23 | 24 | if(GNURADIO_FCD_INCLUDE_DIRS AND GNURADIO_FCD_LIBRARIES) 25 | set(GNURADIO_FCD_FOUND TRUE CACHE INTERNAL "gnuradio-fcd found") 26 | message(STATUS "Found gnuradio-fcd: ${GNURADIO_FCD_INCLUDE_DIRS}, ${GNURADIO_FCD_LIBRARIES}") 27 | else(GNURADIO_FCD_INCLUDE_DIRS AND GNURADIO_FCD_LIBRARIES) 28 | set(GNURADIO_FCD_FOUND FALSE CACHE INTERNAL "gnuradio-fcd found") 29 | message(STATUS "gnuradio-fcd not found.") 30 | endif(GNURADIO_FCD_INCLUDE_DIRS AND GNURADIO_FCD_LIBRARIES) 31 | 32 | INCLUDE(FindPackageHandleStandardArgs) 33 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_FCD DEFAULT_MSG GNURADIO_FCD_LIBRARIES GNURADIO_FCD_INCLUDE_DIRS) 34 | MARK_AS_ADVANCED(GNURADIO_FCD_LIBRARIES GNURADIO_FCD_INCLUDE_DIRS) 35 | -------------------------------------------------------------------------------- /cmake/Modules/FindGnuradioFCDPP.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_GNURADIO_FCDPP gnuradio-fcdproplus) 3 | 4 | FIND_PATH( 5 | GNURADIO_FCDPP_INCLUDE_DIRS 6 | NAMES fcdproplus/api.h 7 | HINTS $ENV{GNURADIO_FCDPP_DIR}/include 8 | ${PC_GNURADIO_FCDPP_INCLUDEDIR} 9 | PATHS /usr/local/include 10 | /usr/include 11 | ) 12 | 13 | FIND_LIBRARY( 14 | GNURADIO_FCDPP_LIBRARIES 15 | NAMES gnuradio-fcdproplus 16 | HINTS $ENV{GNURADIO_FCDPP_DIR}/lib 17 | ${PC_GNURADIO_FCDPP_LIBDIR} 18 | PATHS /usr/local/lib 19 | /usr/local/lib64 20 | /usr/lib 21 | /usr/lib64 22 | ) 23 | 24 | if(GNURADIO_FCDPP_INCLUDE_DIRS AND GNURADIO_FCDPP_LIBRARIES) 25 | set(GNURADIO_FCDPP_FOUND TRUE CACHE INTERNAL "gnuradio-fcdproplus found") 26 | message(STATUS "Found gnuradio-fcdproplus: ${GNURADIO_FCDPP_INCLUDE_DIRS}, ${GNURADIO_FCDPP_LIBRARIES}") 27 | else(GNURADIO_FCDPP_INCLUDE_DIRS AND GNURADIO_FCDPP_LIBRARIES) 28 | set(GNURADIO_FCDPP_FOUND FALSE CACHE INTERNAL "gnuradio-fcdproplus found") 29 | message(STATUS "gnuradio-fcdproplus not found.") 30 | endif(GNURADIO_FCDPP_INCLUDE_DIRS AND GNURADIO_FCDPP_LIBRARIES) 31 | 32 | INCLUDE(FindPackageHandleStandardArgs) 33 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_FCDPP DEFAULT_MSG GNURADIO_FCDPP_LIBRARIES GNURADIO_FCDPP_INCLUDE_DIRS) 34 | MARK_AS_ADVANCED(GNURADIO_FCDPP_LIBRARIES GNURADIO_FCDPP_INCLUDE_DIRS) 35 | -------------------------------------------------------------------------------- /cmake/Modules/FindGnuradioIQBalance.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_GNURADIO_IQBALANCE gnuradio-iqbalance) 3 | 4 | FIND_PATH( 5 | GNURADIO_IQBALANCE_INCLUDE_DIRS 6 | NAMES iqbalance_api.h 7 | HINTS $ENV{GNURADIO_IQBALANCE_DIR}/include/iqbalance 8 | ${PC_GNURADIO_IQBALANCE_INCLUDEDIR} 9 | ${CMAKE_INSTALL_PREFIX}/include/iqbalance 10 | PATHS /usr/local/include/iqbalance 11 | /usr/include/iqbalance 12 | ) 13 | 14 | FIND_LIBRARY( 15 | GNURADIO_IQBALANCE_LIBRARIES 16 | NAMES gnuradio-iqbalance 17 | HINTS $ENV{GNURADIO_IQBALANCE_DIR}/lib 18 | ${PC_GNURADIO_IQBALANCE_LIBDIR} 19 | ${CMAKE_INSTALL_PREFIX}/lib64 20 | ${CMAKE_INSTALL_PREFIX}/lib 21 | PATHS /usr/local/lib 22 | /usr/local/lib64 23 | /usr/lib 24 | /usr/lib64 25 | ) 26 | 27 | INCLUDE(FindPackageHandleStandardArgs) 28 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_IQBALANCE DEFAULT_MSG GNURADIO_IQBALANCE_LIBRARIES GNURADIO_IQBALANCE_INCLUDE_DIRS) 29 | MARK_AS_ADVANCED(GNURADIO_IQBALANCE_LIBRARIES GNURADIO_IQBALANCE_INCLUDE_DIRS) 30 | -------------------------------------------------------------------------------- /cmake/Modules/FindGnuradioUHD.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_GNURADIO_UHD gnuradio-uhd) 3 | 4 | FIND_PATH( 5 | GNURADIO_UHD_INCLUDE_DIRS 6 | NAMES gnuradio/gr_uhd_api.h 7 | HINTS $ENV{GNURADIO_UHD_DIR}/include 8 | ${PC_GNURADIO_UHD_INCLUDEDIR} 9 | PATHS /usr/local/include 10 | /usr/include 11 | ) 12 | 13 | FIND_LIBRARY( 14 | GNURADIO_UHD_LIBRARIES 15 | NAMES gnuradio-uhd 16 | HINTS $ENV{GNURADIO_UHD_DIR}/lib 17 | ${PC_GNURADIO_UHD_LIBDIR} 18 | PATHS /usr/local/lib 19 | /usr/local/lib64 20 | /usr/lib 21 | /usr/lib64 22 | ) 23 | 24 | if(GNURADIO_UHD_INCLUDE_DIRS AND GNURADIO_UHD_LIBRARIES) 25 | set(GNURADIO_UHD_FOUND TRUE CACHE INTERNAL "gnuradio-uhd found") 26 | message(STATUS "Found gnuradio-uhd: ${GNURADIO_UHD_INCLUDE_DIRS}, ${GNURADIO_UHD_LIBRARIES}") 27 | else(GNURADIO_UHD_INCLUDE_DIRS AND GNURADIO_UHD_LIBRARIES) 28 | set(GNURADIO_UHD_FOUND FALSE CACHE INTERNAL "gnuradio-uhd found") 29 | message(STATUS "gnuradio-uhd not found.") 30 | endif(GNURADIO_UHD_INCLUDE_DIRS AND GNURADIO_UHD_LIBRARIES) 31 | 32 | INCLUDE(FindPackageHandleStandardArgs) 33 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_UHD DEFAULT_MSG GNURADIO_UHD_LIBRARIES GNURADIO_UHD_INCLUDE_DIRS) 34 | MARK_AS_ADVANCED(GNURADIO_UHD_LIBRARIES GNURADIO_UHD_INCLUDE_DIRS) 35 | -------------------------------------------------------------------------------- /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 | ${CMAKE_INSTALL_PREFIX}/include 10 | PATHS /usr/local/include 11 | /usr/include 12 | ) 13 | 14 | FIND_LIBRARY( 15 | GRUEL_LIBRARIES 16 | NAMES gruel 17 | HINTS $ENV{GRUEL_DIR}/lib 18 | ${PC_GRUEL_LIBDIR} 19 | ${CMAKE_INSTALL_PREFIX}/lib 20 | ${CMAKE_INSTALL_PREFIX}/lib64 21 | PATHS /usr/local/lib 22 | /usr/local/lib64 23 | /usr/lib 24 | /usr/lib64 25 | ) 26 | 27 | INCLUDE(FindPackageHandleStandardArgs) 28 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(GRUEL DEFAULT_MSG GRUEL_LIBRARIES GRUEL_INCLUDE_DIRS) 29 | MARK_AS_ADVANCED(GRUEL_LIBRARIES GRUEL_INCLUDE_DIRS) 30 | -------------------------------------------------------------------------------- /cmake/Modules/FindLibDSD.cmake: -------------------------------------------------------------------------------- 1 | if(NOT LIBDSD_FOUND) 2 | pkg_check_modules (LIBDSD_PKG libdsd) 3 | find_path(LIBDSD_INCLUDE_DIR NAMES dsd_api.h 4 | PATHS 5 | ${LIBDSD_PKG_INCLUDE_DIRS} 6 | /usr/include 7 | /usr/local/include 8 | /usr/local/include/dsd 9 | ) 10 | 11 | find_library(LIBDSD_LIBRARIES NAMES gr-dsd 12 | PATHS 13 | ${LIBDSD_PKG_LIBRARY_DIRS} 14 | /usr/lib 15 | /usr/local/lib 16 | ) 17 | 18 | if(LIBDSD_INCLUDE_DIR AND LIBDSD_LIBRARIES) 19 | set(LIBDSD_FOUND TRUE CACHE INTERNAL "libdsd found") 20 | message(STATUS "Found libdsd: ${LIBDSD_INCLUDE_DIR}, ${LIBDSD_LIBRARIES}") 21 | else(LIB_INCLUDE_DIR AND LIBDSD_LIBRARIES) 22 | set(LIBDSD_FOUND FALSE CACHE INTERNAL "libdsd found") 23 | message(STATUS "libdsd not found.") 24 | endif(LIBDSD_INCLUDE_DIR AND LIBDSD_LIBRARIES) 25 | 26 | mark_as_advanced(LIBDSD_INCLUDE_DIR LIBDSD_LIBRARIES) 27 | 28 | endif(NOT LIBDSD_FOUND) 29 | -------------------------------------------------------------------------------- /cmake/Modules/FindLibDSD.cmake~: -------------------------------------------------------------------------------- 1 | if(NOT LIBDSD_FOUND) 2 | pkg_check_modules (LIBDSD_PKG libdsd) 3 | find_path(LIBDSD_INCLUDE_DIR NAMES dsd_api.h 4 | PATHS 5 | ${LIBDSD_PKG_INCLUDE_DIRS} 6 | /usr/include 7 | /usr/local/include 8 | /usr/local/include/dsd 9 | ) 10 | 11 | find_library(LIBDSD_LIBRARIES NAMES libgr-dsd 12 | PATHS 13 | ${LIBDSD_PKG_LIBRARY_DIRS} 14 | /usr/lib 15 | /usr/local/lib 16 | ) 17 | 18 | if(LIBDSD_INCLUDE_DIR AND LIBDSD_LIBRARIES) 19 | set(LIBDSD_FOUND TRUE CACHE INTERNAL "libdsd found") 20 | message(STATUS "Found libdsd: ${LIBDSD_INCLUDE_DIR}, ${LIBDSD_LIBRARIES}") 21 | else(LIB_INCLUDE_DIR AND LIBDSD_LIBRARIES) 22 | set(LIBDSD_FOUND FALSE CACHE INTERNAL "libdsd found") 23 | message(STATUS "libdsd not found.") 24 | endif(LIBDSD_INCLUDE_DIR AND LIBDSD_LIBRARIES) 25 | 26 | mark_as_advanced(LIBDSD_INCLUDE_DIR LIBDSD_LIBRARIES) 27 | 28 | endif(NOT LIBDSD_FOUND) 29 | -------------------------------------------------------------------------------- /cmake/Modules/FindLibHackRF.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_LIBHACKRF libhackrf) 3 | 4 | FIND_PATH( 5 | LIBHACKRF_INCLUDE_DIRS 6 | NAMES libhackrf/hackrf.h 7 | HINTS $ENV{LIBHACKRF_DIR}/include 8 | ${PC_LIBHACKRF_INCLUDEDIR} 9 | PATHS /usr/local/include 10 | /usr/include 11 | ) 12 | 13 | FIND_LIBRARY( 14 | LIBHACKRF_LIBRARIES 15 | NAMES hackrf 16 | HINTS $ENV{LIBHACKRF_DIR}/lib 17 | ${PC_LIBHACKRF_LIBDIR} 18 | PATHS /usr/local/lib 19 | /usr/lib 20 | ) 21 | 22 | INCLUDE(FindPackageHandleStandardArgs) 23 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBHACKRF DEFAULT_MSG LIBHACKRF_LIBRARIES LIBHACKRF_INCLUDE_DIRS) 24 | MARK_AS_ADVANCED(LIBHACKRF_LIBRARIES LIBHACKRF_INCLUDE_DIRS) 25 | -------------------------------------------------------------------------------- /cmake/Modules/FindLibMiriSDR.cmake: -------------------------------------------------------------------------------- 1 | if(NOT LIBMIRISDR_FOUND) 2 | pkg_check_modules (LIBMIRISDR_PKG libmirisdr) 3 | find_path(LIBMIRISDR_INCLUDE_DIR NAMES mirisdr.h 4 | PATHS 5 | ${LIBMIRISDR_PKG_INCLUDE_DIRS} 6 | /usr/include 7 | /usr/local/include 8 | ) 9 | 10 | find_library(LIBMIRISDR_LIBRARIES NAMES mirisdr 11 | PATHS 12 | ${LIBMIRISDR_PKG_LIBRARY_DIRS} 13 | /usr/lib 14 | /usr/local/lib 15 | ) 16 | 17 | if(LIBMIRISDR_INCLUDE_DIR AND LIBMIRISDR_LIBRARIES) 18 | set(LIBMIRISDR_FOUND TRUE CACHE INTERNAL "libmirisdr found") 19 | message(STATUS "Found libmirisdr: ${LIBMIRISDR_INCLUDE_DIR}, ${LIBMIRISDR_LIBRARIES}") 20 | else(LIBMIRISDR_INCLUDE_DIR AND LIBMIRISDR_LIBRARIES) 21 | set(LIBMIRISDR_FOUND FALSE CACHE INTERNAL "libmirisdr found") 22 | message(STATUS "libmirisdr not found.") 23 | endif(LIBMIRISDR_INCLUDE_DIR AND LIBMIRISDR_LIBRARIES) 24 | 25 | mark_as_advanced(LIBMIRISDR_INCLUDE_DIR LIBMIRISDR_LIBRARIES) 26 | 27 | endif(NOT LIBMIRISDR_FOUND) 28 | -------------------------------------------------------------------------------- /cmake/Modules/FindLibOsmoSDR.cmake: -------------------------------------------------------------------------------- 1 | if(NOT LIBOSMOSDR_FOUND) 2 | pkg_check_modules (LIBOSMOSDR_PKG libosmosdr) 3 | find_path(LIBOSMOSDR_INCLUDE_DIR NAMES osmosdr_api.h 4 | PATHS 5 | ${LIBOSMOSDR_PKG_INCLUDE_DIRS} 6 | /usr/include 7 | /usr/local/include 8 | /usr/local/include/osmosdr 9 | ) 10 | 11 | find_library(LIBOSMOSDR_LIBRARIES NAMES gnuradio-osmosdr 12 | PATHS 13 | ${LIBOSMOSDR_PKG_LIBRARY_DIRS} 14 | /usr/lib 15 | /usr/local/lib 16 | ) 17 | 18 | if(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES) 19 | set(LIBOSMOSDR_FOUND TRUE CACHE INTERNAL "libosmosdr found") 20 | message(STATUS "Found libosmosdr: ${LIBOSMOSDR_INCLUDE_DIR}, ${LIBOSMOSDR_LIBRARIES}") 21 | else(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES) 22 | set(LIBOSMOSDR_FOUND FALSE CACHE INTERNAL "libosmosdr found") 23 | message(STATUS "libosmosdr not found.") 24 | endif(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES) 25 | 26 | mark_as_advanced(LIBOSMOSDR_INCLUDE_DIR LIBOSMOSDR_LIBRARIES) 27 | 28 | endif(NOT LIBOSMOSDR_FOUND) 29 | -------------------------------------------------------------------------------- /cmake/Modules/FindLibOsmoSDR.cmake~: -------------------------------------------------------------------------------- 1 | if(NOT LIBOSMOSDR_FOUND) 2 | pkg_check_modules (LIBOSMOSDR_PKG libosmosdr) 3 | find_path(LIBOSMOSDR_INCLUDE_DIR NAMES osmosdr.h 4 | PATHS 5 | ${LIBOSMOSDR_PKG_INCLUDE_DIRS} 6 | /usr/include 7 | /usr/local/include 8 | ) 9 | 10 | find_library(LIBOSMOSDR_LIBRARIES NAMES osmosdr 11 | PATHS 12 | ${LIBOSMOSDR_PKG_LIBRARY_DIRS} 13 | /usr/lib 14 | /usr/local/lib 15 | ) 16 | 17 | if(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES) 18 | set(LIBOSMOSDR_FOUND TRUE CACHE INTERNAL "libosmosdr found") 19 | message(STATUS "Found libosmosdr: ${LIBOSMOSDR_INCLUDE_DIR}, ${LIBOSMOSDR_LIBRARIES}") 20 | else(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES) 21 | set(LIBOSMOSDR_FOUND FALSE CACHE INTERNAL "libosmosdr found") 22 | message(STATUS "libosmosdr not found.") 23 | endif(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES) 24 | 25 | mark_as_advanced(LIBOSMOSDR_INCLUDE_DIR LIBOSMOSDR_LIBRARIES) 26 | 27 | endif(NOT LIBOSMOSDR_FOUND) 28 | -------------------------------------------------------------------------------- /cmake/Modules/FindLibRTLSDR.cmake: -------------------------------------------------------------------------------- 1 | if(NOT LIBRTLSDR_FOUND) 2 | pkg_check_modules (LIBRTLSDR_PKG librtlsdr) 3 | find_path(LIBRTLSDR_INCLUDE_DIR NAMES rtl-sdr.h 4 | PATHS 5 | ${LIBRTLSDR_PKG_INCLUDE_DIRS} 6 | /usr/include 7 | /usr/local/include 8 | ) 9 | 10 | find_library(LIBRTLSDR_LIBRARIES NAMES rtlsdr 11 | PATHS 12 | ${LIBRTLSDR_PKG_LIBRARY_DIRS} 13 | /usr/lib 14 | /usr/local/lib 15 | ) 16 | 17 | if(LIBRTLSDR_INCLUDE_DIR AND LIBRTLSDR_LIBRARIES) 18 | set(LIBRTLSDR_FOUND TRUE CACHE INTERNAL "librtlsdr found") 19 | message(STATUS "Found librtlsdr: ${LIBRTLSDR_INCLUDE_DIR}, ${LIBRTLSDR_LIBRARIES}") 20 | else(LIBRTLSDR_INCLUDE_DIR AND LIBRTLSDR_LIBRARIES) 21 | set(LIBRTLSDR_FOUND FALSE CACHE INTERNAL "librtlsdr found") 22 | message(STATUS "librtlsdr not found.") 23 | endif(LIBRTLSDR_INCLUDE_DIR AND LIBRTLSDR_LIBRARIES) 24 | 25 | mark_as_advanced(LIBRTLSDR_INCLUDE_DIR LIBRTLSDR_LIBRARIES) 26 | 27 | endif(NOT LIBRTLSDR_FOUND) 28 | -------------------------------------------------------------------------------- /cmake/Modules/FindLibbladeRF.cmake: -------------------------------------------------------------------------------- 1 | if(NOT LIBBLADERF_FOUND) 2 | pkg_check_modules (LIBBLADERF_PKG libbladeRF) 3 | find_path(LIBBLADERF_INCLUDE_DIR NAMES libbladeRF.h 4 | PATHS 5 | ${LIBBLADERF_PKG_INCLUDE_DIRS} 6 | /usr/include 7 | /usr/local/include 8 | ) 9 | 10 | find_library(LIBBLADERF_LIBRARIES NAMES bladeRF 11 | PATHS 12 | ${LIBBLADERF_PKG_LIBRARY_DIRS} 13 | /usr/lib 14 | /usr/local/lib 15 | ) 16 | 17 | if(LIBBLADERF_INCLUDE_DIR AND LIBBLADERF_LIBRARIES) 18 | set(LIBBLADERF_FOUND TRUE CACHE INTERNAL "libbladeRF found") 19 | message(STATUS "Found libbladeRF: ${LIBBLADERF_INCLUDE_DIR}, ${LIBBLADERF_LIBRARIES}") 20 | else(LIBBLADERF_INCLUDE_DIR AND LIBBLADERF_LIBRARIES) 21 | set(LIBBLADERF_FOUND FALSE CACHE INTERNAL "libbladeRF found") 22 | message(STATUS "libbladeRF not found.") 23 | endif(LIBBLADERF_INCLUDE_DIR AND LIBBLADERF_LIBRARIES) 24 | 25 | mark_as_advanced(LIBBLADERF_INCLUDE_DIR LIBBLADERF_LIBRARIES) 26 | 27 | endif(NOT LIBBLADERF_FOUND) 28 | -------------------------------------------------------------------------------- /cmake/Modules/FindOP25.cmake: -------------------------------------------------------------------------------- 1 | if(NOT OP25_FOUND) 2 | pkg_check_modules (OP25_PKG op25) 3 | 4 | find_library(OP25_LIBRARIES NAMES gr-dsd 5 | PATHS 6 | ${LIBDSD_PKG_LIBRARY_DIRS} 7 | /usr/lib 8 | /usr/local/lib 9 | ) 10 | 11 | if(LIBDSD_INCLUDE_DIR AND LIBDSD_LIBRARIES) 12 | set(LIBDSD_FOUND TRUE CACHE INTERNAL "libdsd found") 13 | message(STATUS "Found libdsd: ${LIBDSD_INCLUDE_DIR}, ${LIBDSD_LIBRARIES}") 14 | else(LIB_INCLUDE_DIR AND LIBDSD_LIBRARIES) 15 | set(LIBDSD_FOUND FALSE CACHE INTERNAL "libdsd found") 16 | message(STATUS "libdsd not found.") 17 | endif(LIBDSD_INCLUDE_DIR AND LIBDSD_LIBRARIES) 18 | 19 | mark_as_advanced(LIBDSD_INCLUDE_DIR LIBDSD_LIBRARIES) 20 | 21 | endif(NOT LIBDSD_FOUND) 22 | -------------------------------------------------------------------------------- /cmake/Modules/FindOP25.cmake~: -------------------------------------------------------------------------------- 1 | if(NOT LIBDSD_FOUND) 2 | pkg_check_modules (LIBDSD_PKG libdsd) 3 | find_path(LIBDSD_INCLUDE_DIR NAMES dsd_api.h 4 | PATHS 5 | ${LIBDSD_PKG_INCLUDE_DIRS} 6 | /usr/include 7 | /usr/local/include 8 | /usr/local/include/dsd 9 | ) 10 | 11 | find_library(LIBDSD_LIBRARIES NAMES gr-dsd 12 | PATHS 13 | ${LIBDSD_PKG_LIBRARY_DIRS} 14 | /usr/lib 15 | /usr/local/lib 16 | ) 17 | 18 | if(LIBDSD_INCLUDE_DIR AND LIBDSD_LIBRARIES) 19 | set(LIBDSD_FOUND TRUE CACHE INTERNAL "libdsd found") 20 | message(STATUS "Found libdsd: ${LIBDSD_INCLUDE_DIR}, ${LIBDSD_LIBRARIES}") 21 | else(LIB_INCLUDE_DIR AND LIBDSD_LIBRARIES) 22 | set(LIBDSD_FOUND FALSE CACHE INTERNAL "libdsd found") 23 | message(STATUS "libdsd not found.") 24 | endif(LIBDSD_INCLUDE_DIR AND LIBDSD_LIBRARIES) 25 | 26 | mark_as_advanced(LIBDSD_INCLUDE_DIR LIBDSD_LIBRARIES) 27 | 28 | endif(NOT LIBDSD_FOUND) 29 | -------------------------------------------------------------------------------- /cmake/Modules/FindUHD.cmake: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Find the library for the USRP Hardware Driver 3 | ######################################################################## 4 | 5 | INCLUDE(FindPkgConfig) 6 | PKG_CHECK_MODULES(PC_UHD uhd) 7 | 8 | FIND_PATH( 9 | UHD_INCLUDE_DIRS 10 | NAMES uhd/config.hpp 11 | HINTS $ENV{UHD_DIR}/include 12 | ${PC_UHD_INCLUDEDIR} 13 | PATHS /usr/local/include 14 | /usr/include 15 | ) 16 | 17 | FIND_LIBRARY( 18 | UHD_LIBRARIES 19 | NAMES uhd 20 | HINTS $ENV{UHD_DIR}/lib 21 | ${PC_UHD_LIBDIR} 22 | PATHS /usr/local/lib 23 | /usr/lib 24 | ) 25 | 26 | INCLUDE(FindPackageHandleStandardArgs) 27 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(UHD DEFAULT_MSG UHD_LIBRARIES UHD_INCLUDE_DIRS) 28 | MARK_AS_ADVANCED(UHD_LIBRARIES UHD_INCLUDE_DIRS) 29 | -------------------------------------------------------------------------------- /cmake/Modules/GrComponent.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_COMPONENT_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_COMPONENT_CMAKE TRUE) 24 | 25 | set(_gr_enabled_components "" CACHE INTERNAL "" FORCE) 26 | set(_gr_disabled_components "" CACHE INTERNAL "" FORCE) 27 | 28 | if(NOT DEFINED ENABLE_DEFAULT) 29 | set(ENABLE_DEFAULT ON) 30 | message(STATUS "") 31 | message(STATUS "The build system will automatically enable all components.") 32 | message(STATUS "Use -DENABLE_DEFAULT=OFF to disable components by default.") 33 | endif() 34 | 35 | ######################################################################## 36 | # Register a component into the system 37 | # - name: canonical component name 38 | # - var: variable for enabled status 39 | # - argn: list of dependencies 40 | ######################################################################## 41 | function(GR_REGISTER_COMPONENT name var) 42 | include(CMakeDependentOption) 43 | message(STATUS "") 44 | message(STATUS "Configuring ${name} support...") 45 | foreach(dep ${ARGN}) 46 | message(STATUS " Dependency ${dep} = ${${dep}}") 47 | endforeach(dep) 48 | 49 | #if the user set the var to force, we note this 50 | if("${${var}}" STREQUAL "FORCE") 51 | set(${var} ON) 52 | set(var_force TRUE) 53 | else() 54 | set(var_force FALSE) 55 | endif() 56 | 57 | #rewrite the dependency list so that deps that are also components use the cached version 58 | unset(comp_deps) 59 | foreach(dep ${ARGN}) 60 | list(FIND _gr_enabled_components ${dep} dep_enb_index) 61 | list(FIND _gr_disabled_components ${dep} dep_dis_index) 62 | if (${dep_enb_index} EQUAL -1 AND ${dep_dis_index} EQUAL -1) 63 | list(APPEND comp_deps ${dep}) 64 | else() 65 | list(APPEND comp_deps ${dep}_cached) #is a component, use cached version 66 | endif() 67 | endforeach(dep) 68 | 69 | #setup the dependent option for this component 70 | CMAKE_DEPENDENT_OPTION(${var} "enable ${name} support" ${ENABLE_DEFAULT} "${comp_deps}" OFF) 71 | set(${var} "${${var}}" PARENT_SCOPE) 72 | set(${var}_cached "${${var}}" CACHE INTERNAL "" FORCE) 73 | 74 | #force was specified, but the dependencies were not met 75 | if(NOT ${var} AND var_force) 76 | message(FATAL_ERROR "user force-enabled ${name} but configuration checked failed") 77 | endif() 78 | 79 | #append the component into one of the lists 80 | if(${var}) 81 | message(STATUS " Enabling ${name} support.") 82 | list(APPEND _gr_enabled_components ${name}) 83 | else(${var}) 84 | message(STATUS " Disabling ${name} support.") 85 | list(APPEND _gr_disabled_components ${name}) 86 | endif(${var}) 87 | message(STATUS " Override with -D${var}=ON/OFF") 88 | 89 | #make components lists into global variables 90 | set(_gr_enabled_components ${_gr_enabled_components} CACHE INTERNAL "" FORCE) 91 | set(_gr_disabled_components ${_gr_disabled_components} CACHE INTERNAL "" FORCE) 92 | endfunction(GR_REGISTER_COMPONENT) 93 | 94 | ######################################################################## 95 | # Print the registered component summary 96 | ######################################################################## 97 | function(GR_PRINT_COMPONENT_SUMMARY) 98 | message(STATUS "") 99 | message(STATUS "######################################################") 100 | message(STATUS "# gr-osmosdr enabled components ") 101 | message(STATUS "######################################################") 102 | foreach(comp ${_gr_enabled_components}) 103 | message(STATUS " * ${comp}") 104 | endforeach(comp) 105 | 106 | message(STATUS "") 107 | message(STATUS "######################################################") 108 | message(STATUS "# gr-osmosdr disabled components ") 109 | message(STATUS "######################################################") 110 | foreach(comp ${_gr_disabled_components}) 111 | message(STATUS " * ${comp}") 112 | endforeach(comp) 113 | 114 | message(STATUS "") 115 | endfunction(GR_PRINT_COMPONENT_SUMMARY) 116 | -------------------------------------------------------------------------------- /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 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 | 212 | ######################################################################## 213 | # Control use of gr_logger 214 | # Usage: 215 | # GR_LOGGING() 216 | # 217 | # Will set ENABLE_GR_LOG to 1 by default. 218 | # Can manually set with -DENABLE_GR_LOG=0|1 219 | ######################################################################## 220 | function(GR_LOGGING) 221 | find_package(Log4cpp) 222 | 223 | OPTION(ENABLE_GR_LOG "Use gr_logger" ON) 224 | if(ENABLE_GR_LOG) 225 | # If gr_logger is enabled, make it usable 226 | add_definitions( -DENABLE_GR_LOG ) 227 | 228 | # also test LOG4CPP; if we have it, use this version of the logger 229 | # otherwise, default to the stdout/stderr model. 230 | if(LOG4CPP_FOUND) 231 | SET(HAVE_LOG4CPP True CACHE INTERNAL "" FORCE) 232 | add_definitions( -DHAVE_LOG4CPP ) 233 | else(not LOG4CPP_FOUND) 234 | SET(HAVE_LOG4CPP False CACHE INTERNAL "" FORCE) 235 | SET(LOG4CPP_INCLUDE_DIRS "" CACHE INTERNAL "" FORCE) 236 | SET(LOG4CPP_LIBRARY_DIRS "" CACHE INTERNAL "" FORCE) 237 | SET(LOG4CPP_LIBRARIES "" CACHE INTERNAL "" FORCE) 238 | endif(LOG4CPP_FOUND) 239 | 240 | SET(ENABLE_GR_LOG ${ENABLE_GR_LOG} CACHE INTERNAL "" FORCE) 241 | 242 | else(ENABLE_GR_LOG) 243 | SET(HAVE_LOG4CPP False CACHE INTERNAL "" FORCE) 244 | SET(LOG4CPP_INCLUDE_DIRS "" CACHE INTERNAL "" FORCE) 245 | SET(LOG4CPP_LIBRARY_DIRS "" CACHE INTERNAL "" FORCE) 246 | SET(LOG4CPP_LIBRARIES "" CACHE INTERNAL "" FORCE) 247 | endif(ENABLE_GR_LOG) 248 | 249 | message(STATUS "ENABLE_GR_LOG set to ${ENABLE_GR_LOG}.") 250 | message(STATUS "HAVE_LOG4CPP set to ${HAVE_LOG4CPP}.") 251 | message(STATUS "LOG4CPP_LIBRARIES set to ${LOG4CPP_LIBRARIES}.") 252 | 253 | endfunction(GR_LOGGING) 254 | 255 | ######################################################################## 256 | # Run GRCC to compile .grc files into .py files. 257 | # 258 | # Usage: GRCC(filename, directory) 259 | # - filenames: List of file name of .grc file 260 | # - directory: directory of built .py file - usually in 261 | # ${CMAKE_CURRENT_BINARY_DIR} 262 | # - Sets PYFILES: output converted GRC file names to Python files. 263 | ######################################################################## 264 | function(GRCC) 265 | # Extract directory from list of args, remove it for the list of filenames. 266 | list(GET ARGV -1 directory) 267 | list(REMOVE_AT ARGV -1) 268 | set(filenames ${ARGV}) 269 | file(MAKE_DIRECTORY ${directory}) 270 | 271 | SET(GRCC_COMMAND ${CMAKE_SOURCE_DIR}/gr-utils/src/python/grcc) 272 | 273 | # GRCC uses some stuff in grc and gnuradio-core, so we force 274 | # the known paths here 275 | list(APPEND PYTHONPATHS 276 | ${CMAKE_SOURCE_DIR} 277 | ${CMAKE_SOURCE_DIR}/gnuradio-core/src/python 278 | ${CMAKE_SOURCE_DIR}/gnuradio-core/src/lib/swig 279 | ${CMAKE_BINARY_DIR}/gnuradio-core/src/lib/swig 280 | ) 281 | 282 | if(WIN32) 283 | #SWIG generates the python library files into a subdirectory. 284 | #Therefore, we must append this subdirectory into PYTHONPATH. 285 | #Only do this for the python directories matching the following: 286 | foreach(pydir ${PYTHONPATHS}) 287 | get_filename_component(name ${pydir} NAME) 288 | if(name MATCHES "^(swig|lib|src)$") 289 | list(APPEND PYTHONPATHS ${pydir}/${CMAKE_BUILD_TYPE}) 290 | endif() 291 | endforeach(pydir) 292 | endif(WIN32) 293 | 294 | file(TO_NATIVE_PATH "${PYTHONPATHS}" pypath) 295 | 296 | if(UNIX) 297 | list(APPEND pypath "$PYTHONPATH") 298 | string(REPLACE ";" ":" pypath "${pypath}") 299 | set(ENV{PYTHONPATH} ${pypath}) 300 | endif(UNIX) 301 | 302 | if(WIN32) 303 | list(APPEND pypath "%PYTHONPATH%") 304 | string(REPLACE ";" "\\;" pypath "${pypath}") 305 | #list(APPEND environs "PYTHONPATH=${pypath}") 306 | set(ENV{PYTHONPATH} ${pypath}) 307 | endif(WIN32) 308 | 309 | foreach(f ${filenames}) 310 | execute_process( 311 | COMMAND ${GRCC_COMMAND} -d ${directory} ${f} 312 | ) 313 | string(REPLACE ".grc" ".py" pyfile "${f}") 314 | string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" pyfile "${pyfile}") 315 | list(APPEND pyfiles ${pyfile}) 316 | endforeach(f) 317 | 318 | set(PYFILES ${pyfiles} PARENT_SCOPE) 319 | endfunction(GRCC) 320 | -------------------------------------------------------------------------------- /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 | if(LINUX AND EXISTS "/etc/slackware-version") 41 | set(SLACKWARE TRUE) 42 | endif() 43 | 44 | ######################################################################## 45 | # when the library suffix should be 64 (applies to redhat linux family) 46 | ######################################################################## 47 | if (REDHAT OR SLACKWARE) 48 | set(LIB64_CONVENTION TRUE) 49 | endif() 50 | 51 | if(NOT DEFINED LIB_SUFFIX AND LIB64_CONVENTION AND CMAKE_SYSTEM_PROCESSOR MATCHES "64$") 52 | set(LIB_SUFFIX 64) 53 | endif() 54 | set(LIB_SUFFIX ${LIB_SUFFIX} CACHE STRING "lib directory suffix") 55 | -------------------------------------------------------------------------------- /cmake/Modules/GrPython.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2011 Free Software Foundation, Inc. 2 | # 3 | # This file is part of GNU Radio 4 | # 5 | # GNU Radio is free software; you can redistribute it and/or modify 6 | # it under the terms of the GNU General Public License as published by 7 | # the Free Software Foundation; either version 3, or (at your option) 8 | # any later version. 9 | # 10 | # GNU Radio is distributed in the hope that it will be useful, 11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | # GNU General Public License for more details. 14 | # 15 | # You should have received a copy of the GNU General Public License 16 | # along with GNU Radio; see the file COPYING. If not, write to 17 | # the Free Software Foundation, Inc., 51 Franklin Street, 18 | # Boston, MA 02110-1301, USA. 19 | 20 | if(DEFINED __INCLUDED_GR_PYTHON_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_PYTHON_CMAKE TRUE) 24 | 25 | ######################################################################## 26 | # Setup the python interpreter: 27 | # This allows the user to specify a specific interpreter, 28 | # or finds the interpreter via the built-in cmake module. 29 | ######################################################################## 30 | #this allows the user to override PYTHON_EXECUTABLE 31 | if(PYTHON_EXECUTABLE) 32 | 33 | set(PYTHONINTERP_FOUND TRUE) 34 | 35 | #otherwise if not set, try to automatically find it 36 | else(PYTHON_EXECUTABLE) 37 | 38 | #use the built-in find script 39 | find_package(PythonInterp 2) 40 | 41 | #and if that fails use the find program routine 42 | if(NOT PYTHONINTERP_FOUND) 43 | find_program(PYTHON_EXECUTABLE NAMES python python2 python2.7 python2.6 python2.5) 44 | if(PYTHON_EXECUTABLE) 45 | set(PYTHONINTERP_FOUND TRUE) 46 | endif(PYTHON_EXECUTABLE) 47 | endif(NOT PYTHONINTERP_FOUND) 48 | 49 | endif(PYTHON_EXECUTABLE) 50 | 51 | #make the path to the executable appear in the cmake gui 52 | set(PYTHON_EXECUTABLE ${PYTHON_EXECUTABLE} CACHE FILEPATH "python interpreter") 53 | 54 | #make sure we can use -B with python (introduced in 2.6) 55 | if(PYTHON_EXECUTABLE) 56 | execute_process( 57 | COMMAND ${PYTHON_EXECUTABLE} -B -c "" 58 | OUTPUT_QUIET ERROR_QUIET 59 | RESULT_VARIABLE PYTHON_HAS_DASH_B_RESULT 60 | ) 61 | if(PYTHON_HAS_DASH_B_RESULT EQUAL 0) 62 | set(PYTHON_DASH_B "-B") 63 | endif() 64 | endif(PYTHON_EXECUTABLE) 65 | 66 | ######################################################################## 67 | # Check for the existence of a python module: 68 | # - desc a string description of the check 69 | # - mod the name of the module to import 70 | # - cmd an additional command to run 71 | # - have the result variable to set 72 | ######################################################################## 73 | macro(GR_PYTHON_CHECK_MODULE desc mod cmd have) 74 | message(STATUS "") 75 | message(STATUS "Python checking for ${desc}") 76 | execute_process( 77 | COMMAND ${PYTHON_EXECUTABLE} -c " 78 | ######################################### 79 | try: 80 | import ${mod} 81 | assert ${cmd} 82 | except ImportError, AssertionError: exit(-1) 83 | except: pass 84 | #########################################" 85 | RESULT_VARIABLE ${have} 86 | ) 87 | if(${have} EQUAL 0) 88 | message(STATUS "Python checking for ${desc} - found") 89 | set(${have} TRUE) 90 | else(${have} EQUAL 0) 91 | message(STATUS "Python checking for ${desc} - not found") 92 | set(${have} FALSE) 93 | endif(${have} EQUAL 0) 94 | endmacro(GR_PYTHON_CHECK_MODULE) 95 | 96 | ######################################################################## 97 | # Sets the python installation directory GR_PYTHON_DIR 98 | ######################################################################## 99 | if(NOT DEFINED GR_PYTHON_DIR) 100 | execute_process(COMMAND ${PYTHON_EXECUTABLE} -c " 101 | from distutils import sysconfig 102 | print sysconfig.get_python_lib(plat_specific=True, prefix='') 103 | " OUTPUT_VARIABLE GR_PYTHON_DIR OUTPUT_STRIP_TRAILING_WHITESPACE 104 | ) 105 | endif() 106 | file(TO_CMAKE_PATH ${GR_PYTHON_DIR} GR_PYTHON_DIR) 107 | 108 | ######################################################################## 109 | # Create an always-built target with a unique name 110 | # Usage: GR_UNIQUE_TARGET( ) 111 | ######################################################################## 112 | function(GR_UNIQUE_TARGET desc) 113 | file(RELATIVE_PATH reldir ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}) 114 | execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import re, hashlib 115 | unique = hashlib.md5('${reldir}${ARGN}').hexdigest()[:5] 116 | print(re.sub('\\W', '_', '${desc} ${reldir} ' + unique))" 117 | OUTPUT_VARIABLE _target OUTPUT_STRIP_TRAILING_WHITESPACE) 118 | add_custom_target(${_target} ALL DEPENDS ${ARGN}) 119 | endfunction(GR_UNIQUE_TARGET) 120 | 121 | ######################################################################## 122 | # Install python sources (also builds and installs byte-compiled python) 123 | ######################################################################## 124 | function(GR_PYTHON_INSTALL) 125 | include(CMakeParseArgumentsCopy) 126 | CMAKE_PARSE_ARGUMENTS(GR_PYTHON_INSTALL "" "DESTINATION;COMPONENT" "FILES;PROGRAMS" ${ARGN}) 127 | 128 | #################################################################### 129 | if(GR_PYTHON_INSTALL_FILES) 130 | #################################################################### 131 | install(${ARGN}) #installs regular python files 132 | 133 | #create a list of all generated files 134 | unset(pysrcfiles) 135 | unset(pycfiles) 136 | unset(pyofiles) 137 | foreach(pyfile ${GR_PYTHON_INSTALL_FILES}) 138 | get_filename_component(pyfile ${pyfile} ABSOLUTE) 139 | list(APPEND pysrcfiles ${pyfile}) 140 | 141 | #determine if this file is in the source or binary directory 142 | file(RELATIVE_PATH source_rel_path ${CMAKE_CURRENT_SOURCE_DIR} ${pyfile}) 143 | string(LENGTH "${source_rel_path}" source_rel_path_len) 144 | file(RELATIVE_PATH binary_rel_path ${CMAKE_CURRENT_BINARY_DIR} ${pyfile}) 145 | string(LENGTH "${binary_rel_path}" binary_rel_path_len) 146 | 147 | #and set the generated path appropriately 148 | if(${source_rel_path_len} GREATER ${binary_rel_path_len}) 149 | set(pygenfile ${CMAKE_CURRENT_BINARY_DIR}/${binary_rel_path}) 150 | else() 151 | set(pygenfile ${CMAKE_CURRENT_BINARY_DIR}/${source_rel_path}) 152 | endif() 153 | list(APPEND pycfiles ${pygenfile}c) 154 | list(APPEND pyofiles ${pygenfile}o) 155 | 156 | #ensure generation path exists 157 | get_filename_component(pygen_path ${pygenfile} PATH) 158 | file(MAKE_DIRECTORY ${pygen_path}) 159 | 160 | endforeach(pyfile) 161 | 162 | #the command to generate the pyc files 163 | add_custom_command( 164 | DEPENDS ${pysrcfiles} OUTPUT ${pycfiles} 165 | COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_BINARY_DIR}/python_compile_helper.py ${pysrcfiles} ${pycfiles} 166 | ) 167 | 168 | #the command to generate the pyo files 169 | add_custom_command( 170 | DEPENDS ${pysrcfiles} OUTPUT ${pyofiles} 171 | COMMAND ${PYTHON_EXECUTABLE} -O ${CMAKE_BINARY_DIR}/python_compile_helper.py ${pysrcfiles} ${pyofiles} 172 | ) 173 | 174 | #create install rule and add generated files to target list 175 | set(python_install_gen_targets ${pycfiles} ${pyofiles}) 176 | install(FILES ${python_install_gen_targets} 177 | DESTINATION ${GR_PYTHON_INSTALL_DESTINATION} 178 | COMPONENT ${GR_PYTHON_INSTALL_COMPONENT} 179 | ) 180 | 181 | 182 | #################################################################### 183 | elseif(GR_PYTHON_INSTALL_PROGRAMS) 184 | #################################################################### 185 | file(TO_NATIVE_PATH ${PYTHON_EXECUTABLE} pyexe_native) 186 | 187 | if (CMAKE_CROSSCOMPILING) 188 | set(pyexe_native /usr/bin/env python) 189 | endif() 190 | 191 | foreach(pyfile ${GR_PYTHON_INSTALL_PROGRAMS}) 192 | get_filename_component(pyfile_name ${pyfile} NAME) 193 | get_filename_component(pyfile ${pyfile} ABSOLUTE) 194 | string(REPLACE "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" pyexefile "${pyfile}.exe") 195 | list(APPEND python_install_gen_targets ${pyexefile}) 196 | 197 | get_filename_component(pyexefile_path ${pyexefile} PATH) 198 | file(MAKE_DIRECTORY ${pyexefile_path}) 199 | 200 | add_custom_command( 201 | OUTPUT ${pyexefile} DEPENDS ${pyfile} 202 | COMMAND ${PYTHON_EXECUTABLE} -c 203 | \"open('${pyexefile}', 'w').write('\#!${pyexe_native}\\n'+open('${pyfile}').read())\" 204 | COMMENT "Shebangin ${pyfile_name}" 205 | ) 206 | 207 | #on windows, python files need an extension to execute 208 | get_filename_component(pyfile_ext ${pyfile} EXT) 209 | if(WIN32 AND NOT pyfile_ext) 210 | set(pyfile_name "${pyfile_name}.py") 211 | endif() 212 | 213 | install(PROGRAMS ${pyexefile} RENAME ${pyfile_name} 214 | DESTINATION ${GR_PYTHON_INSTALL_DESTINATION} 215 | COMPONENT ${GR_PYTHON_INSTALL_COMPONENT} 216 | ) 217 | endforeach(pyfile) 218 | 219 | endif() 220 | 221 | GR_UNIQUE_TARGET("pygen" ${python_install_gen_targets}) 222 | 223 | endfunction(GR_PYTHON_INSTALL) 224 | 225 | ######################################################################## 226 | # Write the python helper script that generates byte code files 227 | ######################################################################## 228 | file(WRITE ${CMAKE_BINARY_DIR}/python_compile_helper.py " 229 | import sys, py_compile 230 | files = sys.argv[1:] 231 | srcs, gens = files[:len(files)/2], files[len(files)/2:] 232 | for src, gen in zip(srcs, gens): 233 | py_compile.compile(file=src, cfile=gen, doraise=True) 234 | ") 235 | -------------------------------------------------------------------------------- /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 | if(ENABLE_DOXYGEN) 37 | 38 | #setup the input files variable list, quote formated 39 | set(input_files) 40 | unset(INPUT_PATHS) 41 | foreach(input_path ${ARGN}) 42 | if (IS_DIRECTORY ${input_path}) #when input path is a directory 43 | file(GLOB input_path_h_files ${input_path}/*.h) 44 | else() #otherwise its just a file, no glob 45 | set(input_path_h_files ${input_path}) 46 | endif() 47 | list(APPEND input_files ${input_path_h_files}) 48 | set(INPUT_PATHS "${INPUT_PATHS} \"${input_path}\"") 49 | endforeach(input_path) 50 | 51 | #determine the output directory 52 | get_filename_component(name ${output_file} NAME_WE) 53 | get_filename_component(OUTPUT_DIRECTORY ${output_file} PATH) 54 | set(OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}/${name}_swig_docs) 55 | make_directory(${OUTPUT_DIRECTORY}) 56 | 57 | #generate the Doxyfile used by doxygen 58 | configure_file( 59 | ${CMAKE_SOURCE_DIR}/docs/doxygen/Doxyfile.swig_doc.in 60 | ${OUTPUT_DIRECTORY}/Doxyfile 61 | @ONLY) 62 | 63 | #Create a dummy custom command that depends on other targets 64 | include(GrMiscUtils) 65 | GR_GEN_TARGET_DEPS(_${name}_tag tag_deps ${GR_SWIG_DOCS_TARGET_DEPS}) 66 | 67 | #call doxygen on the Doxyfile + input headers 68 | add_custom_command( 69 | OUTPUT ${OUTPUT_DIRECTORY}/xml/index.xml 70 | DEPENDS ${input_files} ${GR_SWIG_DOCS_SOURCE_DEPS} ${tag_deps} 71 | COMMAND ${DOXYGEN_EXECUTABLE} ${OUTPUT_DIRECTORY}/Doxyfile 72 | COMMENT "Generating doxygen xml for ${name} docs" 73 | ) 74 | 75 | #call the swig_doc script on the xml files 76 | add_custom_command( 77 | OUTPUT ${output_file} 78 | DEPENDS ${input_files} ${stamp-file} ${OUTPUT_DIRECTORY}/xml/index.xml 79 | COMMAND ${PYTHON_EXECUTABLE} ${PYTHON_DASH_B} 80 | ${CMAKE_SOURCE_DIR}/docs/doxygen/swig_doc.py 81 | ${OUTPUT_DIRECTORY}/xml 82 | ${output_file} 83 | COMMENT "Generating python docstrings for ${name}" 84 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs/doxygen 85 | ) 86 | 87 | else(ENABLE_DOXYGEN) 88 | file(WRITE ${output_file} "\n") #no doxygen -> empty file 89 | endif(ENABLE_DOXYGEN) 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_TARGET_DEPS ${GR_SWIG_TARGET_DEPS}) 112 | GR_SWIG_MAKE_DOCS(${GR_SWIG_DOC_FILE} ${GR_SWIG_DOC_DIRS}) 113 | add_custom_target(${name}_swig_doc DEPENDS ${GR_SWIG_DOC_FILE}) 114 | list(APPEND GR_SWIG_TARGET_DEPS ${name}_swig_doc) 115 | endif() 116 | 117 | #append additional include directories 118 | find_package(PythonLibs 2) 119 | list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_PATH}) #deprecated name (now dirs) 120 | list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS}) 121 | list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}) 122 | list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) 123 | 124 | #determine include dependencies for swig file 125 | execute_process( 126 | COMMAND ${PYTHON_EXECUTABLE} 127 | ${CMAKE_BINARY_DIR}/get_swig_deps.py 128 | "${ifiles}" "${GR_SWIG_INCLUDE_DIRS}" 129 | OUTPUT_STRIP_TRAILING_WHITESPACE 130 | OUTPUT_VARIABLE SWIG_MODULE_${name}_EXTRA_DEPS 131 | WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} 132 | ) 133 | 134 | #Create a dummy custom command that depends on other targets 135 | include(GrMiscUtils) 136 | GR_GEN_TARGET_DEPS(_${name}_swig_tag tag_deps ${GR_SWIG_TARGET_DEPS}) 137 | set(tag_file ${CMAKE_CURRENT_BINARY_DIR}/${name}.tag) 138 | add_custom_command( 139 | OUTPUT ${tag_file} 140 | DEPENDS ${GR_SWIG_SOURCE_DEPS} ${tag_deps} 141 | COMMAND ${CMAKE_COMMAND} -E touch ${tag_file} 142 | ) 143 | 144 | #append the specified include directories 145 | include_directories(${GR_SWIG_INCLUDE_DIRS}) 146 | list(APPEND SWIG_MODULE_${name}_EXTRA_DEPS ${tag_file}) 147 | 148 | #setup the swig flags with flags and include directories 149 | set(CMAKE_SWIG_FLAGS -fvirtual -modern -keyword -w511 -module ${name} ${GR_SWIG_FLAGS}) 150 | foreach(dir ${GR_SWIG_INCLUDE_DIRS}) 151 | list(APPEND CMAKE_SWIG_FLAGS "-I${dir}") 152 | endforeach(dir) 153 | 154 | #set the C++ property on the swig .i file so it builds 155 | set_source_files_properties(${ifiles} PROPERTIES CPLUSPLUS ON) 156 | 157 | #setup the actual swig library target to be built 158 | include(UseSWIG) 159 | SWIG_ADD_MODULE(${name} python ${ifiles}) 160 | SWIG_LINK_LIBRARIES(${name} ${PYTHON_LIBRARIES} ${GR_SWIG_LIBRARIES}) 161 | 162 | endmacro(GR_SWIG_MAKE) 163 | 164 | ######################################################################## 165 | # Install swig targets generated by GR_SWIG_MAKE. Usage: 166 | # GR_SWIG_INSTALL( 167 | # TARGETS target target target... 168 | # [DESTINATION destination] 169 | # [COMPONENT component] 170 | # ) 171 | ######################################################################## 172 | macro(GR_SWIG_INSTALL) 173 | 174 | include(CMakeParseArgumentsCopy) 175 | CMAKE_PARSE_ARGUMENTS(GR_SWIG_INSTALL "" "DESTINATION;COMPONENT" "TARGETS" ${ARGN}) 176 | 177 | foreach(name ${GR_SWIG_INSTALL_TARGETS}) 178 | install(TARGETS ${SWIG_MODULE_${name}_REAL_NAME} 179 | DESTINATION ${GR_SWIG_INSTALL_DESTINATION} 180 | COMPONENT ${GR_SWIG_INSTALL_COMPONENT} 181 | ) 182 | 183 | include(GrPython) 184 | GR_PYTHON_INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/${name}.py 185 | DESTINATION ${GR_SWIG_INSTALL_DESTINATION} 186 | COMPONENT ${GR_SWIG_INSTALL_COMPONENT} 187 | ) 188 | 189 | GR_LIBTOOL( 190 | TARGET ${SWIG_MODULE_${name}_REAL_NAME} 191 | DESTINATION ${GR_SWIG_INSTALL_DESTINATION} 192 | ) 193 | 194 | endforeach(name) 195 | 196 | endmacro(GR_SWIG_INSTALL) 197 | 198 | ######################################################################## 199 | # Generate a python file that can determine swig dependencies. 200 | # Used by the make macro above to determine extra dependencies. 201 | # When you build C++, CMake figures out the header dependencies. 202 | # This code essentially performs that logic for swig includes. 203 | ######################################################################## 204 | file(WRITE ${CMAKE_BINARY_DIR}/get_swig_deps.py " 205 | 206 | import os, sys, re 207 | 208 | i_include_matcher = re.compile('%(include|import)\\s*[<|\"](.*)[>|\"]') 209 | h_include_matcher = re.compile('#(include)\\s*[<|\"](.*)[>|\"]') 210 | include_dirs = sys.argv[2].split(';') 211 | 212 | def get_swig_incs(file_path): 213 | if file_path.endswith('.i'): matcher = i_include_matcher 214 | else: matcher = h_include_matcher 215 | file_contents = open(file_path, 'r').read() 216 | return matcher.findall(file_contents, re.MULTILINE) 217 | 218 | def get_swig_deps(file_path, level): 219 | deps = [file_path] 220 | if level == 0: return deps 221 | for keyword, inc_file in get_swig_incs(file_path): 222 | for inc_dir in include_dirs: 223 | inc_path = os.path.join(inc_dir, inc_file) 224 | if not os.path.exists(inc_path): continue 225 | deps.extend(get_swig_deps(inc_path, level-1)) 226 | break #found, we dont search in lower prio inc dirs 227 | return deps 228 | 229 | if __name__ == '__main__': 230 | ifiles = sys.argv[1].split(';') 231 | deps = sum([get_swig_deps(ifile, 3) for ifile in ifiles], []) 232 | #sys.stderr.write(';'.join(set(deps)) + '\\n\\n') 233 | print(';'.join(set(deps))) 234 | ") 235 | -------------------------------------------------------------------------------- /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 | #Ensure that the build exe also appears in the PATH. 37 | list(APPEND GR_TEST_TARGET_DEPS ${ARGN}) 38 | 39 | #In the land of windows, all libraries must be in the PATH. 40 | #Since the dependent libraries are not yet installed, 41 | #we must manually set them in the PATH to run tests. 42 | #The following appends the path of a target dependency. 43 | foreach(target ${GR_TEST_TARGET_DEPS}) 44 | get_target_property(location ${target} LOCATION) 45 | if(location) 46 | get_filename_component(path ${location} PATH) 47 | string(REGEX REPLACE "\\$\\(.*\\)" ${CMAKE_BUILD_TYPE} path ${path}) 48 | list(APPEND GR_TEST_LIBRARY_DIRS ${path}) 49 | endif(location) 50 | endforeach(target) 51 | 52 | if(WIN32) 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(LD_PATH_VAR "LD_LIBRARY_PATH") 78 | if(APPLE) 79 | set(LD_PATH_VAR "DYLD_LIBRARY_PATH") 80 | endif() 81 | 82 | set(binpath "${CMAKE_CURRENT_BINARY_DIR}:$PATH") 83 | list(APPEND libpath "$${LD_PATH_VAR}") 84 | list(APPEND pypath "$PYTHONPATH") 85 | 86 | #replace list separator with the path separator 87 | string(REPLACE ";" ":" libpath "${libpath}") 88 | string(REPLACE ";" ":" pypath "${pypath}") 89 | list(APPEND environs "PATH=${binpath}" "${LD_PATH_VAR}=${libpath}" "PYTHONPATH=${pypath}") 90 | 91 | #generate a bat file that sets the environment and runs the test 92 | find_program(SHELL sh) 93 | set(sh_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.sh) 94 | file(WRITE ${sh_file} "#!${SHELL}\n") 95 | #each line sets an environment variable 96 | foreach(environ ${environs}) 97 | file(APPEND ${sh_file} "export ${environ}\n") 98 | endforeach(environ) 99 | #load the command to run with its arguments 100 | foreach(arg ${ARGN}) 101 | file(APPEND ${sh_file} "${arg} ") 102 | endforeach(arg) 103 | file(APPEND ${sh_file} "\n") 104 | 105 | #make the shell file executable 106 | execute_process(COMMAND chmod +x ${sh_file}) 107 | 108 | add_test(${test_name} ${SHELL} ${sh_file}) 109 | 110 | endif(UNIX) 111 | 112 | if(WIN32) 113 | list(APPEND libpath ${DLL_PATHS} "%PATH%") 114 | list(APPEND pypath "%PYTHONPATH%") 115 | 116 | #replace list separator with the path separator (escaped) 117 | string(REPLACE ";" "\\;" libpath "${libpath}") 118 | string(REPLACE ";" "\\;" pypath "${pypath}") 119 | list(APPEND environs "PATH=${libpath}" "PYTHONPATH=${pypath}") 120 | 121 | #generate a bat file that sets the environment and runs the test 122 | set(bat_file ${CMAKE_CURRENT_BINARY_DIR}/${test_name}_test.bat) 123 | file(WRITE ${bat_file} "@echo off\n") 124 | #each line sets an environment variable 125 | foreach(environ ${environs}) 126 | file(APPEND ${bat_file} "SET ${environ}\n") 127 | endforeach(environ) 128 | #load the command to run with its arguments 129 | foreach(arg ${ARGN}) 130 | file(APPEND ${bat_file} "${arg} ") 131 | endforeach(arg) 132 | file(APPEND ${bat_file} "\n") 133 | 134 | add_test(${test_name} ${bat_file}) 135 | endif(WIN32) 136 | 137 | endfunction(GR_ADD_TEST) 138 | -------------------------------------------------------------------------------- /cmake/Modules/GrVersion.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2011,2013 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_VERSION_CMAKE) 21 | return() 22 | endif() 23 | set(__INCLUDED_GR_VERSION_CMAKE TRUE) 24 | 25 | #eventually, replace version.sh and fill in the variables below 26 | set(MAJOR_VERSION ${VERSION_INFO_MAJOR_VERSION}) 27 | set(API_COMPAT ${VERSION_INFO_API_COMPAT}) 28 | set(MINOR_VERSION ${VERSION_INFO_MINOR_VERSION}) 29 | set(MAINT_VERSION ${VERSION_INFO_MAINT_VERSION}) 30 | 31 | ######################################################################## 32 | # Extract the version string from git describe. 33 | ######################################################################## 34 | find_package(Git) 35 | 36 | if(GIT_FOUND AND EXISTS ${CMAKE_SOURCE_DIR}/.git) 37 | message(STATUS "Extracting version information from git describe...") 38 | execute_process( 39 | COMMAND ${GIT_EXECUTABLE} describe --always --abbrev=8 --long 40 | OUTPUT_VARIABLE GIT_DESCRIBE OUTPUT_STRIP_TRAILING_WHITESPACE 41 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} 42 | ) 43 | else() 44 | set(GIT_DESCRIBE "v${MAJOR_VERSION}.${API_COMPAT}.x-xxx-xunknown") 45 | endif() 46 | 47 | ######################################################################## 48 | # Use the logic below to set the version constants 49 | ######################################################################## 50 | if("${MINOR_VERSION}" STREQUAL "git") 51 | # VERSION: 3.3git-xxx-gxxxxxxxx 52 | # DOCVER: 3.3git 53 | # LIBVER: 3.3git 54 | set(VERSION "${GIT_DESCRIBE}") 55 | set(DOCVER "${MAJOR_VERSION}.${API_COMPAT}${MINOR_VERSION}") 56 | set(LIBVER "${MAJOR_VERSION}.${API_COMPAT}${MINOR_VERSION}") 57 | set(RC_MINOR_VERSION "0") 58 | set(RC_MAINT_VERSION "0") 59 | elseif("${MAINT_VERSION}" STREQUAL "git") 60 | # VERSION: 3.3.1git-xxx-gxxxxxxxx 61 | # DOCVER: 3.3.1git 62 | # LIBVER: 3.3.1git 63 | set(VERSION "${GIT_DESCRIBE}") 64 | set(DOCVER "${MAJOR_VERSION}.${API_COMPAT}.${MINOR_VERSION}${MAINT_VERSION}") 65 | set(LIBVER "${MAJOR_VERSION}.${API_COMPAT}.${MINOR_VERSION}${MAINT_VERSION}") 66 | math(EXPR RC_MINOR_VERSION "${MINOR_VERSION} - 1") 67 | set(RC_MAINT_VERSION "0") 68 | else() 69 | # This is a numbered release. 70 | # VERSION: 3.3.1{.x} 71 | # DOCVER: 3.3.1{.x} 72 | # LIBVER: 3.3.1{.x} 73 | if("${MAINT_VERSION}" STREQUAL "0") 74 | set(VERSION "${MAJOR_VERSION}.${API_COMPAT}.${MINOR_VERSION}") 75 | else() 76 | set(VERSION "${MAJOR_VERSION}.${API_COMPAT}.${MINOR_VERSION}.${MAINT_VERSION}") 77 | endif() 78 | set(DOCVER "${VERSION}") 79 | set(LIBVER "${VERSION}") 80 | set(RC_MINOR_VERSION ${MINOR_VERSION}) 81 | set(RC_MAINT_VERSION ${MAINT_VERSION}) 82 | endif() 83 | -------------------------------------------------------------------------------- /dsd_block_ff.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 3, 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., 51 Franklin Street, 20 | * Boston, MA 02110-1301, USA. 21 | */ 22 | #ifndef INCLUDED_DSD_BLOCK_FF_H 23 | #define INCLUDED_DSD_BLOCK_FF_H 24 | 25 | #include 26 | #include 27 | 28 | /* 29 | extern "C" 30 | { 31 | #include 32 | } 33 | */ 34 | 35 | #define HEURISTICS_SIZE 200 36 | typedef struct 37 | { 38 | int values[HEURISTICS_SIZE]; 39 | float means[HEURISTICS_SIZE]; 40 | int index; 41 | int count; 42 | float sum; 43 | float var_sum; 44 | } SymbolHeuristics; 45 | 46 | typedef struct 47 | { 48 | unsigned int bit_count; 49 | unsigned int bit_error_count; 50 | SymbolHeuristics symbols[4][4]; 51 | } P25Heuristics; 52 | 53 | typedef struct 54 | { 55 | int value; 56 | int dibit; 57 | int corrected_dibit; 58 | int sequence_broken; 59 | } AnalogSignal; 60 | 61 | struct mbe_parameters 62 | { 63 | float w0; 64 | int L; 65 | int K; 66 | int Vl[57]; 67 | float Ml[57]; 68 | float log2Ml[57]; 69 | float PHIl[57]; 70 | float PSIl[57]; 71 | float gamma; 72 | int un; 73 | int repeat; 74 | }; 75 | 76 | typedef struct mbe_parameters mbe_parms; 77 | 78 | #define NZEROS 60 // because filter is static 79 | #define NXZEROS 134 80 | 81 | typedef struct 82 | { 83 | int onesymbol; 84 | char mbe_in_file[1024]; 85 | 86 | int errorbars; 87 | int datascope; 88 | int symboltiming; 89 | int verbose; 90 | int p25enc; 91 | int p25lc; 92 | int p25status; 93 | int p25tg; 94 | int scoperate; 95 | char audio_in_dev[1024]; 96 | int audio_in_fd; 97 | 98 | int audio_in_type; // 0 for device, 1 for file 99 | char audio_out_dev[1024]; 100 | int audio_out_fd; 101 | 102 | int audio_out_type; // 0 for device, 1 for file 103 | int split; 104 | int playoffset; 105 | char mbe_out_dir[1024]; 106 | char mbe_out_file[1024]; 107 | 108 | float audio_gain; 109 | int audio_out; 110 | char wav_out_file[1024]; 111 | 112 | //int wav_out_fd; 113 | int serial_baud; 114 | char serial_dev[1024]; 115 | int serial_fd; 116 | int resume; 117 | int frame_dstar; 118 | int frame_x2tdma; 119 | int frame_p25p1; 120 | int frame_nxdn48; 121 | int frame_nxdn96; 122 | int frame_dmr; 123 | int frame_provoice; 124 | int mod_c4fm; 125 | int mod_qpsk; 126 | int mod_gfsk; 127 | int uvquality; 128 | int inverted_x2tdma; 129 | int inverted_dmr; 130 | int mod_threshold; 131 | int ssize; 132 | int msize; 133 | int playfiles; 134 | int delay; 135 | int use_cosine_filter; 136 | int unmute_encrypted_p25; 137 | } dsd_opts; 138 | 139 | typedef struct 140 | { 141 | int *dibit_buf; 142 | int *dibit_buf_p; 143 | int repeat; 144 | short *audio_out_buf; 145 | short *audio_out_buf_p; 146 | float *audio_out_float_buf; 147 | float *audio_out_float_buf_p; 148 | float audio_out_temp_buf[160]; 149 | float *audio_out_temp_buf_p; 150 | int audio_out_idx; 151 | int audio_out_idx2; 152 | //int wav_out_bytes; 153 | int center; 154 | int jitter; 155 | int synctype; 156 | int min; 157 | int max; 158 | int lmid; 159 | int umid; 160 | int minref; 161 | int maxref; 162 | int lastsample; 163 | int sbuf[128]; 164 | int sidx; 165 | int maxbuf[1024]; 166 | int minbuf[1024]; 167 | int midx; 168 | char err_str[64]; 169 | char fsubtype[16]; 170 | char ftype[16]; 171 | int symbolcnt; 172 | int rf_mod; 173 | int numflips; 174 | int lastsynctype; 175 | int lastp25type; 176 | int offset; 177 | int carrier; 178 | char tg[25][16]; 179 | int tgcount; 180 | int lasttg; 181 | long lastsrc; 182 | long src_list[50]; 183 | int nac; 184 | int errs; 185 | int errs2; 186 | int mbe_file_type; 187 | int optind; 188 | int numtdulc; 189 | int firstframe; 190 | char slot0light[8]; 191 | char slot1light[8]; 192 | float aout_gain; 193 | float aout_max_buf[200]; 194 | float *aout_max_buf_p; 195 | int aout_max_buf_idx; 196 | int samplesPerSymbol; 197 | int symbolCenter; 198 | char algid[9]; 199 | char keyid[17]; 200 | int currentslot; 201 | mbe_parms *cur_mp; 202 | mbe_parms *prev_mp; 203 | mbe_parms *prev_mp_enhanced; 204 | int p25kid; 205 | 206 | unsigned int debug_audio_errors; 207 | unsigned int debug_header_errors; 208 | unsigned int debug_header_critical_errors; 209 | 210 | // Last dibit read 211 | int last_dibit; 212 | 213 | // Heuristics state data for +P5 signals 214 | P25Heuristics p25_heuristics; 215 | 216 | // Heuristics state data for -P5 signals 217 | P25Heuristics inv_p25_heuristics; 218 | 219 | #ifdef TRACE_DSD 220 | char debug_prefix; 221 | char debug_prefix_2; 222 | unsigned int debug_sample_index; 223 | unsigned int debug_sample_left_edge; 224 | unsigned int debug_sample_right_edge; 225 | FILE* debug_label_file; 226 | FILE* debug_label_dibit_file; 227 | FILE* debug_label_imbe_file; 228 | #endif 229 | 230 | pthread_mutex_t input_mutex; 231 | pthread_cond_t input_ready; 232 | const float *input_samples; 233 | int input_length; 234 | int input_offset; 235 | pthread_mutex_t quit_mutex; 236 | pthread_cond_t quit_now; 237 | pthread_mutex_t output_mutex; 238 | pthread_cond_t output_ready; 239 | short *output_buffer; 240 | int output_offset; 241 | float *output_samples; 242 | int output_num_samples; 243 | int output_length; 244 | int output_finished; 245 | int exitflag; 246 | float xv[NZEROS+1]; 247 | float nxv[NXZEROS+1]; 248 | } dsd_state; 249 | 250 | 251 | 252 | 253 | enum dsd_frame_mode { 254 | dsd_FRAME_AUTO_DETECT, 255 | dsd_FRAME_P25_PHASE_1, 256 | dsd_FRAME_DSTAR, 257 | dsd_FRAME_NXDN48_IDAS, 258 | dsd_FRAME_NXDN96, 259 | dsd_FRAME_PROVOICE, 260 | dsd_FRAME_DMR_MOTOTRBO, 261 | dsd_FRAME_X2_TDMA 262 | }; 263 | 264 | enum dsd_modulation_optimizations { 265 | dsd_MOD_AUTO_SELECT, 266 | dsd_MOD_C4FM, 267 | dsd_MOD_GFSK, 268 | dsd_MOD_QPSK 269 | }; 270 | 271 | typedef struct 272 | { 273 | int num; 274 | dsd_opts opts; 275 | dsd_state state; 276 | } dsd_params; 277 | 278 | class dsd_block_ff; 279 | 280 | /* 281 | * We use boost::shared_ptr's instead of raw pointers for all access 282 | * to gr_blocks (and many other data structures). The shared_ptr gets 283 | * us transparent reference counting, which greatly simplifies storage 284 | * management issues. This is especially helpful in our hybrid 285 | * C++ / Python system. 286 | * 287 | * See http://www.boost.org/libs/smart_ptr/smart_ptr.htm 288 | * 289 | * As a convention, the _sptr suffix indicates a boost::shared_ptr 290 | */ 291 | typedef boost::shared_ptr dsd_block_ff_sptr; 292 | 293 | /*! 294 | * \brief Return a shared_ptr to a new instance of dsd_block_ff. 295 | * 296 | * To avoid accidental use of raw pointers, dsd_block_ff's 297 | * constructor is private. dsd_make_block_ff is the public 298 | * interface for creating new instances. 299 | */ 300 | DSD_API dsd_block_ff_sptr dsd_make_block_ff (dsd_frame_mode frame = dsd_FRAME_AUTO_DETECT, 301 | dsd_modulation_optimizations mod = dsd_MOD_AUTO_SELECT, 302 | int uvquality = 3, bool errorbars = true, int verbosity = 2, bool empty = false, int num=-1); 303 | 304 | /*! 305 | * \brief pass discriminator output through Digital Speech Decoder 306 | * \ingroup block 307 | */ 308 | //class DSD_API dsd_block_ff : public gr_sync_decimator 309 | class DSD_API dsd_block_ff : public gr_block 310 | { 311 | private: 312 | // The friend declaration allows dsd_make_block_ff to 313 | // access the private constructor. 314 | 315 | friend DSD_API dsd_block_ff_sptr dsd_make_block_ff (dsd_frame_mode frame, dsd_modulation_optimizations mod, int uvquality, bool errorbars, int verbosity, bool empty, int num); 316 | 317 | dsd_params params; 318 | 319 | /*! 320 | * \brief pass discriminator output thread Digital Speech Decoder 321 | */ 322 | dsd_block_ff (dsd_frame_mode frame, dsd_modulation_optimizations mod, int uvquality, bool errorbars, int verbosity, bool empty, int num); // private constructor 323 | bool empty_frames; 324 | 325 | public: 326 | ~dsd_block_ff (); // public destructor 327 | void reset_state(); 328 | dsd_state *get_state(); 329 | // Where all the action really happens 330 | 331 | int close(); 332 | int general_work (int noutput_items, 333 | gr_vector_int &ninput_items, 334 | gr_vector_const_void_star &input_items, 335 | gr_vector_void_star &output_items); 336 | /* 337 | int work (int noutput_items, 338 | gr_vector_const_void_star &input_items, 339 | gr_vector_void_star &output_items);*/ 340 | }; 341 | 342 | #endif /* INCLUDED_DSD_BLOCK_FF_H */ 343 | -------------------------------------------------------------------------------- /fsk_demod.cc: -------------------------------------------------------------------------------- 1 | #ifdef HAVE_CONFIG_H 2 | #include "config.h" 3 | #endif 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | fsk_demod_sptr 10 | smartnet_make_fsk_demod () 11 | { 12 | return fsk_demod_sptr (new fsk_demod()); 13 | } 14 | 15 | static const int MIN_IN = 1; // mininum number of input streams 16 | static const int MAX_IN = 1; // maximum number of input streams 17 | static const int MIN_OUT = 1; // minimum number of output streams 18 | static const int MAX_OUT = 1; // maximum number of output streams 19 | 20 | fsk_demod::fsk_demod () 21 | : gr_block ("fsk_demod", 22 | gr_make_io_signature (MIN_IN, MAX_IN, sizeof (gr_complex)), 23 | gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (char))) 24 | { 25 | // nothing else required in this example 26 | } 27 | 28 | fsk_demod::~fsk_demod () 29 | { 30 | // nothing else required in this example 31 | } 32 | 33 | int fsk_demod::general_work (int noutput_items, 34 | gr_vector_int &ninput_items, 35 | gr_vector_const_void_star &input_items, 36 | gr_vector_void_star &output_items) 37 | { 38 | const float *in = (const float *) input_items[0]; 39 | float *out = (float *) output_items[0]; 40 | 41 | for (int i = 0; i < noutput_items; i++){ 42 | out[i] = in[i] * in[i]; 43 | } 44 | 45 | consume_each (noutput_items); 46 | 47 | // Tell runtime system how many output items we produced. 48 | return noutput_items; 49 | } 50 | -------------------------------------------------------------------------------- /fsk_demod.h: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDED_FSK_DEMOD_H 2 | #define INCLUDED_FSK_DEMOD_H 3 | 4 | #include 5 | 6 | class fsk_demod; 7 | 8 | typedef boost::shared_ptr fsk_demod_sptr; 9 | 10 | fsk_demod_sptr smartnet_make_fsk_demod(); 11 | 12 | 13 | class fsk_demod : public gr_block 14 | { 15 | private: 16 | // The friend declaration allows smartnet_make_deinterleave to 17 | // access the private constructor. 18 | 19 | friend fsk_demod_sptr smartnet_make_fsk_demod(); 20 | 21 | fsk_demod(); // private constructor 22 | 23 | public: 24 | ~fsk_demod(); // public destructor 25 | 26 | int general_work ( int noutput_items, 27 | gr_vector_int &ninput_items, 28 | gr_vector_const_void_star &input_items, 29 | gr_vector_void_star &output_items); 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /logging_receiver_dsd.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "logging_receiver_dsd.h" 3 | using namespace std; 4 | 5 | bool log_dsd::logging = false; 6 | 7 | log_dsd_sptr make_log_dsd(float freq, float center, long t, int n) 8 | { 9 | return gnuradio::get_initial_sptr(new log_dsd(freq, center, t, n)); 10 | } 11 | unsigned GCD(unsigned u, unsigned v) { 12 | while ( v != 0) { 13 | unsigned r = u % v; 14 | u = v; 15 | v = r; 16 | } 17 | return u; 18 | } 19 | 20 | std::vector design_filter(double interpolation, double deci) { 21 | float beta = 5.0; 22 | float trans_width = 0.5 - 0.4; 23 | float mid_transition_band = 0.5 - trans_width/2; 24 | 25 | std::vector result = gr_firdes::low_pass( 26 | interpolation, 27 | 1, 28 | mid_transition_band/interpolation, 29 | trans_width/interpolation, 30 | gr_firdes::WIN_KAISER, 31 | beta 32 | ); 33 | 34 | return result; 35 | } 36 | log_dsd::log_dsd(float f, float c, long t, int n) 37 | : gr_hier_block2 ("log_dsd", 38 | gr_make_io_signature (1, 1, sizeof(gr_complex)), 39 | gr_make_io_signature (0, 0, sizeof(float))) 40 | { 41 | freq = f; 42 | center = c; 43 | talkgroup = t; 44 | num = n; 45 | active = false; 46 | 47 | timestamp = time(NULL); 48 | starttime = time(NULL); 49 | 50 | float offset = center - (f*1000000); 51 | 52 | int samp_per_sym = 10; 53 | double samp_rate = 5000000; 54 | double decim = 80; 55 | float xlate_bandwidth = 14000; //24260.0; 56 | float channel_rate = 4800 * samp_per_sym; 57 | double pre_channel_rate = samp_rate/decim; 58 | 59 | 60 | 61 | 62 | lpf_taps = gr_firdes::low_pass(1, samp_rate, xlate_bandwidth/2, 6000); 63 | prefilter = gr_make_freq_xlating_fir_filter_ccf(decim, 64 | lpf_taps, 65 | offset, 66 | samp_rate); 67 | unsigned int d = GCD(channel_rate, pre_channel_rate); 68 | channel_rate = floor(channel_rate / d); 69 | pre_channel_rate = floor(pre_channel_rate / d); 70 | resampler_taps = design_filter(channel_rate, pre_channel_rate); 71 | 72 | downsample_sig = gr_make_rational_resampler_base_ccf(channel_rate, pre_channel_rate, resampler_taps); 73 | demod = gr_make_quadrature_demod_cf(1.6); //1.4); 74 | levels = gr_make_multiply_const_ff(0.33); 75 | 76 | for (int i=0; i < samp_per_sym; i++) { 77 | sym_taps.push_back(1.0 / samp_per_sym); 78 | } 79 | sym_filter = gr_make_fir_filter_fff(1, sym_taps); 80 | 81 | if (!logging) { 82 | iam_logging = true; 83 | logging = true; 84 | //dsd = dsd_make_block_ff(dsd_FRAME_P25_PHASE_1,dsd_MOD_C4FM,3,0,0, false, num); 85 | 86 | dsd = dsd_make_block_ff(dsd_FRAME_P25_PHASE_1,dsd_MOD_C4FM,3,1,1, false, num); 87 | } else { 88 | iam_logging = false; 89 | dsd = dsd_make_block_ff(dsd_FRAME_P25_PHASE_1,dsd_MOD_C4FM,3,0,0, false, num); 90 | } 91 | 92 | 93 | tm *ltm = localtime(&starttime); 94 | 95 | std::stringstream path_stream; 96 | path_stream << boost::filesystem::current_path().string() << "/" << 1900 + ltm->tm_year << "/" << 1 + ltm->tm_mon << "/" << ltm->tm_mday; 97 | 98 | boost::filesystem::create_directories(path_stream.str()); 99 | sprintf(filename, "%s/%ld-%ld_%g.wav", path_stream.str().c_str(),talkgroup,timestamp,freq); 100 | //sprintf(raw_filename, "%s/%ld-%ld_%g.raw.wav", path_stream.str().c_str(),talkgroup,timestamp,freq); 101 | sprintf(status_filename, "%s/%ld-%ld_%g.json", path_stream.str().c_str(),talkgroup,timestamp,freq); 102 | wav_sink = gr_make_wavfile_sink(filename,1,8000,16); 103 | //raw_sink = gr_make_wavfile_sink(raw_filename,1,48000,16); 104 | null_sink = gr_make_null_sink(sizeof(gr_complex)); 105 | bismark = gr_make_null_sink(sizeof(float)); 106 | 107 | connect(self(),0, null_sink,0); 108 | } 109 | 110 | log_dsd::~log_dsd() { 111 | //std::cout<< "logging_receiver_dsd.cc: destructor" <close(); 154 | 155 | disconnect(self(), 0, prefilter, 0); 156 | disconnect(prefilter, 0, downsample_sig, 0); 157 | disconnect(downsample_sig, 0, demod, 0); 158 | disconnect(demod, 0, sym_filter, 0); 159 | disconnect(sym_filter, 0, dsd, 0); 160 | disconnect(dsd, 0, wav_sink,0); 161 | 162 | //std::cout<< "logging_receiver_dsd.cc: finished close()" <set_center_freq(center - (f*1000000)); 170 | //std::cout << "Offset set to: " << (center - f*1000000) << "Freq: " << f << std::endl; 171 | } 172 | void log_dsd::deactivate() { 173 | //std::cout<< "logging_receiver_dsd.cc: Deactivating Logger [ " << num << " ] - freq[ " << freq << "] \t talkgroup[ " << talkgroup << " ] " <close(); 178 | //raw_sink->close(); 179 | 180 | disconnect(self(), 0, prefilter, 0); 181 | connect(self(),0, null_sink,0); 182 | 183 | 184 | 185 | 186 | disconnect(prefilter, 0, downsample_sig, 0); 187 | disconnect(downsample_sig, 0, demod, 0); 188 | disconnect(demod, 0, sym_filter, 0); 189 | disconnect(sym_filter, 0, levels, 0); 190 | disconnect(levels, 0, dsd, 0); 191 | //disconnect(levels, 0, raw_sink, 0); 192 | disconnect(dsd, 0, wav_sink,0); 193 | //disconnect(dsd, 0, bismark,0); 194 | 195 | 196 | unlock(); 197 | active = false; 198 | dsd_state *state = dsd->get_state(); 199 | ofstream myfile (status_filename); 200 | if (myfile.is_open()) 201 | { 202 | int level = (int) state->max / 164; 203 | int index=0; 204 | myfile << "{\n"; 205 | myfile << "\"freq\": " << freq << ",\n"; 206 | myfile << "\"num\": " << num << ",\n"; 207 | myfile << "\"talkgroup\": " << talkgroup << ",\n"; 208 | myfile << "\"center\": " << state->center << ",\n"; 209 | myfile << "\"umid\": " << state->umid << ",\n"; 210 | myfile << "\"lmid\": " << state->lmid << ",\n"; 211 | myfile << "\"max\": " << state->max << ",\n"; 212 | myfile << "\"inlvl\": " << level << ",\n"; 213 | myfile << "\"nac\": " << state->nac << ",\n"; 214 | myfile << "\"src\": " << state->lastsrc << ",\n"; 215 | myfile << "\"dsdtg\": " << state->lasttg << ",\n"; 216 | myfile << "\"headerCriticalErrors\": " << state->debug_header_critical_errors << ",\n"; 217 | myfile << "\"headerErrors\": " << state->debug_header_errors << ",\n"; 218 | myfile << "\"audioErrors\": " << state->debug_audio_errors << ",\n"; 219 | myfile << "\"symbCount\": " << state->symbolcnt << ",\n"; 220 | myfile << "\"srcList\": [ "; 221 | while(state->src_list[index]!=0){ 222 | if (index !=0) { 223 | myfile << ", " << state->src_list[index]; 224 | } else { 225 | myfile << state->src_list[index]; 226 | } 227 | index++; 228 | } 229 | myfile << " ]\n"; 230 | myfile << "}\n"; 231 | myfile.close(); 232 | } 233 | else cout << "Unable to open file"; 234 | dsd->reset_state(); 235 | //dsd.reset(); 236 | 237 | //wav_sink.reset(); 238 | 239 | } 240 | 241 | void log_dsd::activate(float f, int t, int n) { 242 | 243 | timestamp = time(NULL); 244 | starttime = time(NULL); 245 | 246 | talkgroup = t; 247 | freq = f; 248 | num = n; 249 | 250 | prefilter->set_center_freq(center - (f*1000000)); 251 | 252 | if (iam_logging) { 253 | printf("Recording Freq: %f \n", f); 254 | } 255 | 256 | tm *ltm = localtime(&starttime); 257 | 258 | std::stringstream path_stream; 259 | path_stream << boost::filesystem::current_path().string() << "/" << 1900 + ltm->tm_year << "/" << 1 + ltm->tm_mon << "/" << ltm->tm_mday; 260 | 261 | boost::filesystem::create_directories(path_stream.str()); 262 | sprintf(filename, "%s/%ld-%ld_%g.wav", path_stream.str().c_str(),talkgroup,timestamp,f); 263 | //sprintf(raw_filename, "%s/%ld-%ld_%g.raw.wav", path_stream.str().c_str(),talkgroup,timestamp,freq); 264 | sprintf(status_filename, "%s/%ld-%ld_%g.json", path_stream.str().c_str(),talkgroup,timestamp,freq); 265 | wav_sink->open(filename); 266 | //raw_sink->open(raw_filename); 267 | //wav_sink = gr_make_wavfile_sink(filename,1,8000,16); 268 | lock(); 269 | disconnect(self(),0, null_sink, 0); 270 | connect(self(),0, prefilter,0); 271 | connect(prefilter, 0, downsample_sig, 0); 272 | connect(downsample_sig, 0, demod, 0); 273 | connect(demod, 0, sym_filter, 0); 274 | connect(sym_filter, 0, levels, 0); 275 | connect(levels, 0, dsd, 0); 276 | //connect(levels, 0, raw_sink, 0); 277 | connect(dsd, 0, wav_sink,0); 278 | //connect(dsd, 0, bismark,0); 279 | 280 | unlock(); 281 | active = true; 282 | 283 | } 284 | -------------------------------------------------------------------------------- /logging_receiver_dsd.h: -------------------------------------------------------------------------------- 1 | #ifndef LPF_H 2 | #define LPF_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "dsd_block_ff.h" 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | //Valve 38 | #include 39 | #include 40 | #include 41 | #include 42 | //#include 43 | #include 44 | //#include 45 | 46 | class log_dsd; 47 | 48 | typedef boost::shared_ptr log_dsd_sptr; 49 | 50 | log_dsd_sptr make_log_dsd(float f, float c, long t, int n); 51 | 52 | class log_dsd : public gr_hier_block2 53 | { 54 | friend log_dsd_sptr make_log_dsd(float f, float c, long t, int n); 55 | protected: 56 | log_dsd(float f, float c, long t, int n); 57 | 58 | public: 59 | ~log_dsd(); 60 | void tune_offset(float f); 61 | void activate(float f, int talkgroup, int num); 62 | 63 | void deactivate(); 64 | float get_freq(); 65 | long get_talkgroup(); 66 | bool is_active(); 67 | int lastupdate(); 68 | long elapsed(); 69 | void close(); 70 | void mute(); 71 | void unmute(); 72 | char *get_filename(); 73 | //void forecast(int noutput_items, gr_vector_int &ninput_items_required); 74 | static bool logging; 75 | private: 76 | float center, freq; 77 | bool muted; 78 | long talkgroup; 79 | time_t timestamp; 80 | time_t starttime; 81 | char filename[160]; 82 | char status_filename[160]; 83 | char raw_filename[160]; 84 | int num; 85 | 86 | bool iam_logging; 87 | bool active; 88 | std::vector lpf_taps; 89 | std::vector resampler_taps; 90 | std::vector sym_taps; 91 | 92 | /* GR blocks */ 93 | gr_fir_filter_ccf_sptr lpf; 94 | gr_fir_filter_fff_sptr sym_filter; 95 | gr_freq_xlating_fir_filter_ccf_sptr prefilter; 96 | gr_sig_source_c_sptr offset_sig; 97 | 98 | gr_multiply_cc_sptr mixer; 99 | gr_file_sink_sptr fs; 100 | gr_multiply_const_ff_sptr levels; 101 | 102 | gr_rational_resampler_base_ccf_sptr downsample_sig; 103 | gr_rational_resampler_base_fff_sptr upsample_audio; 104 | //gr::analog::quadrature_demod_cf::sptr demod; 105 | gr_quadrature_demod_cf_sptr demod; 106 | dsd_block_ff_sptr dsd; 107 | gr_wavfile_sink_sptr wav_sink; 108 | gr_wavfile_sink_sptr raw_sink; 109 | //smartnet_wavsink_sptr wav_sink 110 | //gr::blocks::wavfile_sink::sptr wav_sink; 111 | gr_null_sink_sptr null_sink; 112 | gr_null_sink_sptr bismark; 113 | gr_null_source_sptr null_source; 114 | gr_head_sptr head_source; 115 | gr_kludge_copy_sptr copier; 116 | 117 | }; 118 | 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /logging_receiver_p25.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "logging_receiver_p25.h" 3 | 4 | 5 | log_p25_sptr make_log_p25(float freq, float center, long t) 6 | { 7 | return gnuradio::get_initial_sptr(new log_p25(freq, center, t)); 8 | } 9 | 10 | 11 | unsigned log_p25::GCD(unsigned u, unsigned v) { 12 | while ( v != 0) { 13 | unsigned r = u % v; 14 | u = v; 15 | v = r; 16 | } 17 | return u; 18 | } 19 | 20 | std::vector log_p25::design_filter(double interpolation, double deci) { 21 | float beta = 5.0; 22 | float trans_width = 0.5 - 0.4; 23 | float mid_transition_band = 0.5 - trans_width/2; 24 | 25 | std::vector result = gr_firdes::low_pass( 26 | interpolation, 27 | 1, 28 | mid_transition_band/interpolation, 29 | trans_width/interpolation, 30 | gr_firdes::WIN_KAISER, 31 | beta 32 | ); 33 | 34 | return result; 35 | } 36 | 37 | 38 | log_p25::log_p25(float f, float c, long t) 39 | : gr_hier_block2 ("log_p25", 40 | gr_make_io_signature (1, 1, sizeof(gr_complex)), 41 | gr_make_io_signature (0, 0, sizeof(float))) 42 | { 43 | freq = f; 44 | center = c; 45 | talkgroup = t; 46 | double capture_rate = 4000000; 47 | float offset = center - (f*1000000); 48 | 49 | float system_channel_rate = 48000; //125000; 50 | float symbol_rate = 4800; 51 | double symbol_deviation = 600.0; 52 | double channel_decim = floor(capture_rate / system_channel_rate); 53 | double channel_rate = floor(capture_rate / channel_decim); 54 | double trans_width = 12500 / 2; 55 | double trans_centre = trans_width + (trans_width / 2); 56 | std::vector sym_taps; 57 | const double pi = M_PI; //boost::math::constants::pi(); 58 | 59 | timestamp = time(NULL); 60 | starttime = time(NULL); 61 | 62 | std::cout << " Decim: " << channel_decim << " Rate: " << channel_rate << " trans center: " << trans_centre << std::endl; 63 | 64 | /* 65 | coeffs = gr.firdes.low_pass(1.0, capture_rate, trans_centre, trans_width, gr.firdes.WIN_HANN) 66 | self.channel_filter = gr.freq_xlating_fir_filter_ccf(channel_decim, coeffs, 0.0, capture_rate) 67 | self.set_channel_offset(0.0, 0, self.spectrum.win._units) 68 | # power squelch 69 | squelch_db = 0 70 | self.squelch = gr.pwr_squelch_cc(squelch_db, 1e-3, 0, True) 71 | self.set_squelch_threshold(squelch_db) 72 | # FM demodulator 73 | fm_demod_gain = channel_rate / (2.0 * pi * self.symbol_deviation) 74 | fm_demod = gr.quadrature_demod_cf(fm_demod_gain) 75 | # symbol filter 76 | symbol_decim = 1 77 | # symbol_coeffs = gr.firdes.root_raised_cosine(1.0, channel_rate, self.symbol_rate, 0.2, 500) 78 | # boxcar coefficients for "integrate and dump" filter 79 | samples_per_symbol = channel_rate // self.symbol_rate 80 | symbol_coeffs = (1.0/samples_per_symbol,)*samples_per_symbol 81 | symbol_filter = gr.fir_filter_fff(symbol_decim, symbol_coeffs) 82 | # C4FM demodulator 83 | autotuneq = gr.msg_queue(2) 84 | self.demod_watcher = demod_watcher(autotuneq, self.adjust_channel_offset) 85 | demod_fsk4 = op25.fsk4_demod_ff(autotuneq, channel_rate, self.symbol_rate) 86 | # symbol slicer 87 | levels = [ -2.0, 0.0, 2.0, 4.0 ] 88 | slicer = op25.fsk4_slicer_fb(levels)*/ 89 | 90 | 91 | 92 | prefilter = gr_make_freq_xlating_fir_filter_ccf(int(channel_decim), 93 | gr_firdes::low_pass(1.0, capture_rate, trans_centre, trans_width, gr_firdes::WIN_HANN), 94 | offset, 95 | capture_rate); 96 | int squelch_db = 40; 97 | squelch = gr_make_pwr_squelch_cc(squelch_db, 0.001, 0, true); 98 | double fm_demod_gain = floor(channel_rate / (2.0 * pi * symbol_deviation)); 99 | demod = gr_make_quadrature_demod_cf(fm_demod_gain); 100 | 101 | double symbol_decim = 1; 102 | double samples_per_symbol = floor(channel_rate / symbol_rate); 103 | std::cout << " FM Gain: " << fm_demod_gain << " Samples per sym: " << samples_per_symbol << std::endl; 104 | 105 | for (int i=0; i < samples_per_symbol; i++) { 106 | sym_taps.push_back(1.0 / samples_per_symbol); 107 | } 108 | //symbol_coeffs = (1.0/samples_per_symbol,)*samples_per_symbol 109 | sym_filter = gr_make_fir_filter_fff(symbol_decim, sym_taps); 110 | tune_queue = gr_make_msg_queue(); 111 | traffic_queue = gr_make_msg_queue(); 112 | const float l[] = { -2.0, 0.0, 2.0, 4.0 }; 113 | std::vector levels( l,l + sizeof( l ) / sizeof( l[0] ) ); 114 | op25_demod = op25_make_fsk4_demod_ff(tune_queue, channel_rate, symbol_rate); 115 | op25_decoder = op25_make_decoder_bf(); 116 | op25_slicer = op25_make_fsk4_slicer_fb(levels); 117 | op25_decoder->set_msgq(traffic_queue); 118 | 119 | 120 | /* 121 | unsigned int d = GCD(channel_rate, pre_channel_rate); 122 | channel_rate = floor(channel_rate / d); 123 | pre_channel_rate = floor(pre_channel_rate / d); 124 | 125 | downsample_sig = gr_make_rational_resampler_base_ccf(channel_rate, pre_channel_rate, design_filter(channel_rate, pre_channel_rate)); 126 | 127 | */ 128 | 129 | /* 130 | d = GCD(audio_rate, vocoder_rate); 131 | audio_rate = floor(audio_rate / d); 132 | vocoder_rate = floor(vocoder_rate / d); 133 | upsample_audio = gr_make_rational_resampler_base_fff(audio_rate, vocoder_rate, design_filter(audio_rate, vocoder_rate)); 134 | */ 135 | 136 | //demod = gr_make_quadrature_demod_cf(1.6); 137 | 138 | 139 | //const float a[] = { 0.1, 0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1}; 140 | /*const float a[] = { 1.0/6, 1.0/6, 1.0/6, 1.0/6, 1.0/6, 1.0/6}; 141 | 142 | 143 | std::vector data( a,a + sizeof( a ) / sizeof( a[0] ) ); 144 | sym_filter = gr_make_fir_filter_fff(1, data); 145 | 146 | 147 | tm *ltm = localtime(&starttime); 148 | 149 | std::stringstream path_stream; 150 | path_stream << boost::filesystem::current_path().string() << "/" << 1900 + ltm->tm_year << "/" << 1 + ltm->tm_mon << "/" << ltm->tm_mday; 151 | 152 | boost::filesystem::create_directories(path_stream.str()); 153 | //sprintf(filename, "%s/%ld-%ld.wav", path_stream.str().c_str(),talkgroup,timestamp); 154 | */ 155 | sprintf(filename, "%ld-%ld.wav",talkgroup,timestamp); 156 | 157 | wav_sink = gr_make_wavfile_sink(filename,1,8000,16); 158 | 159 | 160 | //gr_file_sink_sptr fs = gr_make_file_sink(sizeof(float),"sym_filter.dat"); 161 | 162 | 163 | 164 | 165 | connect(self(), 0, prefilter, 0); 166 | 167 | muted = true; 168 | 169 | connect(prefilter, 0, squelch, 0); 170 | connect(squelch, 0, demod, 0); 171 | //connect(demod, 0, wav_sink,0); 172 | 173 | connect(demod, 0, sym_filter, 0); 174 | connect(sym_filter, 0, op25_demod, 0); 175 | connect(op25_demod,0, op25_slicer, 0); 176 | connect(op25_slicer,0, op25_decoder,0); 177 | connect(op25_decoder, 0, wav_sink,0); 178 | /* 179 | 180 | 181 | int samp_per_sym = 10; //6 182 | double samp_rate = 5000000; 183 | int decim = 80; //20 184 | float xlate_bandwidth = 14000; //24260.0; 185 | float channel_rate = 4800 * samp_per_sym; 186 | double pre_channel_rate = double(samp_rate/decim); 187 | double vocoder_rate = 8000; 188 | double audio_rate = 44100; 189 | int symbol_deviation = 600; 190 | int symbol_rate = 4800; 191 | const double pi = M_PI; //boost::math::constants::pi(); 192 | 193 | timestamp = time(NULL); 194 | starttime = time(NULL); 195 | 196 | 197 | 198 | 199 | prefilter = gr_make_freq_xlating_fir_filter_ccf(decim, 200 | gr_firdes::low_pass(1, samp_rate, xlate_bandwidth/2, 6000), 201 | offset, 202 | samp_rate); 203 | 204 | 205 | tune_queue = gr_make_msg_queue(); 206 | traffic_queue = gr_make_msg_queue(); 207 | const float l[] = { -2.0, 0.0, 2.0, 4.0 }; 208 | std::vector levels( l,l + sizeof( l ) / sizeof( l[0] ) ); 209 | op25_demod = op25_make_fsk4_demod_ff(tune_queue, channel_rate, 4800); 210 | op25_decoder = op25_make_decoder_bf(); 211 | op25_slicer = op25_make_fsk4_slicer_fb(levels); 212 | op25_decoder->set_msgq(traffic_queue); 213 | 214 | demod = gr_make_quadrature_demod_cf(channel_rate/(2.0 * pi * symbol_deviation)); 215 | 216 | unsigned int d = GCD(channel_rate, pre_channel_rate); 217 | channel_rate = floor(channel_rate / d); 218 | pre_channel_rate = floor(pre_channel_rate / d); 219 | 220 | downsample_sig = gr_make_rational_resampler_base_ccf(channel_rate, pre_channel_rate, design_filter(channel_rate, pre_channel_rate)); 221 | 222 | */ 223 | 224 | /* 225 | d = GCD(audio_rate, vocoder_rate); 226 | audio_rate = floor(audio_rate / d); 227 | vocoder_rate = floor(vocoder_rate / d); 228 | upsample_audio = gr_make_rational_resampler_base_fff(audio_rate, vocoder_rate, design_filter(audio_rate, vocoder_rate)); 229 | */ 230 | 231 | //demod = gr_make_quadrature_demod_cf(1.6); 232 | 233 | 234 | //const float a[] = { 0.1, 0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1}; 235 | /* const float a[] = { 1.0/6, 1.0/6, 1.0/6, 1.0/6, 1.0/6, 1.0/6}; 236 | 237 | 238 | std::vector data( a,a + sizeof( a ) / sizeof( a[0] ) ); 239 | sym_filter = gr_make_fir_filter_fff(1, data); 240 | 241 | 242 | tm *ltm = localtime(&starttime); 243 | 244 | std::stringstream path_stream; 245 | path_stream << boost::filesystem::current_path().string() << "/" << 1900 + ltm->tm_year << "/" << 1 + ltm->tm_mon << "/" << ltm->tm_mday; 246 | 247 | boost::filesystem::create_directories(path_stream.str()); 248 | //sprintf(filename, "%s/%ld-%ld.wav", path_stream.str().c_str(),talkgroup,timestamp); 249 | 250 | sprintf(filename, "%ld-%ld.wav",talkgroup,timestamp); 251 | 252 | wav_sink = gr_make_wavfile_sink(filename,1,8000,16); 253 | 254 | 255 | //gr_file_sink_sptr fs = gr_make_file_sink(sizeof(float),"sym_filter.dat"); 256 | 257 | 258 | 259 | 260 | connect(self(), 0, prefilter, 0); 261 | 262 | muted = true; 263 | 264 | connect(prefilter, 0, downsample_sig, 0); 265 | connect(downsample_sig, 0, demod, 0); 266 | connect(demod, 0, sym_filter, 0); 267 | connect(sym_filter, 0, op25_demod, 0); 268 | connect(op25_demod,0, op25_slicer, 0); 269 | connect(op25_slicer,0, op25_decoder,0); 270 | connect(op25_decoder, 0, wav_sink,0);*/ 271 | //connect(sym_filter, 0, wav_sink,0); 272 | 273 | 274 | //connect(dsd, 0, upsample_audio,0); 275 | //connect(upsample_audio, 0, self(),0); 276 | //connect(sym_filter,0,fs,0); 277 | } 278 | 279 | log_p25::~log_p25() { 280 | 281 | } 282 | // from: /gnuradio/grc/grc_gnuradio/blks2/selector.py 283 | void log_p25::unmute() { 284 | // this function gets called everytime their is a TG continuation command. This keeps the timestamp updated. 285 | timestamp = time(NULL); 286 | if (muted) { 287 | 288 | /*disconnect(self(),0, null_sink,0); 289 | disconnect(head_source,0, prefilter,0); 290 | connect(head_source,0, null_sink,0);*/ 291 | /*connect(self(),0, copier,0); 292 | connect(copier,0, prefilter,0);*/ 293 | //connect(self(),0, prefilter,0); 294 | muted = false; 295 | } 296 | } 297 | 298 | void log_p25::mute() { 299 | 300 | if (!muted) { 301 | 302 | //disconnect(self(),0, prefilter,0); 303 | 304 | /*disconnect(self(),0, copier,0); 305 | disconnect(copier,0, prefilter,0);*/ 306 | /*disconnect(head_source,0, null_sink,0); 307 | 308 | connect(head_source,0,prefilter,0); 309 | connect(self(),0, null_sink,0); 310 | */muted = true; 311 | } 312 | } 313 | 314 | long log_p25::get_talkgroup() { 315 | return talkgroup; 316 | } 317 | 318 | float log_p25::get_freq() { 319 | return freq; 320 | } 321 | 322 | long log_p25::timeout() { 323 | return time(NULL) - timestamp; 324 | } 325 | 326 | char *log_p25::get_filename() { 327 | return filename; 328 | } 329 | 330 | void log_p25::close() { 331 | mute(); 332 | wav_sink->close(); 333 | /*disconnect(prefilter, 0, demod, 0); 334 | disconnect(demod, 0, sym_filter, 0); 335 | disconnect(sym_filter, 0, op25_demod, 0); 336 | disconnect(op25_demod,0, op25_slicer, 0); 337 | disconnect(op25_slicer,0, op25_decoder,0); 338 | disconnect(op25_decoder, 0, wav_sink,0);*/ 339 | } 340 | 341 | /* 342 | void log_p25::forecast(int noutput_items, gr_vector_int &ninput_items_required) { 343 | ninput_items_required[0] = 4 * noutput_items; 344 | }*/ 345 | 346 | 347 | void log_p25::tune_offset(float f) { 348 | freq = f; 349 | prefilter->set_center_freq(center - (f*1000000)); 350 | std::cout << "Offset set to: " << (f*1000000-center) << std::endl; 351 | } 352 | 353 | 354 | 355 | -------------------------------------------------------------------------------- /logging_receiver_p25.h: -------------------------------------------------------------------------------- 1 | #ifndef LPF_H 2 | #define LPF_H 3 | 4 | #define _USE_MATH_DEFINES 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | //Valve 44 | #include 45 | #include 46 | #include 47 | #include 48 | //#include 49 | #include 50 | //#include 51 | 52 | class log_p25; 53 | 54 | typedef boost::shared_ptr log_p25_sptr; 55 | 56 | log_p25_sptr make_log_p25(float f, float c, long t); 57 | 58 | class log_p25 : public gr_hier_block2 59 | { 60 | friend log_p25_sptr make_log_p25(float f, float c, long t); 61 | protected: 62 | log_p25(float f, float c, long t); 63 | 64 | public: 65 | ~log_p25(); 66 | void tune_offset(float f); 67 | float get_freq(); 68 | long get_talkgroup(); 69 | long timeout(); 70 | void close(); 71 | void mute(); 72 | void unmute(); 73 | char *get_filename(); 74 | //void forecast(int noutput_items, gr_vector_int &ninput_items_required); 75 | 76 | private: 77 | float center, freq; 78 | bool muted; 79 | long talkgroup; 80 | time_t timestamp; 81 | time_t starttime; 82 | 83 | char filename[160]; 84 | 85 | /* GR blocks */ 86 | gr_fir_filter_ccf_sptr lpf; 87 | gr_fir_filter_fff_sptr sym_filter; 88 | gr_freq_xlating_fir_filter_ccf_sptr prefilter; 89 | gr_sig_source_c_sptr offset_sig; 90 | 91 | gr_multiply_cc_sptr mixer; 92 | gr_file_sink_sptr fs; 93 | 94 | gr_rational_resampler_base_ccf_sptr downsample_sig; 95 | gr_rational_resampler_base_fff_sptr upsample_audio; 96 | //gr::analog::quadrature_demod_cf::sptr demod; 97 | gr_quadrature_demod_cf_sptr demod; 98 | gr_pwr_squelch_cc_sptr squelch; 99 | gr_wavfile_sink_sptr wav_sink; 100 | //smartnet_wavsink_sptr wav_sink 101 | //gr::blocks::wavfile_sink::sptr wav_sink; 102 | gr_null_sink_sptr null_sink; 103 | gr_null_source_sptr null_source; 104 | gr_head_sptr head_source; 105 | gr_kludge_copy_sptr copier; 106 | op25_fsk4_demod_ff_sptr op25_demod; 107 | op25_decoder_bf_sptr op25_decoder; 108 | op25_fsk4_slicer_fb_sptr op25_slicer; 109 | gr_msg_queue_sptr tune_queue; 110 | gr_msg_queue_sptr traffic_queue; 111 | unsigned GCD(unsigned u, unsigned v); 112 | std::vector design_filter(double interpolation, double deci); 113 | }; 114 | 115 | 116 | #endif 117 | 118 | -------------------------------------------------------------------------------- /logging_receiver_pocsag.cc: -------------------------------------------------------------------------------- 1 | 2 | #include "logging_receiver_pocsag.h" 3 | 4 | bool log_pocsag::logging = false; 5 | 6 | log_pocsag_sptr make_log_pocsag(float freq, float center, long t, int n) 7 | { 8 | return gnuradio::get_initial_sptr(new log_pocsag(freq, center, t, n)); 9 | } 10 | unsigned pocsag_GCD(unsigned u, unsigned v) { 11 | while ( v != 0) { 12 | unsigned r = u % v; 13 | u = v; 14 | v = r; 15 | } 16 | return u; 17 | } 18 | 19 | std::vector pocsag_design_filter(double interpolation, double deci) { 20 | float beta = 5.0; 21 | float trans_width = 0.5 - 0.4; 22 | float mid_transition_band = 0.5 - trans_width/2; 23 | 24 | std::vector result = gr_firdes::low_pass( 25 | interpolation, 26 | 1, 27 | mid_transition_band/interpolation, 28 | trans_width/interpolation, 29 | gr_firdes::WIN_KAISER, 30 | beta 31 | ); 32 | 33 | return result; 34 | } 35 | 36 | log_pocsag::log_pocsag(float f, float c, long t, int n) 37 | : gr_hier_block2 ("log_pocsag", 38 | gr_make_io_signature (1, 1, sizeof(gr_complex)), 39 | gr_make_io_signature (0, 0, sizeof(float))) 40 | { 41 | freq = f; 42 | center = c; 43 | talkgroup = t; 44 | num = n; 45 | 46 | timestamp = time(NULL); 47 | starttime = time(NULL); 48 | 49 | float offset = center - (f*1000000); 50 | 51 | int samp_per_sym = 10; 52 | double samp_rate = 5000000; int decim = 80; 53 | float xlate_bandwidth = 14000; //24260.0; 54 | float channel_rate = 4800 * samp_per_sym; 55 | double pre_channel_rate = double(samp_rate/decim); 56 | 57 | 58 | 59 | 60 | lpf_taps = gr_firdes::low_pass(1, samp_rate, xlate_bandwidth/2, 6000); 61 | prefilter = gr_make_freq_xlating_fir_filter_ccf(decim, 62 | lpf_taps, 63 | offset, 64 | samp_rate); 65 | unsigned int d = pocsag_GCD(channel_rate, pre_channel_rate); 66 | channel_rate = floor(channel_rate / d); 67 | pre_channel_rate = floor(pre_channel_rate / d); 68 | resampler_taps = pocsag_design_filter(channel_rate, pre_channel_rate); 69 | 70 | downsample_sig = gr_make_rational_resampler_base_ccf(channel_rate, pre_channel_rate, resampler_taps); 71 | quiet = gr_make_multiply_const_ff(0.75); 72 | demod = gr_make_quadrature_demod_cf(1.6); 73 | 74 | 75 | if (!logging) { 76 | iam_logging = true; 77 | logging = true; 78 | } else { 79 | iam_logging = false; 80 | } 81 | 82 | 83 | tm *ltm = localtime(&starttime); 84 | 85 | std::stringstream path_stream; 86 | path_stream << boost::filesystem::current_path().string() << "/" << 1900 + ltm->tm_year << "/" << 1 + ltm->tm_mon << "/" << ltm->tm_mday; 87 | 88 | boost::filesystem::create_directories(path_stream.str()); 89 | sprintf(filename, "%s/POCSAG-%ld-%ld.wav", path_stream.str().c_str(),talkgroup,timestamp); 90 | wav_sink = gr_make_wavfile_sink(filename,1,8000,16); 91 | 92 | 93 | connect(self(), 0, prefilter, 0); 94 | connect(prefilter, 0, downsample_sig, 0); 95 | connect(downsample_sig, 0, demod, 0); 96 | connect(demod, 0, wav_sink,0); 97 | 98 | //connect(sym_filter, 0, wav_sink, 0); 99 | 100 | 101 | 102 | std::cout << " POCSAG Recv [ " << num << " ] \t Tg: " << t << "\t Freq: " << f << std::endl; 103 | } 104 | 105 | log_pocsag::~log_pocsag() { 106 | //std::cout<< "logging_receiver_dsd.cc: destructor" <close(); 145 | 146 | disconnect(self(), 0, prefilter, 0); 147 | disconnect(prefilter, 0, downsample_sig, 0); 148 | disconnect(downsample_sig, 0, demod, 0); 149 | disconnect(demod, 0, wav_sink,0); 150 | 151 | //std::cout<< "logging_receiver_dsd.cc: finished close()" <set_center_freq(center - (f*1000000)); 159 | //std::cout << "Offset set to: " << (center - f*1000000) << "Freq: " << f << std::endl; 160 | } 161 | void log_pocsag::deactivate() { 162 | //std::cout<< "logging_receiver_dsd.cc: Deactivating Logger [ " << num << " ] - freq[ " << freq << "] \t talkgroup[ " << talkgroup << " ] " <close(); 169 | 170 | 171 | 172 | 173 | disconnect(self(), 0, prefilter, 0); 174 | disconnect(prefilter, 0, downsample_sig, 0); 175 | disconnect(downsample_sig, 0, demod, 0); 176 | disconnect(demod, 0, wav_sink,0); 177 | 178 | 179 | 180 | /* 181 | wav_sink.reset(); 182 | prefilter.reset(); 183 | downsample_sig.reset(); 184 | demod.reset(); 185 | sym_filter.reset(); 186 | dsd.reset(); 187 | */ 188 | //std::cout<< "logging_receiver_dsd.cc: Deactivated Logger [ " << num << " ] - freq[ " << freq << "] \t talkgroup[ " << talkgroup << " ] " < data( a,a + sizeof( a ) / sizeof( a[0] ) ); 237 | sym_filter = gr_make_fir_filter_fff(1, data); 238 | if (!logging) { 239 | iam_logging = true; 240 | logging = true; 241 | dsd = dsd_make_block_ff(dsd_FRAME_P25_PHASE_1,dsd_MOD_C4FM,3,1,1, false); 242 | } else { 243 | iam_logging = false; 244 | dsd = dsd_make_block_ff(dsd_FRAME_P25_PHASE_1,dsd_MOD_C4FM,3,0,0, false); 245 | } 246 | sprintf(filename, "%ld-%ld.wav", talkgroup,timestamp); 247 | wav_sink = gr_make_wavfile_sink(filename,1,8000,16); 248 | 249 | connect(self(), 0, prefilter, 0); 250 | connect(prefilter, 0, downsample_sig, 0); 251 | connect(downsample_sig, 0, demod, 0); 252 | connect(demod, 0, sym_filter, 0); 253 | connect(sym_filter, 0, dsd, 0); 254 | connect(dsd, 0, wav_sink,0);*/ 255 | 256 | 257 | /* 258 | 259 | prefilter->set_center_freq(center - (f*1000000)); 260 | std::cout << "logging_receiver_dsd.cc: Offset set to: " << (center - f*1000000) << "Freq: " << f << std::endl; 261 | 262 | 263 | 264 | tm *ltm = localtime(&starttime); 265 | 266 | std::stringstream path_stream; 267 | path_stream << boost::filesystem::current_path().string() << "/" << 1900 + ltm->tm_year << "/" << 1 + ltm->tm_mon << "/" << ltm->tm_mday; 268 | 269 | boost::filesystem::create_directories(path_stream.str()); 270 | sprintf(filename, "%s/%ld-%ld.wav", path_stream.str().c_str(),talkgroup,timestamp); 271 | wav_sink->open(filename); // = gr_make_wavfile_sink(filename,1,8000,16); */ 272 | } 273 | 274 | 275 | 276 | 277 | 278 | -------------------------------------------------------------------------------- /logging_receiver_pocsag.h: -------------------------------------------------------------------------------- 1 | #ifndef LPOCSAG_H 2 | #define LPOCSAG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "dsd_block_ff.h" 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | //Valve 36 | #include 37 | #include 38 | #include 39 | #include 40 | //#include 41 | #include 42 | //#include 43 | 44 | class log_pocsag; 45 | 46 | typedef boost::shared_ptr log_pocsag_sptr; 47 | 48 | log_pocsag_sptr make_log_pocsag(float f, float c, long t, int n); 49 | 50 | class log_pocsag : public gr_hier_block2 51 | { 52 | friend log_pocsag_sptr make_log_pocsag(float f, float c, long t, int n); 53 | protected: 54 | log_pocsag(float f, float c, long t, int n); 55 | 56 | public: 57 | ~log_pocsag(); 58 | void tune_offset(float f); 59 | void activate(float f, int talkgroup); 60 | void deactivate(); 61 | float get_freq(); 62 | long get_talkgroup(); 63 | long timeout(); 64 | long elapsed(); 65 | void close(); 66 | void mute(); 67 | void unmute(); 68 | char *get_filename(); 69 | //void forecast(int noutput_items, gr_vector_int &ninput_items_required); 70 | static bool logging; 71 | private: 72 | float center, freq; 73 | bool muted; 74 | long talkgroup; 75 | time_t timestamp; 76 | time_t starttime; 77 | char filename[160]; 78 | int num; 79 | 80 | bool iam_logging; 81 | std::vector lpf_taps; 82 | std::vector resampler_taps; 83 | std::vector sym_taps; 84 | 85 | /* GR blocks */ 86 | gr_fir_filter_ccf_sptr lpf; 87 | gr_freq_xlating_fir_filter_ccf_sptr prefilter; 88 | gr_sig_source_c_sptr offset_sig; 89 | 90 | gr_multiply_cc_sptr mixer; 91 | gr_file_sink_sptr fs; 92 | gr_multiply_const_ff_sptr quiet; 93 | 94 | gr_rational_resampler_base_ccf_sptr downsample_sig; 95 | gr_rational_resampler_base_fff_sptr upsample_audio; 96 | //gr::analog::quadrature_demod_cf::sptr demod; 97 | gr_quadrature_demod_cf_sptr demod; 98 | dsd_block_ff_sptr dsd; 99 | gr_wavfile_sink_sptr wav_sink; 100 | //smartnet_wavsink_sptr wav_sink 101 | //gr::blocks::wavfile_sink::sptr wav_sink; 102 | gr_null_sink_sptr null_sink; 103 | gr_null_source_sptr null_source; 104 | gr_head_sptr head_source; 105 | gr_kludge_copy_sptr copier; 106 | 107 | }; 108 | 109 | 110 | #endif 111 | 112 | -------------------------------------------------------------------------------- /op25-headers/data_unit.h: -------------------------------------------------------------------------------- 1 | /* -*- C++ -*- */ 2 | 3 | /* 4 | * Copyright 2008 Steve Glass 5 | * 6 | * This file is part of OP25. 7 | * 8 | * OP25 is free software; you can redistribute it and/or modify it 9 | * under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 3, or (at your option) 11 | * any later version. 12 | * 13 | * OP25 is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 16 | * License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with OP25; see the file COPYING. If not, write to the Free 20 | * Software Foundation, Inc., 51 Franklin Street, Boston, MA 21 | * 02110-1301, USA. 22 | */ 23 | 24 | #ifndef INCLUDED_DATA_UNIT_H 25 | #define INCLUDED_DATA_UNIT_H 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | typedef std::deque bit_queue; 35 | typedef const std::deque const_bit_queue; 36 | 37 | typedef uint8_t dibit; 38 | 39 | typedef std::deque float_queue; 40 | 41 | typedef boost::shared_ptr data_unit_sptr; 42 | 43 | /** 44 | * A P25 data unit. 45 | */ 46 | class data_unit : public boost::noncopyable 47 | { 48 | public: 49 | 50 | /** 51 | * data_unit (virtual) constructor. Returns a pointer to an 52 | * appropriate data_unit instance given the initial frame_body. 53 | * \param fs The frame sync value for this data_unit. 54 | * \param nid The network ID for this data_unit. 55 | * \return A (possibly null-valued) pointer to the data_unit. 56 | */ 57 | static data_unit_sptr make_data_unit(const_bit_queue& frame_body); 58 | 59 | /** 60 | * data_unit (virtual) destructor. 61 | */ 62 | virtual ~data_unit(); 63 | 64 | /** 65 | * Apply error correction to this data_unit. 66 | * 67 | * \precondition is_complete() == true. 68 | */ 69 | virtual void correct_errors() = 0; 70 | 71 | /** 72 | * Decode compressed audio using the supplied imbe_decoder and 73 | * writes output to audio. 74 | * 75 | * \precondition is_complete() == true. 76 | * \param imbe The imbe_decoder to use to generate the audio. 77 | */ 78 | virtual void decode_audio(imbe_decoder& imbe) = 0; 79 | 80 | /** 81 | * Decode the frame into an octet vector. 82 | * 83 | * \precondition is_complete() == true. 84 | * \param msg_sz The size of the message buffer. 85 | * \param msg A pointer to the message buffer. 86 | * \return The number of octets written to msg. 87 | */ 88 | virtual size_t decode_frame(size_t msg_sz, uint8_t *msg) = 0; 89 | 90 | /** 91 | * Dump this data unit in human readable format to stream s. 92 | * 93 | * \param s The stream to write on 94 | */ 95 | virtual void dump(std::ostream& os) const = 0; 96 | 97 | /** 98 | * Extends this data_unit with the specified dibit. If this 99 | * data_unit is already complete a range_error is thrown. 100 | * 101 | * \precondition is_complete() == false. 102 | * \param d The dibit to extend the frame with. 103 | * \throws range_error When the frame already is at its maximum size. 104 | * \return true when the frame is complete otherwise false. 105 | */ 106 | virtual void extend(dibit d) = 0; 107 | 108 | /** 109 | * Tests whether this data unit is complete. 110 | * 111 | * \return true when this data_unit is complete; otherwise returns 112 | * false. 113 | * \ see extend() 114 | */ 115 | virtual bool is_complete() const = 0; 116 | 117 | /** 118 | * Returns the size (in octets) of the data_unit. 119 | * 120 | * \return The actual size (in octets) of this data_unit. 121 | */ 122 | virtual uint16_t size() const = 0; 123 | 124 | /** 125 | * Return a snapshot of the key fields from this frame in a manner 126 | * suitable for display by the UI. The string is encoded using the 127 | * Python pickle format allowing for different fields to be 128 | * returned. 129 | * 130 | * \return A string containing the fields to display. 131 | */ 132 | virtual std::string snapshot() const = 0; 133 | 134 | protected: 135 | 136 | /** 137 | * data_unit default constructor. 138 | */ 139 | data_unit(); 140 | }; 141 | 142 | #endif /* INCLUDED_DATA_UNIT_H */ 143 | -------------------------------------------------------------------------------- /op25-headers/data_unit_handler.h: -------------------------------------------------------------------------------- 1 | /* -*- C++ -*- */ 2 | 3 | /* 4 | * Copyright 2008 Steve Glass 5 | * 6 | * This file is part of OP25. 7 | * 8 | * OP25 is free software; you can redistribute it and/or modify it 9 | * under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 3, or (at your option) 11 | * any later version. 12 | * 13 | * OP25 is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 16 | * License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with OP25; see the file COPYING. If not, write to the Free 20 | * Software Foundation, Inc., 51 Franklin Street, Boston, MA 21 | * 02110-1301, USA. 22 | */ 23 | 24 | #ifndef INCLUDED_DATA_UNIT_HANDLER_H 25 | #define INCLUDED_DATA_UNIT_HANDLER_H 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | typedef boost::shared_ptr data_unit_handler_sptr; 32 | 33 | /** 34 | * P25 data_unit_handler interface. 35 | */ 36 | class data_unit_handler : public boost::noncopyable 37 | { 38 | 39 | public: 40 | 41 | /** 42 | * data_unit_handler virtual destructor. 43 | */ 44 | virtual ~data_unit_handler(); 45 | 46 | /** 47 | * Handle a received P25 frame. 48 | * 49 | * \param du A non-null data_unit_sptr to handle. 50 | */ 51 | virtual void handle(data_unit_sptr du) = 0; 52 | 53 | protected: 54 | 55 | /** 56 | * data_unit_handler default constructor. 57 | * 58 | * \param next The next data_unit_handler in this chain. 59 | */ 60 | data_unit_handler(data_unit_handler_sptr next); 61 | 62 | private: 63 | 64 | /** 65 | * The next data_unit_handler in this chain. 66 | */ 67 | data_unit_handler_sptr d_next; 68 | 69 | }; 70 | 71 | #endif /* INCLUDED_DATA_UNIT_HANDLER_H */ 72 | -------------------------------------------------------------------------------- /op25-headers/imbe_decoder.h: -------------------------------------------------------------------------------- 1 | /* -*- C++ -*- */ 2 | 3 | /* 4 | * Copyright 2008 Steve Glass 5 | * 6 | * This file is part of OP25. 7 | * 8 | * OP25 is free software; you can redistribute it and/or modify it 9 | * under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 3, or (at your option) 11 | * any later version. 12 | * 13 | * OP25 is distributed in the hope that it will be useful, but WITHOUT 14 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 15 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 16 | * License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with OP25; see the file COPYING. If not, write to the Free 20 | * Software Foundation, Inc., 51 Franklin Street, Boston, MA 21 | * 02110-1301, USA. 22 | */ 23 | 24 | #ifndef INCLUDED_IMBE_DECODER_H 25 | #define INCLUDED_IMBE_DECODER_H 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | typedef std::deque audio_samples; 33 | typedef std::vector voice_codeword; 34 | 35 | typedef boost::shared_ptr imbe_decoder_sptr; 36 | 37 | /** 38 | * imbe_decoder is the interface to the various mechanisms for 39 | * translating P25 voice codewords into audio samples. 40 | */ 41 | class imbe_decoder : public boost::noncopyable { 42 | public: 43 | 44 | /** 45 | * imbe_decoder (virtual) constructor. The exact subclass 46 | * instantiated depends on some yet-to-be-decided magic. 47 | * 48 | * \return A shared_ptr to an imbe_decoder. 49 | */ 50 | static imbe_decoder_sptr make(); 51 | 52 | /** 53 | * imbe_decoder (virtual) destructor. 54 | */ 55 | virtual ~imbe_decoder(); 56 | 57 | /** 58 | * Decode the compressed IMBE audio. 59 | * 60 | * \param cw IMBE codeword (including parity check bits). 61 | */ 62 | virtual void decode(const voice_codeword& cw) = 0; 63 | 64 | /** 65 | * Returns the audio_samples samples. These are mono samples at 66 | * 8KS/s represented as a float in the range -1.0 .. +1.0. 67 | * 68 | * \return A non-null pointer to a deque of audio samples. 69 | */ 70 | audio_samples *audio(); 71 | 72 | protected: 73 | 74 | /** 75 | * Construct an instance of imbe_decoder. Access is protected 76 | * because this is an abstract class and users should call 77 | * make_imbe_decoder to construct concrete instances. 78 | */ 79 | imbe_decoder(); 80 | 81 | private: 82 | 83 | /** 84 | * The audio samples produced by the IMBE decoder. 85 | */ 86 | audio_samples d_audio; 87 | 88 | }; 89 | 90 | #endif /* INCLUDED_IMBE_DECODER_H */ 91 | -------------------------------------------------------------------------------- /op25-headers/op25_fsk4_demod_ff.h: -------------------------------------------------------------------------------- 1 | /* -*- C++ -*- */ 2 | 3 | /* 4 | * Copyright 2006, 2007 Frank (Radio Rausch) 5 | * Copyright 2011 Steve Glass 6 | * 7 | * This file is part of OP25. 8 | * 9 | * OP25 is free software; you can redistribute it and/or modify it 10 | * under the terms of the GNU General Public License as published by 11 | * the Free Software Foundation; either version 3, or (at your option) 12 | * any later version. 13 | * 14 | * OP25 is distributed in the hope that it will be useful, but WITHOUT 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 16 | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 17 | * License for more details. 18 | * 19 | * You should have received a copy of the GNU General Public License 20 | * along with OP25; see the file COPYING. If not, write to the Free 21 | * Software Foundation, Inc., 51 Franklin Street, Boston, MA 22 | * 02110-1301, USA. 23 | */ 24 | 25 | #ifndef INCLUDED_OP25_FSK4_DEMOD_FF_H 26 | #define INCLUDED_OP25_FSK4_DEMOD_FF_H 27 | 28 | #include 29 | #include 30 | 31 | #include 32 | 33 | typedef boost::shared_ptr op25_fsk4_demod_ff_sptr; 34 | 35 | op25_fsk4_demod_ff_sptr op25_make_fsk4_demod_ff(gr_msg_queue_sptr queue, float sample_rate, float symbol_rate); 36 | 37 | /** 38 | * op25_fsk4_demod_ff is a GNU Radio block for demodulating APCO P25 39 | * CF4M signals. This class expects its input to consist of a 4 level 40 | * FSK modulated baseband signal. It produces a stream of symbols. 41 | * 42 | * All inputs are post FM demodulator and symbol shaping filter data 43 | * is normalized before being sent to this block so these parameters 44 | * should not need adjusting even when working on different signals. 45 | * 46 | * Nominal levels are -3, -1, +1, and +3. 47 | */ 48 | class op25_fsk4_demod_ff : public gr_block 49 | { 50 | public: 51 | 52 | /** 53 | * op25_fsk4_demod_ff (virtual) destructor. 54 | */ 55 | virtual ~op25_fsk4_demod_ff(); 56 | 57 | /** 58 | * Estimate nof_input_items_reqd for a given nof_output_items. 59 | */ 60 | virtual void forecast(int noutput_items, gr_vector_int &inputs_required); 61 | 62 | /** 63 | * Process baseband into symbols. 64 | */ 65 | virtual int general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); 66 | 67 | private: 68 | 69 | /** 70 | * Expose class to public ctor. Create a new instance of 71 | * op25_fsk4_demod_ff and wrap it in a shared_ptr. This is 72 | * effectively the public constructor. 73 | */ 74 | friend op25_fsk4_demod_ff_sptr op25_make_fsk4_demod_ff(gr_msg_queue_sptr queue, float sample_rate, float symbol_rate); 75 | 76 | /** 77 | * op25_fsk4_demod_ff private constructor. 78 | */ 79 | op25_fsk4_demod_ff(gr_msg_queue_sptr queue, float sample_rate, float symbol_rate); 80 | 81 | /** 82 | * Called when we want the input frequency to be adjusted. 83 | */ 84 | void send_frequency_correction(); 85 | 86 | /** 87 | * Tracking loop. 88 | */ 89 | bool tracking_loop_mmse(float input, float *output); 90 | 91 | private: 92 | 93 | const float d_block_rate; 94 | 95 | boost::scoped_array d_history; 96 | 97 | size_t d_history_last; 98 | 99 | gr_msg_queue_sptr d_queue; 100 | 101 | double d_symbol_clock; 102 | 103 | double d_symbol_spread; 104 | 105 | const float d_symbol_time; 106 | 107 | double fine_frequency_correction; 108 | 109 | double coarse_frequency_correction; 110 | 111 | }; 112 | 113 | #endif /* INCLUDED_OP25_FSK4_DEMOD_FF_H */ 114 | -------------------------------------------------------------------------------- /smartnet.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 Free Software Foundation, Inc. 3 | * 4 | * This file is part of GNU Radio 5 | * 6 | * GNU Radio is free software; you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation; either version 3, or (at your option) 9 | * any later version. 10 | * 11 | * GNU Radio is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with GNU Radio; see the file COPYING. If not, write to 18 | * the Free Software Foundation, Inc., 51 Franklin Street, 19 | * Boston, MA 02110-1301, USA. 20 | */ 21 | 22 | 23 | /* 24 | * GNU Radio C++ example creating dial tone 25 | * ("the simplest thing that could possibly work") 26 | * 27 | * Send a tone each to the left and right channels of stereo audio 28 | * output and let the user's brain sum them. 29 | * 30 | * GNU Radio makes extensive use of Boost shared pointers. Signal processing 31 | * blocks are typically created by calling a "make" factory function, which 32 | * returns an instance of the block as a typedef'd shared pointer that can 33 | * be used in any way a regular pointer can. Shared pointers created this way 34 | * keep track of their memory and free it at the right time, so the user 35 | * doesn't need to worry about it (really). 36 | * 37 | */ 38 | 39 | // Include header files for each block used in flowgraph 40 | 41 | #include 42 | #include 43 | #include 44 | #include // copy 45 | #include 46 | #include 47 | 48 | #include "logging_receiver_dsd.h" 49 | #include "smartnet_crc.h" 50 | #include "smartnet_deinterleave.h" 51 | #include "talkgroup.h" 52 | 53 | #include 54 | #include 55 | 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | 62 | 63 | #include 64 | #include 65 | 66 | #include 67 | #include 68 | #include 69 | 70 | #include 71 | #include 72 | 73 | #include 74 | #include 75 | #include 76 | #include 77 | #include 78 | #include 79 | #include 80 | #include 81 | #include 82 | #include 83 | #include 84 | #include 85 | #include 86 | 87 | #include 88 | #include 89 | #include 90 | #include 91 | #include 92 | 93 | 94 | 95 | namespace po = boost::program_options; 96 | 97 | using namespace std; 98 | 99 | 100 | int lastcmd = 0; 101 | long lastaddress = 0; 102 | int thread_num=0; 103 | double center_freq; 104 | bool console = false; 105 | 106 | 107 | gr_top_block_sptr tb; 108 | osmosdr_source_c_sptr src; 109 | 110 | 111 | vector loggers; 112 | unsigned int max_loggers = 6; 113 | unsigned int num_loggers = 0; 114 | vector active_loggers; 115 | 116 | 117 | vector talkgroups; 118 | vector active_tg; 119 | char **menu_choices; 120 | char status[150]; 121 | ITEM **tg_menu_items; 122 | 123 | 124 | WINDOW *active_tg_win; 125 | WINDOW *tg_menu_win; 126 | WINDOW *status_win; 127 | MENU *tg_menu; 128 | 129 | 130 | 131 | volatile sig_atomic_t exit_flag = 0; 132 | 133 | 134 | void update_active_tg_win() { 135 | werase(active_tg_win); 136 | box(active_tg_win, 0, 0); 137 | int i=0; 138 | for(std::vector::iterator it = active_tg.begin(); it != active_tg.end(); ++it) { 139 | Talkgroup *tg = (Talkgroup *) *it; 140 | /*s = tg->menu_string(); 141 | c = (char *) malloc((s.size() + 1) * sizeof(char)); 142 | //strncpy(c, s.c_str(), s.size()); 143 | //c[s.size()] = '\0'; 144 | strcpy(c, s.c_str()); 145 | */ 146 | 147 | mvwprintw(active_tg_win,i*2+1,2,"TG: %s", tg->alpha_tag.c_str()); 148 | mvwprintw(active_tg_win,i*2+2,6,"%s ", tg->description.c_str()); 149 | mvwprintw(active_tg_win,i*2+1,40,"Num: %5lu", tg->number); 150 | mvwprintw(active_tg_win,i*2+2,40,"Tag: %s", tg->tag.c_str()); 151 | mvwprintw(active_tg_win,i*2+2,60,"Group: %s", tg->group.c_str()); 152 | 153 | i++; 154 | } 155 | 156 | wrefresh(active_tg_win); 157 | 158 | } 159 | void update_status_win(char *c) { 160 | wclear(status_win); 161 | //wattron(status_win,A_REVERSE); 162 | mvwprintw(status_win,0,2,"%s",c); 163 | wrefresh(status_win); 164 | } 165 | void create_status_win() { 166 | int startx, starty, width, height; 167 | 168 | height = 1; 169 | width = COLS; 170 | starty = LINES-1; 171 | startx = 0; 172 | 173 | status_win = newwin(height, width, starty, startx); 174 | } 175 | void create_active_tg_win() { 176 | int startx, starty, width, height; 177 | 178 | height = 20; 179 | width = COLS; 180 | starty = 0; 181 | startx = 0; 182 | 183 | active_tg_win = newwin(height, width, starty, startx); 184 | box(active_tg_win, 0, 0); 185 | 186 | wrefresh(active_tg_win); 187 | } 188 | 189 | void create_tg_menu() { 190 | std::string s; 191 | int n_choices, i; 192 | char *c; 193 | 194 | //printw("%s\n", s.c_str()); 195 | menu_choices = new char*[talkgroups.size()]; 196 | //menu_choices = malloc(talkgroups.size(), sizeof(char *)); 197 | i=0; 198 | for(std::vector::iterator it = talkgroups.begin(); it != talkgroups.end(); ++it) { 199 | 200 | Talkgroup *tg = (Talkgroup *) *it; 201 | s = tg->menu_string(); 202 | c = (char *) malloc((s.size() + 1) * sizeof(char)); 203 | //strncpy(c, s.c_str(), s.size()); 204 | //c[s.size()] = '\0'; 205 | strcpy(c, s.c_str()); 206 | menu_choices[i] = c; 207 | i++; 208 | } 209 | 210 | n_choices = talkgroups.size(); //ARRAY_SIZE(menu_choices); 211 | tg_menu_items = (ITEM **) calloc(n_choices + 1, sizeof(ITEM *)); 212 | 213 | 214 | for (i=0; i < n_choices; ++i) { 215 | tg_menu_items[i] = new_item(menu_choices[i], menu_choices[i]); 216 | set_item_userptr(tg_menu_items[i], (void *) talkgroups[i]); 217 | } 218 | 219 | tg_menu = new_menu((ITEM **) tg_menu_items); 220 | 221 | tg_menu_win = newwin(LINES - 11, COLS, 10, 0); 222 | keypad(tg_menu_win, TRUE); 223 | 224 | 225 | set_menu_win(tg_menu, tg_menu_win); 226 | set_menu_sub(tg_menu, derwin(tg_menu_win, LINES - 15, COLS - 4, 2, 2)); 227 | set_menu_format(tg_menu, LINES - 14 , 1); 228 | //set_menu_mark(tg_menu, " * "); 229 | box(tg_menu_win,0,0); 230 | menu_opts_off(tg_menu, O_SHOWDESC | O_ONEVALUE); 231 | //menu_opts_off(tg_menu, O_ONEVALUE); 232 | 233 | post_menu(tg_menu); 234 | 235 | } 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | void exit_interupt(int sig){ // can be called asynchronously 244 | exit_flag = 1; // set flag 245 | } 246 | 247 | void init_loggers(int num, float center_freq) { 248 | 249 | // static loggers 250 | for (int i = 0; i < num; i++) { 251 | log_dsd_sptr log = make_log_dsd( center_freq, center_freq, 0, i); 252 | loggers.push_back(log); 253 | tb->connect(src, 0, log, 0); 254 | 255 | } 256 | 257 | } 258 | 259 | float getfreq(int cmd) { 260 | float freq; 261 | if (cmd < 0x1b8) { 262 | freq = float(cmd * 0.025 + 851.0125); 263 | } else if (cmd < 0x230) { 264 | freq = float(cmd * 0.025 + 851.0125 - 10.9875); 265 | } else { 266 | freq = 0; 267 | } 268 | 269 | return freq; 270 | } 271 | 272 | void parse_file(string filename) { 273 | ifstream in(filename.c_str()); 274 | if (!in.is_open()) return; 275 | 276 | boost::char_separator sep(","); 277 | typedef boost::tokenizer< boost::char_separator > t_tokenizer; 278 | 279 | vector< string > vec; 280 | string line; 281 | 282 | while (getline(in,line)) 283 | { 284 | 285 | t_tokenizer tok(line, sep); 286 | //Tokenizer tok(line); 287 | vec.assign(tok.begin(),tok.end()); 288 | if (vec.size() < 8) continue; 289 | 290 | Talkgroup *tg = new Talkgroup(atoi( vec[0].c_str()), vec[2].at(0),vec[3].c_str(),vec[4].c_str(),vec[5].c_str() ,vec[6].c_str(),atoi(vec[7].c_str()) ); 291 | 292 | talkgroups.push_back(tg); 293 | } 294 | } 295 | 296 | 297 | void parse_status(int command, int address, int groupflag) { 298 | int Value = address << 1 | (groupflag ? (1) : 0); 299 | int GroupTimeout = Value & (0x1f); 300 | Value >>= 5; 301 | int ConnecTimeout = Value & (0x1f); 302 | Value >>= 5; 303 | int DispatchTimeout = Value & 0xf; 304 | Value >>= 4; 305 | int Power = Value & 1; 306 | Value >>= 1; 307 | int OpCode = Value; 308 | 309 | if (console) { 310 | sprintf(status, "Status: %d \tPower: %d \tDispatchTimeout: %d \tConnectTimeOut: %d \tGroupTimeOut: %d", OpCode, Power, DispatchTimeout, ConnecTimeout, GroupTimeout); 311 | update_status_win(status); 312 | } 313 | } 314 | 315 | float parse_message(string s) { 316 | float retfreq = 0; 317 | bool rxfound = false; 318 | std::vector x; 319 | boost::split(x, s, boost::is_any_of(","), boost::token_compress_on); 320 | 321 | long address = atoi( x[0].c_str() ) & 0xFFF0; 322 | int groupflag = atoi( x[1].c_str() ); 323 | int command = atoi( x[2].c_str() ); 324 | char shell_command[200]; 325 | 326 | x.clear(); 327 | vector().swap(x); 328 | 329 | if (command < 0x2d0) { 330 | 331 | if ( lastcmd == 0x308) { 332 | // Channel Grant 333 | if ( (address != 56016) && (address != 8176)) { 334 | retfreq = getfreq(command); 335 | //std::cout << "Channel Grant: " << lastaddress << " Address: " << address << " Command: " << command << " Last Command: " << lastcmd << std::endl; 336 | } 337 | } else { 338 | // Call continuation 339 | if ( (address != 56016) && (address != 8176)) { 340 | retfreq = getfreq(command); 341 | //std::cout << "Call Continue: " << lastaddress << " Address: " << address << " Command: " << command << " Last Command: " << lastcmd << std::endl; 342 | 343 | } 344 | } 345 | } 346 | 347 | if (command == 0x03c0) { 348 | //parse_status(command, address,groupflag); 349 | } 350 | 351 | 352 | 353 | if (retfreq) { 354 | for(vector::iterator it = loggers.begin(); it != loggers.end();it++) { 355 | log_dsd_sptr rx = *it; 356 | 357 | if (rx->is_active() && (rx->lastupdate() > 4.0)) { 358 | 359 | if (console) { 360 | for(std::vector::iterator tg_it = active_tg.begin(); tg_it != active_tg.end(); ++tg_it) { 361 | Talkgroup *tg = (Talkgroup *) *tg_it; 362 | if (tg->number == rx->get_talkgroup()) { 363 | active_tg.erase(tg_it); 364 | break; 365 | } 366 | } 367 | 368 | update_active_tg_win(); 369 | } 370 | sprintf(shell_command,"./encode-upload.sh %s > /dev/null 2>&1 &", rx->get_filename()); 371 | 372 | rx->deactivate(); 373 | num_loggers--; 374 | 375 | system(shell_command); 376 | } 377 | } 378 | for(vector::iterator it = loggers.begin(); it != loggers.end(); ++it) { 379 | log_dsd_sptr rx = *it; 380 | 381 | if (rx->is_active()) 382 | { 383 | if (rx->get_talkgroup() == address) { 384 | if (rx->get_freq() != retfreq) { 385 | if (console) { 386 | sprintf(status, "Retuning TG: %Ld \tOld Freq: %g \tNew Freq: %g \t TG last update %d seconds ago",rx->get_talkgroup(),rx->get_freq(),retfreq,rx->lastupdate()); 387 | update_status_win(status); 388 | } 389 | rx->tune_offset(retfreq); 390 | } 391 | rx->unmute(); 392 | 393 | rxfound = true; 394 | } else { 395 | if (rx->get_freq() == retfreq) { 396 | if (console) { 397 | sprintf(status, "%g \t- Freq overlap: Existing TG %d \tNew TG %d \tTG Updated %d seconds ago",rx->get_freq(),rx->get_talkgroup(),address,rx->lastupdate()); 398 | update_status_win(status); 399 | } 400 | //cout << " !! Someone else is on my Channel - My TG: "<< rx->get_talkgroup() << " Freq: " <get_freq() << " Intruding TG: " << address << endl; 401 | rx->mute(); 402 | } 403 | } 404 | } 405 | } 406 | 407 | 408 | if ((!rxfound)){ 409 | Talkgroup *rx_talkgroup = NULL; 410 | bool record_tg = false; 411 | for(std::vector::iterator it = talkgroups.begin(); it != talkgroups.end(); ++it) { 412 | Talkgroup *tg = (Talkgroup *) *it; 413 | if (tg->number == address) { 414 | rx_talkgroup = tg; 415 | break; 416 | } 417 | 418 | } 419 | if (rx_talkgroup) { 420 | if (((rx_talkgroup->get_priority() == 1) && (num_loggers < max_loggers)) || 421 | ((rx_talkgroup->get_priority() == 2) && (num_loggers < 4 )) || 422 | ((rx_talkgroup->get_priority() == 3) && (num_loggers < 2 ))) { 423 | record_tg = true; 424 | if (console) { 425 | active_tg.push_back(rx_talkgroup); 426 | update_active_tg_win(); 427 | } 428 | } else { 429 | record_tg = false; 430 | } 431 | } 432 | 433 | if (record_tg){ 434 | for(vector::iterator it = loggers.begin(); it != loggers.end();it++) { 435 | log_dsd_sptr rx = *it; 436 | if (!rx->is_active()) 437 | { 438 | num_loggers++; 439 | rx->activate(retfreq, address,num_loggers); 440 | break; 441 | } 442 | } 443 | 444 | } 445 | 446 | 447 | } 448 | 449 | } 450 | 451 | 452 | 453 | lastaddress = address; 454 | lastcmd = command; 455 | 456 | 457 | return retfreq; 458 | } 459 | 460 | 461 | int main(int argc, char **argv) 462 | { 463 | 464 | std::string device_addr; 465 | double samp_rate, chan_freq, error; 466 | int if_gain, bb_gain, rf_gain; 467 | //setup the program options 468 | po::options_description desc("Allowed options"); 469 | desc.add_options() 470 | ("help", "help message") 471 | ("arg", po::value(&device_addr)->default_value(""), "the device arguments in string format") 472 | ("rate", po::value(&samp_rate)->default_value(1e6), "the sample rate in samples per second") 473 | ("center", po::value(¢er_freq)->default_value(10e6), "the center frequency in Hz") 474 | ("error", po::value(&error)->default_value(0), "the Error in frequency in Hz") 475 | ("freq", po::value(&chan_freq)->default_value(10e6), "the frequency in Hz of the trunking channel") 476 | ("rfgain", po::value(&rf_gain)->default_value(14), "RF Gain") 477 | ("bbgain", po::value(&bb_gain)->default_value(25), "BB Gain") 478 | ("ifgain", po::value(&if_gain)->default_value(25), "IF Gain") 479 | ; 480 | po::variables_map vm; 481 | po::store(po::parse_command_line(argc, argv, desc), vm); 482 | po::notify(vm); 483 | 484 | //print the help message 485 | if (vm.count("help")){ 486 | std::cout 487 | << boost::format("SmartNet Trunking Reciever %s") % desc << std::endl 488 | << "The tags sink demo block will print USRP source time stamps." << std::endl 489 | << "The tags source demo block will send bursts to the USRP sink." << std::endl 490 | << "Look at the USRP output on a scope to see the timed bursts." << std::endl 491 | << std::endl; 492 | return ~0; 493 | } 494 | 495 | 496 | 497 | 498 | signal(SIGINT, exit_interupt); 499 | parse_file("ChanList.csv"); 500 | 501 | tb = gr_make_top_block("smartnet"); 502 | 503 | 504 | src = osmosdr_make_source_c(); 505 | cout << "Setting sample rate to: " << samp_rate << endl; 506 | src->set_sample_rate(samp_rate); 507 | cout << "Tunning to " << center_freq - error << "hz" << endl; 508 | src->set_center_freq(center_freq - error,0); 509 | 510 | cout << "Setting RF gain to " << rf_gain << endl; 511 | cout << "Setting BB gain to " << bb_gain << endl; 512 | cout << "Setting IF gain to " << if_gain << endl; 513 | 514 | src->set_gain(rf_gain); 515 | src->set_if_gain(if_gain); 516 | src->set_bb_gain(bb_gain); 517 | 518 | 519 | 520 | 521 | float samples_per_second = samp_rate; 522 | float syms_per_sec = 3600; 523 | float gain_mu = 0.01; 524 | float mu=0.5; 525 | float omega_relative_limit = 0.3; 526 | float offset = center_freq - chan_freq; 527 | float clockrec_oversample = 3; 528 | int decim = int(samples_per_second / (syms_per_sec * clockrec_oversample)); 529 | float sps = samples_per_second/decim/syms_per_sec; 530 | const double pi = boost::math::constants::pi(); 531 | 532 | cout << "Control channel offset: " << offset << endl; 533 | cout << "Decim: " << decim << endl; 534 | cout << "Samples per symbol: " << sps << endl; 535 | 536 | 537 | init_loggers(max_loggers, center_freq); 538 | 539 | gr_msg_queue_sptr queue = gr_make_msg_queue(); 540 | 541 | 542 | gr_freq_xlating_fir_filter_ccf_sptr prefilter = gr_make_freq_xlating_fir_filter_ccf(decim, 543 | gr_firdes::low_pass(1, samp_rate, 10000, 12000), 544 | offset, 545 | samp_rate); 546 | 547 | 548 | gr_pll_freqdet_cf_sptr pll_demod = gr_make_pll_freqdet_cf(2.0 / clockrec_oversample, 2*pi/clockrec_oversample, 549 | -2*pi/clockrec_oversample); 550 | 551 | digital_fll_band_edge_cc_sptr carriertrack = digital_make_fll_band_edge_cc(sps, 0.6, 64, 0.35); 552 | 553 | digital_clock_recovery_mm_ff_sptr softbits = digital_make_clock_recovery_mm_ff(sps, 0.25 * gain_mu * gain_mu, mu, gain_mu, omega_relative_limit); 554 | 555 | 556 | digital_binary_slicer_fb_sptr slicer = digital_make_binary_slicer_fb(); 557 | gr_correlate_access_code_tag_bb_sptr start_correlator = gr_make_correlate_access_code_tag_bb("10101100",0,"smartnet_preamble"); 558 | 559 | 560 | smartnet_deinterleave_sptr deinterleave = smartnet_make_deinterleave(); 561 | 562 | smartnet_crc_sptr crc = smartnet_make_crc(queue); 563 | 564 | /* gr_null_sink_sptr nullsink = gr_make_null_sink(sizeof(u_char)); 565 | tb->connect(deinterleave,0,nullsink,0);*/ 566 | 567 | tb->connect(src,0,prefilter,0); 568 | tb->connect(prefilter,0,carriertrack,0); 569 | tb->connect(carriertrack, 0, pll_demod, 0); 570 | tb->connect(pll_demod, 0, softbits, 0); 571 | tb->connect(softbits, 0, slicer, 0); 572 | tb->connect(slicer, 0, start_correlator, 0); 573 | 574 | 575 | tb->connect(start_correlator, 0, deinterleave, 0); 576 | 577 | tb->connect(deinterleave, 0, crc, 0); 578 | 579 | tb->start(); 580 | 581 | parse_file("ChanList.csv"); 582 | if (console) { 583 | initscr(); 584 | cbreak(); 585 | noecho(); 586 | nodelay(active_tg_win,TRUE); 587 | 588 | 589 | create_active_tg_win(); 590 | create_status_win(); 591 | } 592 | 593 | 594 | gr_message_sptr msg; 595 | while (1) { 596 | if(exit_flag){ // my action when signal set it 1 597 | printf("\n Signal caught!\n"); 598 | tb->stop(); 599 | endwin(); 600 | return 0; 601 | } 602 | 603 | 604 | msg = queue->delete_head(); 605 | parse_message(msg->to_string()); 606 | msg.reset(); 607 | //delete(sentence); 608 | 609 | 610 | } 611 | 612 | endwin(); 613 | 614 | // Exit normally. 615 | return 0; 616 | } 617 | -------------------------------------------------------------------------------- /smartnet_crc.cc: -------------------------------------------------------------------------------- 1 | //smartnet_crc.cc 2 | /* -*- c++ -*- */ 3 | /* 4 | * Copyright 2012 Nick Foster 5 | * 6 | * This file is part of gr_smartnet 7 | * 8 | * gr_smartnet is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2, or (at your option) 11 | * any later version. 12 | * 13 | * gr_smartnet is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with GNU Radio; see the file COPYING. If not, write to 20 | * the Free Software Foundation, Inc., 51 Franklin Street, 21 | * Boston, MA 02110-1301, USA. 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include "config.h" 26 | #endif 27 | 28 | #include "smartnet_crc.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include "smartnet_types.h" 34 | 35 | #define VERBOSE 0 36 | 37 | /* 38 | * Create a new instance of smartnet_crc and return 39 | * a boost shared_ptr. This is effectively the public constructor. 40 | */ 41 | smartnet_crc_sptr smartnet_make_crc(gr_msg_queue_sptr queue) 42 | { 43 | return smartnet_crc_sptr (new smartnet_crc (queue)); 44 | } 45 | 46 | smartnet_crc::smartnet_crc (gr_msg_queue_sptr queue) 47 | : gr_sync_block ("crc", 48 | gr_make_io_signature (1, 1, sizeof (char)), 49 | gr_make_io_signature (0, 0, 0)) 50 | { 51 | set_output_multiple(38); 52 | d_queue = queue; 53 | } 54 | 55 | /* 56 | * Our virtual destructor. 57 | */ 58 | smartnet_crc::~smartnet_crc () 59 | { 60 | //nothing else required in this example 61 | } 62 | 63 | static void smartnet_ecc(char *out, const char *in) { 64 | char expected[76]; 65 | char syndrome[76]; 66 | 67 | //first we calculate the EXPECTED parity bits from the RECEIVED bitstream 68 | //parity is I[i] ^ I[i-1] 69 | //since the bitstream is still interleaved with the P bits, we can do this while running 70 | expected[0] = in[0] & 0x01; //info bit 71 | expected[1] = in[0] & 0x01; //this is a parity bit, prev bits were 0 so we call x ^ 0 = x 72 | for(int k = 2; k < 76*2; k+=2) { 73 | expected[k] = in[k] & 0x01; //info bit 74 | expected[k+1] = (in[k] & 0x01) ^ (in[k-2] & 0x01); //parity bit 75 | } 76 | 77 | for(int k = 0; k < 76; k++) { 78 | syndrome[k] = expected[k] ^ (in[k] & 0x01); //calculate the syndrome 79 | if(VERBOSE) if(syndrome[k]) std::cout << "Bit error at bit " << k << std::endl; 80 | } 81 | 82 | for(int k = 0; k < 38-1; k++) { 83 | //now we correct the data using the syndrome: if two consecutive 84 | //parity bits are flipped, you've got a bad previous bit 85 | if(syndrome[2*k+1] && syndrome[2*k+3]) { 86 | out[k] = (in[2*k] & 0x01) ? 0 : 1; //byte-safe bit flip 87 | if(VERBOSE) std::cout << "I just flipped a bit!" << std::endl; 88 | } 89 | else out[k] = in[2*k]; 90 | } 91 | } 92 | 93 | static bool crc(const char *in) { 94 | unsigned int crcaccum = 0x0393; 95 | unsigned int crcop = 0x036E; 96 | unsigned int crcgiven; 97 | 98 | //calc expected crc 99 | for(int j=0; j<27; j++) { 100 | if(crcop & 0x01) crcop = (crcop >> 1)^0x0225; 101 | else crcop >>= 1; 102 | if (in[j] & 0x01) crcaccum = crcaccum ^ crcop; 103 | } 104 | 105 | //load given crc 106 | crcgiven = 0x0000; 107 | for(int j=0; j<10; j++) { 108 | crcgiven <<= 1; 109 | crcgiven += !bool(in[j+27] & 0x01); 110 | } 111 | 112 | return (crcgiven == crcaccum); 113 | } 114 | 115 | static smartnet_packet parse(const char *in) { 116 | smartnet_packet pkt; 117 | 118 | pkt.address = 0; 119 | pkt.groupflag = false; 120 | pkt.command = 0; 121 | pkt.crc = 0; 122 | 123 | int i=0; 124 | 125 | for(int k = 15; k >=0 ; k--) pkt.address += (!bool(in[i++] & 0x01)) << k; //first 16 bits are ID, MSB first 126 | pkt.groupflag = !bool(in[i++]); 127 | for(int k = 9; k >=0 ; k--) pkt.command += (!bool(in[i++] & 0x01)) << k; //next 10 bits are command, MSB first 128 | for(int k = 9; k >=0 ; k--) pkt.crc += (!bool(in[i++] & 0x01)) << k; //next 10 bits are CRC 129 | i++; //skip the guard bit 130 | 131 | //now correct things according to the mottrunk.txt description 132 | pkt.address ^= 0x33C7; 133 | pkt.command ^= 0x032A; 134 | 135 | return pkt; 136 | } 137 | 138 | int 139 | smartnet_crc::work (int noutput_items, 140 | gr_vector_const_void_star &input_items, 141 | gr_vector_void_star &output_items) 142 | { 143 | const char *in = (const char *) input_items[0]; 144 | 145 | 146 | int size = noutput_items - 76; 147 | if(size <= 0) { 148 | return 0; //better luck next time 149 | } 150 | 151 | uint64_t abs_sample_cnt = nitems_read(0); 152 | std::vector frame_tags; 153 | 154 | 155 | get_tags_in_range(frame_tags, 0, abs_sample_cnt, abs_sample_cnt + size, pmt::pmt_string_to_symbol("smartnet_frame")); 156 | 157 | if(frame_tags.size() == 0) { 158 | return 0; //sad trombone 159 | } 160 | 161 | std::vector::iterator tag_iter; 162 | 163 | for(tag_iter = frame_tags.begin(); tag_iter != frame_tags.end(); tag_iter++) { 164 | uint64_t mark = tag_iter->offset - abs_sample_cnt; 165 | if(VERBOSE) std::cout << "found a frame at " << mark << std::endl; 166 | 167 | char databits[38]; 168 | smartnet_ecc(databits, &in[mark]); 169 | bool crc_ok = crc(databits); 170 | 171 | if(crc_ok) { 172 | if(VERBOSE) std::cout << "CRC OK" << std::endl; 173 | //parse the message into readable chunks 174 | smartnet_packet pkt = parse(databits); 175 | 176 | //and throw it at the msgq 177 | std::ostringstream payload; 178 | payload.str(""); 179 | payload << pkt.address << "," << pkt.groupflag << "," << pkt.command; 180 | gr_message_sptr msg = gr_make_message_from_string(std::string(payload.str())); 181 | d_queue->handle(msg); 182 | } else if (VERBOSE) std::cout << "CRC FAILED" << std::endl; 183 | } 184 | return size; 185 | } 186 | -------------------------------------------------------------------------------- /smartnet_crc.h: -------------------------------------------------------------------------------- 1 | //smartnet_crc.h 2 | /* -*- c++ -*- */ 3 | /* 4 | * Copyright 2004 Free Software Foundation, Inc. 5 | * 6 | * This file is part of GNU Radio 7 | * 8 | * GNU Radio is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2, or (at your option) 11 | * any later version. 12 | * 13 | * GNU Radio is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with GNU Radio; see the file COPYING. If not, write to 20 | * the Free Software Foundation, Inc., 51 Franklin Street, 21 | * Boston, MA 02110-1301, USA. 22 | */ 23 | #ifndef smartnet_crc_H 24 | #define smartnet_crc_H 25 | 26 | #include 27 | #include 28 | 29 | class smartnet_crc; 30 | 31 | /* 32 | * We use boost::shared_ptr's instead of raw pointers for all access 33 | * to gr_blocks (and many other data structures). The shared_ptr gets 34 | * us transparent reference counting, which greatly simplifies storage 35 | * management issues. This is especially helpful in our hybrid 36 | * C++ / Python system. 37 | * 38 | * See http://www.boost.org/libs/smart_ptr/smart_ptr.htm 39 | * 40 | * As a convention, the _sptr suffix indicates a boost::shared_ptr 41 | */ 42 | typedef boost::shared_ptr smartnet_crc_sptr; 43 | 44 | /*! 45 | * \brief Return a shared_ptr to a new instance of smartnet_crc. 46 | * 47 | * To avoid accidental use of raw pointers, smartnet_crc's 48 | * constructor is private. ais_make_invert is the public 49 | * interface for creating new instances. 50 | */ 51 | smartnet_crc_sptr smartnet_make_crc(gr_msg_queue_sptr queue); 52 | 53 | /*! 54 | * \brief invert a packed stream of bits. 55 | * \ingroup block 56 | * 57 | * 58 | * This uses the preferred technique: subclassing gr_crc_block. 59 | */ 60 | class smartnet_crc : public gr_sync_block 61 | { 62 | private: 63 | // The friend declaration allows smartnet_make_crc to 64 | // access the private constructor. 65 | 66 | friend smartnet_crc_sptr smartnet_make_crc(gr_msg_queue_sptr queue); 67 | 68 | smartnet_crc(gr_msg_queue_sptr queue); // private constructor 69 | gr_msg_queue_sptr d_queue; 70 | 71 | public: 72 | ~smartnet_crc(); // public destructor 73 | 74 | // Where all the action really happens 75 | 76 | int work (int noutput_items, 77 | gr_vector_const_void_star &input_items, 78 | gr_vector_void_star &output_items); 79 | }; 80 | 81 | #endif /* smartnet_crc_H */ 82 | 83 | -------------------------------------------------------------------------------- /smartnet_deinterleave.cc: -------------------------------------------------------------------------------- 1 | //smartnet_deinterleave.cc 2 | /* -*- c++ -*- */ 3 | /* 4 | * Copyright 2012 Nick Foster 5 | * 6 | * This file is part of gr_smartnet 7 | * 8 | * gr_smartnet is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2, or (at your option) 11 | * any later version. 12 | * 13 | * gr_smartnet is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with GNU Radio; see the file COPYING. If not, write to 20 | * the Free Software Foundation, Inc., 51 Franklin Street, 21 | * Boston, MA 02110-1301, USA. 22 | */ 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include "config.h" 26 | #endif 27 | 28 | #include "smartnet_deinterleave.h" 29 | #include 30 | #include 31 | 32 | #define VERBOSE 0 33 | 34 | /* 35 | * Create a new instance of smartnet_deinterleave and return 36 | * a boost shared_ptr. This is effectively the public constructor. 37 | */ 38 | smartnet_deinterleave_sptr smartnet_make_deinterleave() 39 | { 40 | return smartnet_deinterleave_sptr (new smartnet_deinterleave ()); 41 | } 42 | 43 | /* 44 | * Specify constraints on number of input and output streams. 45 | * This info is used to construct the input and output signatures 46 | * (2nd & 3rd args to gr_block's constructor). The input and 47 | * output signatures are used by the runtime system to 48 | * check that a valid number and type of inputs and outputs 49 | * are connected to this block. In this case, we accept 50 | * only 1 input and 1 output. 51 | */ 52 | static const int MIN_IN = 1; // mininum number of input streams 53 | static const int MAX_IN = 1; // maximum number of input streams 54 | static const int MIN_OUT = 1; // minimum number of output streams 55 | static const int MAX_OUT = 1; // maximum number of output streams 56 | 57 | /* 58 | * The private constructor 59 | */ 60 | smartnet_deinterleave::smartnet_deinterleave () 61 | : gr_block ("deinterleave", 62 | gr_make_io_signature (MIN_IN, MAX_IN, sizeof (char)), 63 | gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (char))) 64 | { 65 | set_relative_rate((double)(76.0/84.0)); 66 | set_output_multiple(76); 67 | } 68 | 69 | /* 70 | * Our virtual destructor. 71 | */ 72 | smartnet_deinterleave::~smartnet_deinterleave () 73 | { 74 | // nothing else required in this example 75 | } 76 | 77 | void smartnet_deinterleave::forecast (int noutput_items, 78 | gr_vector_int &ninput_items_required) //estimate number of input samples required for noutput_items samples 79 | { 80 | int size = (noutput_items * 84) / 76; 81 | 82 | ninput_items_required[0] = size; 83 | } 84 | 85 | 86 | int 87 | smartnet_deinterleave::general_work (int noutput_items, 88 | gr_vector_int &ninput_items, 89 | gr_vector_const_void_star &input_items, 90 | gr_vector_void_star &output_items) 91 | { 92 | const char *in = (const char *) input_items[0]; 93 | char *out = (char *) output_items[0]; 94 | 95 | if(VERBOSE) std::cout << "Deinterleave called with " << noutput_items << " outputs" << std::endl; 96 | 97 | //you will need to look ahead 84 bits to post 76 bits of data 98 | //TODO this needs to be able to handle shorter frames while keeping state in order to end gracefully 99 | int size = ninput_items[0] - 84; 100 | if(size <= 0) { 101 | consume_each(0); 102 | return 0; //better luck next time 103 | } 104 | 105 | uint64_t abs_sample_cnt = nitems_read(0); 106 | std::vector preamble_tags; 107 | 108 | uint64_t outlen = 0; //output sample count 109 | 110 | get_tags_in_range(preamble_tags, 0, abs_sample_cnt, abs_sample_cnt + size, pmt::pmt_string_to_symbol("smartnet_preamble")); 111 | if(preamble_tags.size() == 0) { 112 | consume_each(size); 113 | return 0; 114 | } 115 | 116 | std::vector::iterator tag_iter; 117 | for(tag_iter = preamble_tags.begin(); tag_iter != preamble_tags.end(); tag_iter++) { 118 | uint64_t mark = tag_iter->offset - abs_sample_cnt; 119 | 120 | if(VERBOSE) std::cout << "found a preamble at " << tag_iter->offset << std::endl; 121 | 122 | for(int k=0; k<76/4; k++) { 123 | for(int l=0; l<4; l++) { 124 | out[k*4 + l] = in[mark + k + l*19]; 125 | } 126 | } 127 | 128 | //since you're a nonsynchronized block, you have to reissue a 129 | //tag with the correct output sample number 130 | add_item_tag(0, //stream ID 131 | nitems_written(0) + mark, //sample 132 | pmt::pmt_string_to_symbol("smartnet_frame"), //key 133 | pmt::pmt_t() //data (unused here) 134 | ); 135 | outlen += 76; 136 | } 137 | 138 | if(VERBOSE) std::cout << "consumed " << size << ", produced " << outlen << std::endl; 139 | consume_each(preamble_tags.back().offset - abs_sample_cnt + 84); 140 | return outlen; 141 | } 142 | -------------------------------------------------------------------------------- /smartnet_deinterleave.h: -------------------------------------------------------------------------------- 1 | //smartnet_deinterleave.h 2 | /* -*- c++ -*- */ 3 | /* 4 | * Copyright 2004 Free Software Foundation, Inc. 5 | * 6 | * This file is part of GNU Radio 7 | * 8 | * GNU Radio is free software; you can redistribute it and/or modify 9 | * it under the terms of the GNU General Public License as published by 10 | * the Free Software Foundation; either version 2, or (at your option) 11 | * any later version. 12 | * 13 | * GNU Radio is distributed in the hope that it will be useful, 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | * GNU General Public License for more details. 17 | * 18 | * You should have received a copy of the GNU General Public License 19 | * along with GNU Radio; see the file COPYING. If not, write to 20 | * the Free Software Foundation, Inc., 51 Franklin Street, 21 | * Boston, MA 02110-1301, USA. 22 | */ 23 | #ifndef smartnet_deinterleave_H 24 | #define smartnet_deinterleave_H 25 | 26 | #include 27 | 28 | class smartnet_deinterleave; 29 | 30 | /* 31 | * We use boost::shared_ptr's instead of raw pointers for all access 32 | * to gr_blocks (and many other data structures). The shared_ptr gets 33 | * us transparent reference counting, which greatly simplifies storage 34 | * management issues. This is especially helpful in our hybrid 35 | * C++ / Python system. 36 | * 37 | * See http://www.boost.org/libs/smart_ptr/smart_ptr.htm 38 | * 39 | * As a convention, the _sptr suffix indicates a boost::shared_ptr 40 | */ 41 | typedef boost::shared_ptr smartnet_deinterleave_sptr; 42 | 43 | /*! 44 | * \brief Return a shared_ptr to a new instance of smartnet_deinterleave. 45 | * 46 | * To avoid accidental use of raw pointers, smartnet_deinterleave's 47 | * constructor is private. smartnet_make_deinterleave is the public 48 | * interface for creating new instances. 49 | */ 50 | smartnet_deinterleave_sptr smartnet_make_deinterleave(); 51 | 52 | /*! 53 | * \brief unstuff a packed stream of bits. 54 | * \ingroup block 55 | * 56 | * 57 | * This uses the preferred technique: subclassing gr_block. 58 | */ 59 | class smartnet_deinterleave : public gr_block 60 | { 61 | private: 62 | // The friend declaration allows smartnet_make_deinterleave to 63 | // access the private constructor. 64 | 65 | friend smartnet_deinterleave_sptr smartnet_make_deinterleave(); 66 | 67 | smartnet_deinterleave(); // private constructor 68 | 69 | public: 70 | ~smartnet_deinterleave(); // public destructor 71 | 72 | // Where all the action really happens 73 | 74 | int general_work (int noutput_items, 75 | gr_vector_int &ninput_items, 76 | gr_vector_const_void_star &input_items, 77 | gr_vector_void_star &output_items); 78 | 79 | void forecast (int noutput_items, 80 | gr_vector_int &ninput_items_required); 81 | }; 82 | 83 | #endif /* smartnet_deinterleave_H */ 84 | 85 | -------------------------------------------------------------------------------- /smartnet_types.h: -------------------------------------------------------------------------------- 1 | //datatypes for smartnet decoder 2 | 3 | struct smartnet_packet { 4 | unsigned int address; 5 | bool groupflag; 6 | unsigned int command; 7 | unsigned int crc; 8 | }; 9 | -------------------------------------------------------------------------------- /smartnet_wavfile.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2004,2008 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 3, 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., 51 Franklin Street, 20 | * Boston, MA 02110-1301, USA. 21 | */ 22 | 23 | #ifdef HAVE_CONFIG_H 24 | #include "config.h" 25 | #endif 26 | 27 | #include "smartnet_wavfile.h" 28 | #include 29 | #include 30 | 31 | # define VALID_COMPRESSION_TYPE 0x0001 32 | 33 | // WAV files are always little-endian, so we need some byte switching macros 34 | 35 | // FIXME: Use libgruel versions 36 | 37 | #ifdef WORDS_BIGENDIAN 38 | 39 | #ifdef HAVE_BYTESWAP_H 40 | #include 41 | #else 42 | #warning Using non-portable code (likely wrong other than ILP32). 43 | 44 | static inline short int 45 | bswap_16 (unsigned short int x) 46 | { 47 | return ((((x) >> 8) & 0xff) | (((x) & 0xff) << 8)); 48 | } 49 | 50 | static inline unsigned int 51 | bswap_32 (unsigned int x) 52 | { 53 | return ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) \ 54 | | (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)); 55 | } 56 | #endif // HAVE_BYTESWAP_H 57 | 58 | static inline uint32_t 59 | host_to_wav(uint32_t x) 60 | { 61 | return bswap_32(x); 62 | } 63 | 64 | static inline uint16_t 65 | host_to_wav(uint16_t x) 66 | { 67 | return bswap_16(x); 68 | } 69 | 70 | static inline int16_t 71 | host_to_wav(int16_t x) 72 | { 73 | return bswap_16(x); 74 | } 75 | 76 | static inline uint32_t 77 | wav_to_host(uint32_t x) 78 | { 79 | return bswap_32(x); 80 | } 81 | 82 | static inline uint16_t 83 | wav_to_host(uint16_t x) 84 | { 85 | return bswap_16(x); 86 | } 87 | 88 | static inline int16_t 89 | wav_to_host(int16_t x) 90 | { 91 | return bswap_16(x); 92 | } 93 | 94 | #else 95 | 96 | static inline uint32_t 97 | host_to_wav(uint32_t x) 98 | { 99 | return x; 100 | } 101 | 102 | static inline uint16_t 103 | host_to_wav(uint16_t x) 104 | { 105 | return x; 106 | } 107 | 108 | static inline int16_t 109 | host_to_wav(int16_t x) 110 | { 111 | return x; 112 | } 113 | 114 | static inline uint32_t 115 | wav_to_host(uint32_t x) 116 | { 117 | return x; 118 | } 119 | 120 | static inline uint16_t 121 | wav_to_host(uint16_t x) 122 | { 123 | return x; 124 | } 125 | 126 | static inline int16_t 127 | wav_to_host(int16_t x) 128 | { 129 | return x; 130 | } 131 | 132 | #endif // WORDS_BIGENDIAN 133 | 134 | 135 | bool 136 | smartnet_wavheader_parse(FILE *fp, 137 | unsigned int &sample_rate_o, 138 | int &nchans_o, 139 | int &bytes_per_sample_o, 140 | int &first_sample_pos_o, 141 | unsigned int &samples_per_chan_o) 142 | { 143 | // _o variables take return values 144 | char str_buf[8] = {0}; 145 | 146 | uint32_t file_size; 147 | uint32_t fmt_hdr_skip; 148 | uint16_t compression_type; 149 | uint16_t nchans; 150 | uint32_t sample_rate; 151 | uint32_t avg_bytes_per_sec; 152 | uint16_t block_align; 153 | uint16_t bits_per_sample; 154 | uint32_t chunk_size; 155 | 156 | size_t fresult; 157 | 158 | fresult = fread(str_buf, 1, 4, fp); 159 | if (fresult != 4 || strncmp(str_buf, "RIFF", 4) || feof(fp)) { 160 | return false; 161 | } 162 | 163 | fresult = fread(&file_size, 1, 4, fp); 164 | 165 | fresult = fread(str_buf, 1, 8, fp); 166 | if (fresult != 8 || strncmp(str_buf, "WAVEfmt ", 8) || feof(fp)) { 167 | return false; 168 | } 169 | 170 | fresult = fread(&fmt_hdr_skip, 1, 4, fp); 171 | 172 | fresult = fread(&compression_type, 1, 2, fp); 173 | if (wav_to_host(compression_type) != VALID_COMPRESSION_TYPE) { 174 | return false; 175 | } 176 | 177 | fresult = fread(&nchans, 1, 2, fp); 178 | fresult = fread(&sample_rate, 1, 4, fp); 179 | fresult = fread(&avg_bytes_per_sec, 1, 4, fp); 180 | fresult = fread(&block_align, 1, 2, fp); 181 | fresult = fread(&bits_per_sample, 1, 2, fp); 182 | 183 | if (ferror(fp)) { 184 | return false; 185 | } 186 | 187 | fmt_hdr_skip = wav_to_host(fmt_hdr_skip); 188 | nchans = wav_to_host(nchans); 189 | sample_rate = wav_to_host(sample_rate); 190 | bits_per_sample = wav_to_host(bits_per_sample); 191 | 192 | if (bits_per_sample != 8 && bits_per_sample != 16) { 193 | return false; 194 | } 195 | 196 | fmt_hdr_skip -= 16; 197 | if (fmt_hdr_skip) { 198 | fseek(fp, fmt_hdr_skip, SEEK_CUR); 199 | } 200 | 201 | // data chunk 202 | fresult = fread(str_buf, 1, 4, fp); 203 | if (strncmp(str_buf, "data", 4)) { 204 | return false; 205 | } 206 | 207 | fresult = fread(&chunk_size, 1, 4, fp); 208 | if (ferror(fp)) { 209 | return false; 210 | } 211 | 212 | // More byte swapping 213 | chunk_size = wav_to_host(chunk_size); 214 | 215 | // Output values 216 | sample_rate_o = (unsigned) sample_rate; 217 | nchans_o = (int) nchans; 218 | bytes_per_sample_o = (int) (bits_per_sample / 8); 219 | first_sample_pos_o = (int) ftell(fp); 220 | samples_per_chan_o = (unsigned) (chunk_size / (bytes_per_sample_o * nchans)); 221 | return true; 222 | } 223 | 224 | 225 | short int 226 | smartnet_wav_read_sample(FILE *fp, int bytes_per_sample) 227 | { 228 | int16_t buf = 0; 229 | size_t fresult; 230 | 231 | fresult = fread(&buf, bytes_per_sample, 1, fp); 232 | 233 | return (short) wav_to_host(buf); 234 | } 235 | 236 | 237 | bool 238 | smartnet_wavheader_write(FILE *fp, 239 | unsigned int sample_rate, 240 | int nchans, 241 | int bytes_per_sample) 242 | { 243 | const int header_len = 44; 244 | char wav_hdr[header_len] = "RIFF\0\0\0\0WAVEfmt \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0data\0\0\0"; 245 | uint16_t nchans_f = (uint16_t) nchans; 246 | uint32_t sample_rate_f = (uint32_t) sample_rate; 247 | uint16_t block_align = bytes_per_sample * nchans; 248 | uint32_t avg_bytes = sample_rate * block_align; 249 | uint16_t bits_per_sample = bytes_per_sample * 8; 250 | 251 | nchans_f = host_to_wav(nchans_f); 252 | sample_rate_f = host_to_wav(sample_rate_f); 253 | block_align = host_to_wav(block_align); 254 | avg_bytes = host_to_wav(avg_bytes); 255 | bits_per_sample = host_to_wav(bits_per_sample); 256 | 257 | wav_hdr[16] = 0x10; // no extra bytes 258 | wav_hdr[20] = 0x01; // no compression 259 | memcpy((void *) (wav_hdr + 22), (void *) &nchans_f, 2); 260 | memcpy((void *) (wav_hdr + 24), (void *) &sample_rate_f, 4); 261 | memcpy((void *) (wav_hdr + 28), (void *) &avg_bytes, 4); 262 | memcpy((void *) (wav_hdr + 32), (void *) &block_align, 2); 263 | memcpy((void *) (wav_hdr + 34), (void *) &bits_per_sample, 2); 264 | 265 | fwrite(&wav_hdr, 1, header_len, fp); 266 | if (ferror(fp)) { 267 | return false; 268 | } 269 | 270 | return true; 271 | } 272 | 273 | 274 | void 275 | smartnet_wav_write_sample(FILE *fp, short int sample, int bytes_per_sample) 276 | { 277 | void *data_ptr; 278 | unsigned char buf_8bit; 279 | int16_t buf_16bit; 280 | 281 | if (bytes_per_sample == 1) { 282 | buf_8bit = (unsigned char) sample; 283 | data_ptr = (void *) &buf_8bit; 284 | } else { 285 | buf_16bit = host_to_wav((int16_t) sample); 286 | data_ptr = (void *) &buf_16bit; 287 | } 288 | 289 | fwrite(data_ptr, 1, bytes_per_sample, fp); 290 | } 291 | 292 | 293 | bool 294 | smartnet_wavheader_complete(FILE *fp, unsigned int byte_count) 295 | { 296 | uint32_t chunk_size = (uint32_t) byte_count; 297 | chunk_size = host_to_wav(chunk_size); 298 | 299 | fseek(fp, 40, SEEK_SET); 300 | fwrite(&chunk_size, 1, 4, fp); 301 | 302 | chunk_size = (uint32_t) byte_count + 36; // fmt chunk and data header 303 | chunk_size = host_to_wav(chunk_size); 304 | fseek(fp, 4, SEEK_SET); 305 | 306 | fwrite(&chunk_size, 1, 4, fp); 307 | 308 | if (ferror(fp)) { 309 | return false; 310 | } 311 | 312 | return true; 313 | } 314 | -------------------------------------------------------------------------------- /smartnet_wavfile.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2008 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 3, 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., 51 Franklin Street, 20 | * Boston, MA 02110-1301, USA. 21 | */ 22 | 23 | // This file stores all the RIFF file type knowledge for the gr_wavfile_* 24 | // blocks. 25 | 26 | //#include 27 | #include 28 | 29 | /*! 30 | * \brief Read signal information from a given WAV file. 31 | * 32 | * \p fp File pointer to an opened, empty file. 33 | * \p sample_rate Stores the sample rate [S/s] 34 | * \p nchans Number of channels 35 | * \p bytes_per_sample Bytes per sample, can either be 1 or 2 (corresponding to 36 | * 8 or 16 bit samples, respectively) 37 | * \p first_sample_pos Number of the first byte containing a sample. Use this 38 | * with fseek() to jump from the end of the file to the first sample 39 | * when in repeat mode. 40 | * \p samples_per_chan Number of samples per channel 41 | * \p normalize_fac The normalization factor with which you need to divide the 42 | * integer values of the samples to get them within [-1;1] 43 | * \p normalize_shift The value by which the sample values need to be shifted 44 | * after normalization (reason being, 8-bit WAV files store samples as 45 | * unsigned char and 16-bit as signed short int) 46 | * \return True on a successful read, false if the file could not be read or is 47 | * not a valid WAV file. 48 | */ 49 | bool 50 | smartnet_wavheader_parse(FILE *fp, 51 | unsigned int &sample_rate, 52 | int &nchans, 53 | int &bytes_per_sample, 54 | int &first_sample_pos, 55 | unsigned int &samples_per_chan); 56 | 57 | 58 | /*! 59 | * \brief Read one sample from an open WAV file at the current position. 60 | * 61 | * Takes care of endianness. 62 | */ 63 | short int 64 | smartnet_wav_read_sample(FILE *fp, int bytes_per_sample); 65 | 66 | 67 | /*! 68 | * \brief Write a valid RIFF file header 69 | * 70 | * Note: Some header values are kept blank because they're usually not known 71 | * a-priori (file and chunk lengths). Use smartnet_wavheader_complete() to fill 72 | * these in. 73 | */ 74 | bool 75 | smartnet_wavheader_write(FILE *fp, 76 | unsigned int sample_rate, 77 | int nchans, 78 | int bytes_per_sample); 79 | 80 | /*! 81 | * \brief Write one sample to an open WAV file at the current position. 82 | * 83 | * Takes care of endianness. 84 | */ 85 | void 86 | smartnet_wav_write_sample(FILE *fp, short int sample, int bytes_per_sample); 87 | 88 | 89 | /*! 90 | * \brief Complete a WAV header 91 | * 92 | * Note: The stream position is changed during this function. If anything 93 | * needs to be written to the WAV file after calling this function (which 94 | * shouldn't happen), you need to fseek() to the end of the file (or 95 | * whereever). 96 | * 97 | * \p fp File pointer to an open WAV file with a blank header 98 | * \p byte_count Length of all samples written to the file in bytes. 99 | */ 100 | bool 101 | smartnet_wavheader_complete(FILE *fp, unsigned int byte_count); 102 | -------------------------------------------------------------------------------- /smartnet_wavsink.cc: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2004,2006,2007,2008,2009 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 3, 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., 51 Franklin Street, 20 | * Boston, MA 02110-1301, USA. 21 | */ 22 | 23 | #ifdef HAVE_CONFIG_H 24 | #include "config.h" 25 | #endif 26 | 27 | #include "smartnet_wavsink.h" 28 | #include 29 | #include "smartnet_wavfile.h" 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | // win32 (mingw/msvc) specific 39 | #ifdef HAVE_IO_H 40 | #include 41 | #endif 42 | #ifdef O_BINARY 43 | #define OUR_O_BINARY O_BINARY 44 | #else 45 | #define OUR_O_BINARY 0 46 | #endif 47 | 48 | // should be handled via configure 49 | #ifdef O_LARGEFILE 50 | #define OUR_O_LARGEFILE O_LARGEFILE 51 | #else 52 | #define OUR_O_LARGEFILE 0 53 | #endif 54 | 55 | 56 | smartnet_wavsink_sptr 57 | smartnet_make_wavsink(const char *filename, 58 | int n_channels, 59 | unsigned int sample_rate, 60 | int bits_per_sample) 61 | { 62 | return smartnet_wavsink_sptr (new smartnet_wavsink (filename, 63 | n_channels, 64 | sample_rate, 65 | bits_per_sample)); 66 | } 67 | 68 | smartnet_wavsink::smartnet_wavsink(const char *filename, 69 | int n_channels, 70 | unsigned int sample_rate, 71 | int bits_per_sample) 72 | : gr_sync_block ("wavsink", 73 | gr_make_io_signature(1, n_channels, sizeof(float)), 74 | gr_make_io_signature(0, 0, 0)), 75 | d_sample_rate(sample_rate), d_nchans(n_channels), 76 | d_fp(0), d_new_fp(0), d_updated(false) 77 | { 78 | if (bits_per_sample != 8 && bits_per_sample != 16) { 79 | throw std::runtime_error("Invalid bits per sample (supports 8 and 16)"); 80 | } 81 | d_bytes_per_sample = bits_per_sample / 8; 82 | d_bytes_per_sample_new = d_bytes_per_sample; 83 | 84 | if (!open(filename)) { 85 | throw std::runtime_error ("can't open file"); 86 | } 87 | 88 | if (bits_per_sample == 8) { 89 | d_max_sample_val = 0xFF; 90 | d_min_sample_val = 0; 91 | d_normalize_fac = d_max_sample_val/2; 92 | d_normalize_shift = 1; 93 | } else { 94 | d_max_sample_val = 0x7FFF; 95 | d_min_sample_val = -0x7FFF; 96 | d_normalize_fac = d_max_sample_val; 97 | d_normalize_shift = 0; 98 | if (bits_per_sample != 16) { 99 | fprintf(stderr, "Invalid bits per sample value requested, using 16"); 100 | } 101 | } 102 | } 103 | 104 | 105 | bool 106 | smartnet_wavsink::open(const char* filename) 107 | { 108 | //this function is modified to append to .wav files instead of overwriting. 109 | int fd; 110 | gruel::scoped_lock guard(d_mutex); 111 | 112 | //first, check to see if the file exists. 113 | int filestat = 0; 114 | struct stat fileinfo; 115 | 116 | filestat = stat(filename, &fileinfo); 117 | 118 | if(filestat == 0 && fileinfo.st_size != 0) { //the file exists and is nonzero in size, so we must append to the .wav 119 | //open() and close() are both thread-safe public functions. they let you close and open files, so that's already in place. 120 | //all we need to add is support for appending to a file rather than overwriting it. 121 | 122 | //all right, here's the way this is gonna work. open the file just like gr_wavfile_source does... 123 | if((fd = ::open (filename,O_RDWR|OUR_O_LARGEFILE|OUR_O_BINARY)) < 0) { //we don't use O_APPEND because we need to overwrite the header 124 | perror(filename); 125 | return false; 126 | } 127 | if (d_new_fp) { // if we've already got a new one open, close it 128 | //printf("New fp found, closing\n"); 129 | fclose(d_new_fp); 130 | d_new_fp = 0; 131 | } 132 | 133 | if ((d_new_fp = fdopen (fd, "r+b")) == NULL) { 134 | perror (filename); 135 | ::close(fd); // don't leak file descriptor if fdopen fails. 136 | return false; 137 | } 138 | d_updated = true; 139 | unsigned new_sample_rate; 140 | int new_nchans, new_bytes_per_sample, new_first_sample_pos; 141 | 142 | //validate the data, be sure it's set up the same as the current block (sample rate, mono/stereo, etc) and barf if it isn't 143 | if (!smartnet_wavheader_parse(d_new_fp, 144 | new_sample_rate, 145 | new_nchans, 146 | new_bytes_per_sample, 147 | new_first_sample_pos, 148 | d_new_samples_per_chan)) { 149 | throw std::runtime_error("is not a valid wav file"); 150 | } 151 | 152 | //printf("Appended file %s has sample count %u and filesize %u\n", filename, d_new_samples_per_chan, fileinfo.st_size); 153 | //printf("Appended file %s has %f seconds of audio\n", filename, (float(d_new_samples_per_chan)/new_sample_rate)); 154 | 155 | if((new_sample_rate != d_sample_rate) || (new_nchans != d_nchans) || (new_bytes_per_sample != d_bytes_per_sample)) { 156 | throw std::runtime_error("WAV file with incompatible sample rate or channels"); 157 | } 158 | 159 | d_new_append = true; 160 | fseek(d_new_fp, 0, SEEK_END); //seek to the end of the file and start writin' 161 | 162 | } else { 163 | // we use the open system call to get access to the O_LARGEFILE flag. 164 | if ((fd = ::open (filename, 165 | O_WRONLY|O_CREAT|O_TRUNC|OUR_O_LARGEFILE|OUR_O_BINARY, 166 | 0664)) < 0){ 167 | perror (filename); 168 | return false; 169 | } 170 | d_new_append = false; 171 | 172 | if (d_new_fp) { // if we've already got a new one open, close it 173 | fclose(d_new_fp); 174 | d_new_fp = 0; 175 | } 176 | if ((d_new_fp = fdopen (fd, "wb")) == NULL) { 177 | perror (filename); 178 | ::close(fd); // don't leak file descriptor if fdopen fails. 179 | return false; 180 | } 181 | d_updated = true; 182 | 183 | if (!smartnet_wavheader_write(d_new_fp, 184 | d_sample_rate, 185 | d_nchans, 186 | d_bytes_per_sample_new)) { 187 | fprintf(stderr, "[%s] could not write to WAV file\n", __FILE__); 188 | exit(-1); 189 | } 190 | 191 | } 192 | 193 | return true; 194 | } 195 | 196 | 197 | void 198 | smartnet_wavsink::close() 199 | { 200 | gruel::scoped_lock guard(d_mutex); 201 | 202 | if (!d_fp) 203 | return; 204 | 205 | close_wav(); 206 | } 207 | 208 | void smartnet_wavsink::close_wav() 209 | { 210 | unsigned int byte_count = d_sample_count * d_bytes_per_sample; 211 | 212 | //printf("Writing wav header with %f seconds of audio\n", ((float(d_sample_count)/d_sample_rate)/d_nchans)/d_bytes_per_sample); 213 | 214 | if(!smartnet_wavheader_complete(d_fp, byte_count)) { 215 | throw std::runtime_error("Error writing wav header\n"); 216 | } 217 | 218 | fclose(d_fp); 219 | d_fp = NULL; 220 | } 221 | 222 | 223 | smartnet_wavsink::~smartnet_wavsink () 224 | { 225 | if (d_new_fp) { 226 | fclose(d_new_fp); 227 | } 228 | 229 | close(); 230 | } 231 | 232 | 233 | int 234 | smartnet_wavsink::work (int noutput_items, 235 | gr_vector_const_void_star &input_items, 236 | gr_vector_void_star &output_items) 237 | { 238 | float **in = (float **) &input_items[0]; 239 | int n_in_chans = input_items.size(); 240 | 241 | short int sample_buf_s; 242 | 243 | int nwritten; 244 | 245 | do_update(); // update: d_fp is reqd 246 | if (!d_fp) // drop output on the floor if there isn't a valid file open 247 | return noutput_items; 248 | 249 | for (nwritten = 0; nwritten < noutput_items; nwritten++) { 250 | for (int chan = 0; chan < d_nchans; chan++) { 251 | // Write zeros to channels which are in the WAV file 252 | // but don't have any inputs here 253 | if (chan < n_in_chans) { 254 | sample_buf_s = convert_to_short(in[chan][nwritten]); 255 | } else { 256 | sample_buf_s = 0; 257 | } 258 | 259 | smartnet_wav_write_sample(d_fp, sample_buf_s, d_bytes_per_sample); 260 | 261 | if (feof(d_fp) || ferror(d_fp)) { 262 | fprintf(stderr, "[%s] file i/o error\n", __FILE__); 263 | close(); 264 | exit(-1); 265 | } 266 | d_sample_count++; 267 | } 268 | } 269 | 270 | return nwritten; 271 | } 272 | 273 | 274 | short int 275 | smartnet_wavsink::convert_to_short(float sample) 276 | { 277 | sample += d_normalize_shift; 278 | sample *= d_normalize_fac; 279 | if (sample > d_max_sample_val) { 280 | sample = d_max_sample_val; 281 | } else if (sample < d_min_sample_val) { 282 | sample = d_min_sample_val; 283 | } 284 | 285 | return (short int) roundf(sample); 286 | } 287 | 288 | 289 | void 290 | smartnet_wavsink::set_bits_per_sample(int bits_per_sample) 291 | { 292 | gruel::scoped_lock guard(d_mutex); 293 | if (bits_per_sample == 8 || bits_per_sample == 16) { 294 | d_bytes_per_sample_new = bits_per_sample / 8; 295 | } 296 | } 297 | 298 | 299 | void 300 | smartnet_wavsink::set_sample_rate(unsigned int sample_rate) 301 | { 302 | gruel::scoped_lock guard(d_mutex); 303 | d_sample_rate = sample_rate; 304 | } 305 | 306 | 307 | void 308 | smartnet_wavsink::do_update() 309 | { 310 | if (!d_updated) { 311 | return; 312 | } 313 | 314 | gruel::scoped_lock guard(d_mutex); // hold mutex for duration of this block 315 | if (d_fp) { 316 | close_wav(); 317 | } 318 | 319 | d_fp = d_new_fp; // install new file pointer 320 | d_new_fp = 0; 321 | if (d_new_append) { 322 | d_sample_count = d_new_samples_per_chan * d_nchans; //set the sample count 323 | } else { 324 | d_sample_count = 0; 325 | } 326 | d_bytes_per_sample = d_bytes_per_sample_new; 327 | 328 | if (d_bytes_per_sample == 1) { 329 | d_max_sample_val = UCHAR_MAX; 330 | d_min_sample_val = 0; 331 | d_normalize_fac = d_max_sample_val/2; 332 | d_normalize_shift = 1; 333 | } else if (d_bytes_per_sample == 2) { 334 | d_max_sample_val = SHRT_MAX; 335 | d_min_sample_val = SHRT_MIN; 336 | d_normalize_fac = d_max_sample_val; 337 | d_normalize_shift = 0; 338 | } 339 | 340 | d_updated = false; 341 | } 342 | -------------------------------------------------------------------------------- /smartnet_wavsink.h: -------------------------------------------------------------------------------- 1 | /* -*- c++ -*- */ 2 | /* 3 | * Copyright 2008,2009 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 3, 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., 51 Franklin Street, 20 | * Boston, MA 02110-1301, USA. 21 | */ 22 | 23 | #ifndef INCLUDED_smartnet_wavsink_H 24 | #define INCLUDED_smartnet_wavsink_H 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | class smartnet_wavsink; 31 | typedef boost::shared_ptr smartnet_wavsink_sptr; 32 | 33 | /* 34 | * \p filename The .wav file to be opened 35 | * \p n_channels Number of channels (2 = stereo or I/Q output) 36 | * \p sample_rate Sample rate [S/s] 37 | * \p bits_per_sample 16 or 8 bit, default is 16 38 | */ 39 | smartnet_wavsink_sptr 40 | smartnet_make_wavsink (const char *filename, 41 | int n_channels, 42 | unsigned int sample_rate, 43 | int bits_per_sample = 16); 44 | 45 | /*! 46 | * \brief Read stream from a Microsoft PCM (.wav) file, output floats 47 | * 48 | * Values are within [-1;1]. 49 | * Check gr_make_wavfile_source() for extra info. 50 | * 51 | * \ingroup sink_blk 52 | */ 53 | class smartnet_wavsink : public gr_sync_block 54 | { 55 | private: 56 | friend smartnet_wavsink_sptr smartnet_make_wavsink (const char *filename, 57 | int n_channels, 58 | unsigned int sample_rate, 59 | int bits_per_sample); 60 | 61 | smartnet_wavsink(const char *filename, 62 | int n_channels, 63 | unsigned int sample_rate, 64 | int bits_per_sample); 65 | 66 | unsigned d_sample_rate; 67 | int d_nchans; 68 | unsigned d_sample_count; 69 | int d_bytes_per_sample; 70 | int d_bytes_per_sample_new; 71 | int d_max_sample_val; 72 | int d_min_sample_val; 73 | int d_normalize_shift; 74 | int d_normalize_fac; 75 | 76 | unsigned d_new_samples_per_chan; 77 | 78 | FILE *d_fp; 79 | FILE *d_new_fp; 80 | bool d_updated; 81 | bool d_new_append; 82 | boost::mutex d_mutex; 83 | 84 | /*! 85 | * \brief Convert a sample value within [-1;+1] to a corresponding 86 | * short integer value 87 | */ 88 | short convert_to_short(float sample); 89 | 90 | /*! 91 | * \brief Writes information to the WAV header which is not available 92 | * a-priori (chunk size etc.) and closes the file. Not thread-safe and 93 | * assumes d_fp is a valid file pointer, should thus only be called by 94 | * other methods. 95 | */ 96 | void close_wav(); 97 | 98 | public: 99 | ~smartnet_wavsink (); 100 | 101 | /*! 102 | * \brief Opens a new file and writes a WAV header. Thread-safe. 103 | */ 104 | bool open(const char* filename); 105 | 106 | /*! 107 | * \brief Closes the currently active file and completes the WAV 108 | * header. Thread-safe. 109 | */ 110 | void close(); 111 | 112 | /*! 113 | * \brief If any file changes have occurred, update now. This is called 114 | * internally by work() and thus doesn't usually need to be called by 115 | * hand. 116 | */ 117 | void do_update(); 118 | 119 | /*! 120 | * \brief Set the sample rate. This will not affect the WAV file 121 | * currently opened. Any following open() calls will use this new 122 | * sample rate. 123 | */ 124 | void set_sample_rate(unsigned int sample_rate); 125 | 126 | /*! 127 | * \brief Set bits per sample. This will not affect the WAV file 128 | * currently opened (see set_sample_rate()). If the value is neither 129 | * 8 nor 16, the call is ignored and the current value is kept. 130 | */ 131 | void set_bits_per_sample(int bits_per_sample); 132 | 133 | //returns the current time offset of the .wav file, for timestamp purposes 134 | float get_time(void) { 135 | if(d_fp) { 136 | return (float(d_sample_count)/d_sample_rate)/d_nchans; 137 | } else if(d_new_fp && d_new_append) { 138 | return float(d_new_samples_per_chan)/d_sample_rate; 139 | } else return 0; 140 | } 141 | 142 | int work(int noutput_items, 143 | gr_vector_const_void_star &input_items, 144 | gr_vector_void_star &output_items); 145 | 146 | }; 147 | 148 | #endif /* INCLUDED_smartnet_wavsink_H */ 149 | -------------------------------------------------------------------------------- /start_hack_rf_decode_856.sh: -------------------------------------------------------------------------------- 1 | #valgrind --leak-check=yes ./smartnet --freq 856187500.0 --center 857600000.0 --rate 5000000 --error -5300 --rfgain 14 --ifgain 35 --bbgain 35 > valgrind.txt 2>&1 2 | 3 | 4 | ./smartnet --freq 856187500.0 --center 857600000.0 --rate 5000000 --error -5300 --rfgain 14 --ifgain 20 --bbgain 20 5 | 6 | 7 | #./smartnet --freq 856187500.0 --center 858000000.0 --rate 8000000 --error -5500 --rfgain 14 --ifgain 35 --bbgain 35 8 | 9 | #./smartnet --freq 496444500.0 --center 496000000.0 --rate 1000000 --error 0 --rfgain 14 --ifgain 30 --bbgain 30 10 | 11 | 12 | #./smartnet --freq 856187500.0 --center 856207500.0 --rate 2000000 --error -5500 --rfgain 10 --ifgain 20 --bbgain 20 13 | 14 | #./smartnet --freq 855218500.0 --center 855000000.0 --rate 2000000 --error 0 --rfgain 14 --ifgain 25 --bbgain 25 15 | -------------------------------------------------------------------------------- /talkgroup.cc: -------------------------------------------------------------------------------- 1 | #include "talkgroup.h" 2 | 3 | 4 | Talkgroup::Talkgroup(long num, char m, std::string a, std::string d, std::string t, std::string g, int p) { 5 | number = num; 6 | mode = m; 7 | alpha_tag = a; 8 | description = d; 9 | tag = t; 10 | group = g; 11 | priority = p; 12 | active = false; 13 | } 14 | 15 | std::string Talkgroup::menu_string() { 16 | char buff[150]; 17 | //std::ostringstream oss; 18 | 19 | sprintf(buff, "%5lu - %-15s %-20s %-15s %-40s", number, alpha_tag.c_str(), tag.c_str(), group.c_str(), description.c_str()); 20 | //sprintf(buff, "%5lu - %s", number, alpha_tag.c_str()); 21 | 22 | std::string buffAsStdStr = buff; 23 | 24 | return buffAsStdStr; 25 | } 26 | 27 | int Talkgroup::get_priority() { 28 | return priority; 29 | } 30 | 31 | bool Talkgroup::is_active() { 32 | 33 | return active; 34 | } 35 | 36 | void Talkgroup::set_active(bool a) { 37 | 38 | active = a; 39 | } 40 | -------------------------------------------------------------------------------- /talkgroup.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | //#include 4 | 5 | class Talkgroup { 6 | public: 7 | long number; 8 | char mode; 9 | std::string alpha_tag; 10 | std::string description; 11 | std::string tag; 12 | std::string group; 13 | int priority; 14 | Talkgroup(long num, char m, std::string a, std::string d, std::string t, std::string g, int p); 15 | bool is_active(); 16 | int get_priority(); 17 | void set_active(bool a); 18 | std::string menu_string(); 19 | private: 20 | bool active; 21 | 22 | }; 23 | --------------------------------------------------------------------------------