Repository: pinkavaj/rtl-sdr Branch: master Commit: cd36c2881559 Files: 117 Total size: 917.8 KB Directory structure: gitextract_nuacun34/ ├── .github/ │ └── workflows/ │ └── c-cpp.yml ├── .gitignore ├── .travis.yml ├── AUTHORS ├── CMakeLists.txt ├── COPYING ├── Doxyfile.in ├── Makefile.am ├── README.md ├── README.rtlfm_cmdfile ├── README.rtlsdr_rpc ├── README_improvements.md ├── cmake/ │ ├── Modules/ │ │ ├── FindLibUSB.cmake │ │ ├── FindThreads.cmake │ │ └── Version.cmake │ └── cmake_uninstall.cmake.in ├── configure.ac ├── contrib/ │ └── jenkins.sh ├── cross_build_mingw32.sh ├── cross_build_mingw64.sh ├── debian/ │ ├── .gitignore │ ├── README.Debian │ ├── changelog │ ├── compat │ ├── control │ ├── copyright │ ├── copyright-scan-patterns.yml │ ├── debianize_armhf │ ├── debianize_x32 │ ├── debianize_x64 │ ├── heatmap.py │ ├── librtlsdr-dev.dirs │ ├── librtlsdr-dev.install │ ├── librtlsdr0.dirs │ ├── librtlsdr0.install │ ├── librtlsdr0.maintscript │ ├── librtlsdr0.metainfo.xml │ ├── librtlsdr0.udev │ ├── rtl-sdr-blacklist.conf │ ├── rtl-sdr.dirs │ ├── rtl-sdr.examples │ ├── rtl-sdr.install │ ├── rtl-sdr.manpages │ ├── rtl_adsb.1 │ ├── rtl_eeprom.1 │ ├── rtl_fm.1 │ ├── rtl_power.1 │ ├── rtl_sdr.1 │ ├── rtl_tcp.1 │ ├── rtl_test.1 │ ├── rules │ ├── source/ │ │ └── format │ └── watch ├── git-version-gen ├── include/ │ ├── CMakeLists.txt │ ├── Makefile.am │ ├── controlThread.h │ ├── reg_field.h │ ├── rtl-sdr.h │ ├── rtl-sdr_export.h │ ├── rtl_tcp.h │ ├── rtlsdr_i2c.h │ ├── rtlsdr_rpc.h │ ├── rtlsdr_rpc_msg.h │ ├── tuner_e4k.h │ ├── tuner_fc0012.h │ ├── tuner_fc0013.h │ ├── tuner_fc2580.h │ └── tuner_r82xx.h ├── install-blacklist.sh ├── librtlsdr.pc.in ├── m4/ │ └── .gitignore ├── mingw-w32-i686.cmake ├── mingw-w64-x64_64.cmake ├── protocol_rtl_tcp.txt ├── rtl-sdr.rules ├── rtlsdr-blacklist.conf ├── src/ │ ├── CMakeLists.txt │ ├── Makefile.am │ ├── controlThread.c │ ├── convenience/ │ │ ├── convenience.c │ │ ├── convenience.h │ │ ├── rtl_convenience.c │ │ ├── rtl_convenience.h │ │ ├── wavehdr.h │ │ ├── waveread.c │ │ ├── waveread.h │ │ ├── wavewrite.c │ │ └── wavewrite.h │ ├── getopt/ │ │ ├── getopt.c │ │ └── getopt.h │ ├── librtlsdr.c │ ├── rtl_adsb.c │ ├── rtl_app_ver.h.in │ ├── rtl_biast.c │ ├── rtl_eeprom.c │ ├── rtl_fm.c │ ├── rtl_ir.c │ ├── rtl_power.c │ ├── rtl_raw2wav.c │ ├── rtl_rpcd.c │ ├── rtl_sdr.c │ ├── rtl_tcp.c │ ├── rtl_test.c │ ├── rtl_udp.c │ ├── rtl_wavestat.c │ ├── rtl_wavestream.c │ ├── rtlsdr.rc.in │ ├── rtlsdr_rpc.c │ ├── rtlsdr_rpc_msg.c │ ├── tuner_e4k.c │ ├── tuner_fc0012.c │ ├── tuner_fc0013.c │ ├── tuner_fc2580.c │ └── tuner_r82xx.c └── win32-qtcreator/ ├── CMakeLists.txt └── README.txt ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/c-cpp.yml ================================================ name: C/C++ CI on: push: branches: [ master, development ] pull_request: branches: [ master, development ] jobs: build_ubuntu-amd64_latest: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: prerequisites # pre-installed on ubuntu-1804: build-essential, git 2.28.0, cmake 3.10/3.17, make # pre-installed: clang 6.0 / 8 / 9, gcc/++ 7.5.0/8.4.0/9.3.0 run: sudo apt -qq update && sudo apt -yqq install libusb-1.0-0-dev - name: cmake_make run: mkdir build && cmake -S . -B build && cd build && make - name: compress run: tar zcvf librtlsdr_build_ubuntu-amd64_latest.tar.gz --directory=build/src --exclude=CMakeFiles --exclude=*.cmake --exclude=Makefile --exclude=rtl_app_ver.h . - name: 'Upload Artifact' uses: actions/upload-artifact@v2 with: name: ubuntu_latest_build path: librtlsdr_build_ubuntu-amd64_latest.tar.gz build_macos_latest: runs-on: macos-latest steps: - uses: actions/checkout@v2 #- name: prerequisites # pre-installed on macos-10.15: git 2.28.0, cmake 3.18.2, libusb 1.0.23 # pre-installed: clang/LLVM 10.0.1, gcc/++ 8.4.0/9.3.0 # run: brew install libusb - name: cmake_make run: mkdir build && cmake -S . -B build && cd build && make - name: compress run: tar zcvf librtlsdr_build_macos-latest.tar.gz --directory=build/src --exclude=CMakeFiles --exclude=*.cmake --exclude=Makefile --exclude=rtl_app_ver.h . - name: 'Upload Artifact' uses: actions/upload-artifact@v2 with: name: macos_latest_build path: librtlsdr_build_macos-latest.tar.gz cross_build_win32_win64_latest: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - name: prerequisites run: sudo apt -qq update && sudo apt -yqq install gcc-mingw-w64 - name: build_w32_static run: bash ./cross_build_mingw32.sh static -DLINK_RTLTOOLS_AGAINST_STATIC_LIB=ON - name: build_w32_static_udpsrv run: bash ./cross_build_mingw32.sh static_udpsrv -DLINK_RTLTOOLS_AGAINST_STATIC_LIB=ON -DPROVIDE_UDP_SERVER=ON - name: build_w32_dlldep run: bash ./cross_build_mingw32.sh dlldep - name: build_w32_dlldep_udpsrv run: bash ./cross_build_mingw32.sh dlldep_udpsrv -DPROVIDE_UDP_SERVER=ON - name: build_w64_static run: bash ./cross_build_mingw64.sh static -DLINK_RTLTOOLS_AGAINST_STATIC_LIB=ON - name: build_w64_static_udpsrv run: bash ./cross_build_mingw64.sh static_udpsrv -DLINK_RTLTOOLS_AGAINST_STATIC_LIB=ON -DPROVIDE_UDP_SERVER=ON - name: build_w64_dlldep run: bash ./cross_build_mingw64.sh dlldep - name: build_w64_dlldep_udpsrv run: bash ./cross_build_mingw64.sh dlldep_udpsrv -DPROVIDE_UDP_SERVER=ON - name: 'upload w32 static artifact' uses: actions/upload-artifact@v2 with: name: rtlsdr-bin-w32_static path: rtlsdr-bin-w32_static/bin/ - name: 'upload w32 static_udpsrv artifact' uses: actions/upload-artifact@v2 with: name: rtlsdr-bin-w32_static_udpsrv path: rtlsdr-bin-w32_static_udpsrv/bin/ - name: 'upload w32 dlldep artifact' uses: actions/upload-artifact@v2 with: name: rtlsdr-bin-w32_dlldep path: rtlsdr-bin-w32_dlldep/bin/ - name: 'upload w32 dlldep_udpsrv artifact' uses: actions/upload-artifact@v2 with: name: rtlsdr-bin-w32_dlldep_udpsrv path: rtlsdr-bin-w32_dlldep_udpsrv/bin/ - name: 'upload w64 static artifact' uses: actions/upload-artifact@v2 with: name: rtlsdr-bin-w64_static path: rtlsdr-bin-w64_static/bin/ - name: 'upload w64 static_udpsrv artifact' uses: actions/upload-artifact@v2 with: name: rtlsdr-bin-w64_static_udpsrv path: rtlsdr-bin-w64_static_udpsrv/bin/ - name: 'upload w64 dlldep artifact' uses: actions/upload-artifact@v2 with: name: rtlsdr-bin-w64_dlldep path: rtlsdr-bin-w64_dlldep/bin/ - name: 'upload w64 dlldep_udpsrv artifact' uses: actions/upload-artifact@v2 with: name: rtlsdr-bin-w64_dlldep_udpsrv path: rtlsdr-bin-w64_dlldep_udpsrv/bin/ ================================================ FILE: .gitignore ================================================ Makefile Makefile.in .deps .libs *.o *.lo *.la *.pc aclocal.m4 acinclude.m4 aminclude.am m4/*.m4 autom4te.cache config.h* config.sub config.log config.status config.guess configure depcomp missing ltmain.sh install-sh stamp-h1 libtool Doxyfile .tarball-version .version .*.swp doc/ src/rtl_sdr src/rtl_tcp CMakeCache.txt */CMakeFiles CMakeFiles build **/*.o **/*.so* **/*.a src/rtl_adsb src/rtl_eeprom src/rtl_fm src/rtl_ir src/rtl_power src/rtl_rpcd src/rtl_test debianize/*.deb .cproject .project ================================================ FILE: .travis.yml ================================================ language: c compiler: - gcc - clang before_install: - sudo apt-get update -qq - sudo apt-get install -qq libusb-1.0-0-dev script: cmake . && make && sudo make install ================================================ FILE: AUTHORS ================================================ Steve Markgraf Dimitri Stolnikov Hoernchen Kyle Keen ================================================ FILE: CMakeLists.txt ================================================ # Copyright 2012 OSMOCOM Project # # This file is part of rtl-sdr # # GNU Radio is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3, or (at your option) # any later version. # # GNU Radio is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Radio; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. ######################################################################## # Project setup ######################################################################## cmake_minimum_required(VERSION 2.6...3.19) project(rtlsdr C) # quite old cmake version - probably for compatibility with old OS versions? # see https://cmake.org/cmake/help/cmake2.6docs.html #select the release build type by default to get optimization flags if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") message(STATUS "Build type not specified: defaulting to release.") endif(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "") list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules) if(NOT LIB_INSTALL_DIR) set(LIB_INSTALL_DIR lib) endif() OPTION(LINK_RTLTOOLS_AGAINST_STATIC_LIB "Link rtl-tools statically against librtlsdr" OFF) OPTION(PROVIDE_UDP_SERVER "Provide UDP server for tests" OFF) OPTION(WITH_RPC "RPC for non-Windows" OFF) # Set the version information here set(VERSION_INFO_MAJOR_VERSION 0) # increment major on api compatibility changes set(VERSION_INFO_MINOR_VERSION 9) # increment minor on feature-level changes set(VERSION_INFO_PATCH_VERSION git) # increment patch for bug fixes and docs include(Version) # setup version info ######################################################################## # Version defines to include in library and tools ######################################################################## configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/src/rtl_app_ver.h.in ${CMAKE_CURRENT_BINARY_DIR}/src/rtl_app_ver.h @ONLY ) if(PROVIDE_UDP_SERVER) add_definitions(-DWITH_UDP_SERVER) endif() ######################################################################## # Compiler specific setup ######################################################################## if(CMAKE_COMPILER_IS_GNUCC AND NOT WIN32) ADD_DEFINITIONS(-Wall) ADD_DEFINITIONS(-Wextra) ADD_DEFINITIONS(-Wno-unused-parameter) ADD_DEFINITIONS(-Wno-unused) ADD_DEFINITIONS(-Wsign-compare) ADD_DEFINITIONS(-Wdeclaration-after-statement) #http://gcc.gnu.org/wiki/Visibility add_definitions(-fvisibility=hidden) elseif(MSVC14 OR MSVC14) #pthread-w32 issue, timespec is now part of time.h ADD_DEFINITIONS(-D_TIMESPEC_DEFINED) endif() if (APPLE) set(CMAKE_MACOSX_RPATH ON) endif (APPLE) OPTION(RTL_STATIC_BUILD "Build rtl-tools static (except RTLSDR.DLL) on MinGW/Win32" ON) if(RTL_STATIC_BUILD) if (WIN32) if(MINGW) # Special MINGW stuff here # see https://cmake.org/pipermail/cmake/2012-September/051970.html # see http://stackoverflow.com/questions/13768515/how-to-do-static-linking-of-libwinpthread-1-dll-in-mingw set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static-libgcc -static") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -static-libstdc++") set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -static-libgcc -s") set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS} -static-libgcc -static-libstdc++ -s") endif() endif() endif() ######################################################################### # Bug Fix ######################################################################### OPTION(NEED_PTHREADS_WORKARROUND "PThreads Workarround for timespec") IF (NEED_PTHREADS_WORKARROUND) ADD_DEFINITIONS(-DNEED_PTHREADS_WORKARROUND) ENDIF() ######################################################################## # Find build dependencies ######################################################################## find_package(PkgConfig) find_package(LibUSB) cmake_policy(SET CMP0075 NEW) if(WIN32 AND NOT MINGW) set(THREADS_USE_PTHREADS_WIN32 true) endif() find_package(Threads) if(NOT LIBUSB_FOUND) message(FATAL_ERROR "LibUSB 1.0 required to compile rtl-sdr") endif() if(NOT THREADS_FOUND) message(FATAL_ERROR "pthreads(-win32) required to compile rtl-sdr") endif() ######################################################################## # Setup the include and linker paths ######################################################################## include_directories( ${PROJECT_SOURCE_DIR}/include ${LIBUSB_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/src ${THREADS_PTHREADS_INCLUDE_DIR} ) #link_directories( # ... #) # Set component parameters #set(INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include CACHE INTERNAL "" FORCE) ######################################################################## # Create uninstall target ######################################################################## if(NOT TARGET uninstall) configure_file( ${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake @ONLY ) add_custom_target(uninstall ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake ) endif() ######################################################################## # Install udev rules ######################################################################## option(INSTALL_UDEV_RULES "Install udev rules for RTL-SDR" OFF) if (INSTALL_UDEV_RULES) install ( FILES rtl-sdr.rules DESTINATION "/etc/udev/rules.d" COMPONENT "udev" ) else (INSTALL_UDEV_RULES) message (STATUS "Udev rules not being installed, install them with -DINSTALL_UDEV_RULES=ON") endif (INSTALL_UDEV_RULES) option(DETACH_KERNEL_DRIVER "Detach kernel driver if loaded" OFF) if (DETACH_KERNEL_DRIVER) message (STATUS "Building with kernel driver detaching enabled") add_definitions(-DDETACH_KERNEL_DRIVER=1) else (DETACH_KERNEL_DRIVER) message (STATUS "Building with kernel driver detaching disabled, use -DDETACH_KERNEL_DRIVER=ON to enable") endif (DETACH_KERNEL_DRIVER) ######################################################################## # Add subdirectories ######################################################################## add_subdirectory(include) add_subdirectory(src) ######################################################################## # Create Pkg Config File ######################################################################## FOREACH(inc ${LIBUSB_INCLUDE_DIR}) LIST(APPEND RTLSDR_PC_CFLAGS "-I${inc}") ENDFOREACH(inc) FOREACH(lib ${LIBUSB_LIBRARY_DIRS}) LIST(APPEND RTLSDR_PC_LIBS "-L${lib}") ENDFOREACH(lib) # use space-separation format for the pc file STRING(REPLACE ";" " " RTLSDR_PC_CFLAGS "${RTLSDR_PC_CFLAGS}") STRING(REPLACE ";" " " RTLSDR_PC_LIBS "${RTLSDR_PC_LIBS}") # unset these vars to avoid hard-coded paths to cross environment IF(CMAKE_CROSSCOMPILING) UNSET(RTLSDR_PC_CFLAGS) UNSET(RTLSDR_PC_LIBS) ENDIF(CMAKE_CROSSCOMPILING) set(prefix ${CMAKE_INSTALL_PREFIX}) set(exec_prefix \${prefix}) set(libdir \${exec_prefix}/${LIB_INSTALL_DIR}) set(includedir \${prefix}/include) CONFIGURE_FILE( ${CMAKE_CURRENT_SOURCE_DIR}/librtlsdr.pc.in ${CMAKE_CURRENT_BINARY_DIR}/librtlsdr.pc @ONLY) INSTALL( FILES ${CMAKE_CURRENT_BINARY_DIR}/librtlsdr.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig ) ######################################################################## # Print Summary ######################################################################## MESSAGE(STATUS "Building for version: ${VERSION} / ${LIBVER}") MESSAGE(STATUS "Using install prefix: ${CMAKE_INSTALL_PREFIX}") ================================================ FILE: COPYING ================================================ GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. ================================================ FILE: Doxyfile.in ================================================ # Doxyfile 1.7.4 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (" "). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # http://www.gnu.org/software/libiconv for the list of possible encodings. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded # by quotes) that should identify the project. PROJECT_NAME = librtlsdr # The PROJECT_NUMBER tag can be used to enter a project or revision number. # This could be handy for archiving the generated documentation or # if some version control system is used. PROJECT_NUMBER = @VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer # a quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = "RTL-SDR library" # With the PROJECT_LOGO tag one can specify an logo or icon that is # included in the documentation. The maximum height of the logo should not # exceed 55 pixels and the maximum width should not exceed 200 pixels. # Doxygen will copy the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) # base path where the generated documentation will be put. # If a relative path is entered, it will be relative to the location # where doxygen was started. If left blank the current directory will be used. OUTPUT_DIRECTORY = doc # If the CREATE_SUBDIRS tag is set to YES, then doxygen will create # 4096 sub-directories (in 2 levels) under the output directory of each output # format and will distribute the generated files over these directories. # Enabling this option can be useful when feeding doxygen a huge amount of # source files, where putting all generated files in the same directory would # otherwise cause performance problems for the file system. CREATE_SUBDIRS = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # The default language is English, other supported languages are: # Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, # Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, # Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English # messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, # Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, # Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will # include brief member descriptions after the members that are listed in # the file and class documentation (similar to JavaDoc). # Set to NO to disable this. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend # the brief description of a member or function before the detailed description. # Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator # that is used to form the text in various listings. Each string # in this list, if found as the leading text of the brief description, will be # stripped from the text and the result after processing the whole list, is # used as the annotated text. Otherwise, the brief description is used as-is. # If left blank, the following values are used ("$name" is automatically # replaced with the name of the entity): "The $name class" "The $name widget" # "The $name file" "is" "provides" "specifies" "contains" # "represents" "a" "an" "the" ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # Doxygen will generate a detailed section even if there is only a brief # description. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full # path before files name in the file list and in the header files. If set # to NO the shortest path that makes the file name unique will be used. FULL_PATH_NAMES = YES # If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag # can be used to strip a user-defined part of the path. Stripping is # only done if one of the specified strings matches the left-hand part of # the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the # path to strip. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of # the path mentioned in the documentation of a class, which tells # the reader which header file to include in order to use a class. # If left blank only the name of the header file containing the class # definition is used. Otherwise one should specify the include paths that # are normally passed to the compiler using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter # (but less readable) file names. This can be useful if your file system # doesn't support long names like on DOS, Mac, or CD-ROM. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen # will interpret the first line (until the first dot) of a JavaDoc-style # comment as the brief description. If set to NO, the JavaDoc # comments will behave just like regular Qt-style comments # (thus requiring an explicit @brief command for a brief description.) JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then Doxygen will # interpret the first line (until the first dot) of a Qt-style # comment as the brief description. If set to NO, the comments # will behave just like regular Qt-style comments (thus requiring # an explicit \brief command for a brief description.) QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen # treat a multi-line C++ special comment block (i.e. a block of //! or /// # comments) as a brief description. This used to be the default behaviour. # The new default is to treat a multi-line C++ comment block as a detailed # description. Set this tag to YES if you prefer the old behaviour instead. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES (the default) then an undocumented # member inherits the documentation from any documented member that it # re-implements. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce # a new page for each member. If set to NO, the documentation of a member will # be part of the file/class/namespace that contains it. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. TAB_SIZE = 8 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". # For example adding "sideeffect=\par Side Effects:\n" will allow you to # put the command \sideeffect (or @sideeffect) in the documentation, which # will result in a user-defined paragraph with heading "Side Effects:". # You can put \n's in the value part of an alias to insert newlines. ALIASES = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C # sources only. Doxygen will then generate output that is more tailored for C. # For instance, some of the names that are used will be different. The list # of all members will be omitted, etc. OPTIMIZE_OUTPUT_FOR_C = YES # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java # sources only. Doxygen will then generate output that is more tailored for # Java. For instance, namespaces will be presented as packages, qualified # scopes will look different, etc. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources only. Doxygen will then generate output that is more tailored for # Fortran. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for # VHDL. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given extension. # Doxygen has a built-in mapping, but you can override or extend it using this # tag. The format is ext=language, where ext is a file extension, and language # is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, # C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make # doxygen treat .inc files as Fortran files (default is PHP), and .f files as C # (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions # you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. EXTENSION_MAPPING = # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should # set this tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); v.s. # func(std::string) {}). This also makes the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. # Doxygen will parse them like normal C++ but will assume all classes use public # instead of private inheritance when no explicit protection keyword is present. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate getter # and setter methods for a property. Setting this option to YES (the default) # will make doxygen replace the get and set methods by a property in the # documentation. This will only work if the methods are indeed getting or # setting a simple type. If this is not the case, or you want to show the # methods anyway, you should set this option to NO. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES, then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. DISTRIBUTE_GROUP_DOC = NO # Set the SUBGROUPING tag to YES (the default) to allow class member groups of # the same type (for instance a group of public functions) to be put as a # subgroup of that type (e.g. under the Public Functions section). Set it to # NO to prevent subgrouping. Alternatively, this can be done per class using # the \nosubgrouping command. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and # unions are shown inside the group in which they are included (e.g. using # @ingroup) instead of on a separate page (for HTML and Man pages) or # section (for LaTeX and RTF). INLINE_GROUPED_CLASSES = NO # When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum # is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically # be useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. TYPEDEF_HIDES_STRUCT = NO # The SYMBOL_CACHE_SIZE determines the size of the internal cache use to # determine which symbols to keep in memory and which to flush to disk. # When the cache is full, less often used symbols will be written to disk. # For small to medium size projects (<1000 input files) the default value is # probably good enough. For larger projects a too small cache size can cause # doxygen to be busy swapping symbols to and from disk most of the time # causing a significant performance penalty. # If the system has enough physical memory increasing the cache will improve the # performance by keeping more symbols in memory. Note that the value works on # a logarithmic scale so increasing the size by one will roughly double the # memory usage. The cache size is given by this formula: # 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, # corresponding to a cache size of 2^16 = 65536 symbols SYMBOL_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in # documentation are documented, even if no documentation was available. # Private class members and static file members will be hidden unless # the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES all private members of a class # will be included in the documentation. EXTRACT_PRIVATE = NO # If the EXTRACT_STATIC tag is set to YES all static members of a file # will be included in the documentation. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) # defined locally in source files will be included in the documentation. # If set to NO only classes defined in header files are included. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. When set to YES local # methods, which are defined in the implementation section but not in # the interface are included in the documentation. # If set to NO (the default) only methods in the interface are included. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base # name of the file that contains the anonymous namespace. By default # anonymous namespaces are hidden. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all # undocumented members of documented classes, files or namespaces. # If set to NO (the default) these members will be included in the # various overviews, but no documentation section is generated. # This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. # If set to NO (the default) these classes will be included in the various # overviews. This option has no effect if EXTRACT_ALL is enabled. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all # friend (class|struct|union) declarations. # If set to NO (the default) these declarations will be included in the # documentation. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any # documentation blocks found inside the body of a function. # If set to NO (the default) these blocks will be appended to the # function's detailed documentation block. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation # that is typed after a \internal command is included. If the tag is set # to NO (the default) then the documentation will be excluded. # Set it to YES to include the internal documentation. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate # file names in lower-case letters. If set to YES upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen # will show members with their full class and namespace scopes in the # documentation. If set to YES the scope will be hidden. HIDE_SCOPE_NAMES = NO # If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen # will put a list of the files that are included by a file in the documentation # of that file. SHOW_INCLUDE_FILES = YES # If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen # will list include files with double quotes in the documentation # rather than with sharp brackets. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES (the default) then a tag [inline] # is inserted in the documentation for inline members. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen # will sort the (detailed) documentation of file and class members # alphabetically by member name. If set to NO the members will appear in # declaration order. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the # brief documentation of file, namespace and class members alphabetically # by member name. If set to NO (the default) the members will appear in # declaration order. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen # will sort the (brief and detailed) documentation of class members so that # constructors and destructors are listed first. If set to NO (the default) # the constructors will appear in the respective orders defined by # SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. # This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO # and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the # hierarchy of group names into alphabetical order. If set to NO (the default) # the group names will appear in their defined order. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be # sorted by fully-qualified names, including namespaces. If set to # NO (the default), the class list will be sorted only by class name, # not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the # alphabetical list. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to # do proper type resolution of all parameters of a function it will reject a # match between the prototype and the implementation of a member function even # if there is only one candidate or it is obvious which candidate to choose # by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen # will still accept a match between prototype and implementation in such cases. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or # disable (NO) the todo list. This list is created by putting \todo # commands in the documentation. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or # disable (NO) the test list. This list is created by putting \test # commands in the documentation. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or # disable (NO) the bug list. This list is created by putting \bug # commands in the documentation. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or # disable (NO) the deprecated list. This list is created by putting # \deprecated commands in the documentation. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional # documentation sections, marked by \if sectionname ... \endif. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines # the initial value of a variable or macro consists of for it to appear in # the documentation. If the initializer consists of more lines than specified # here it will be hidden. Use a value of 0 to hide initializers completely. # The appearance of the initializer of individual variables and macros in the # documentation can be controlled using \showinitializer or \hideinitializer # command in the documentation regardless of this setting. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated # at the bottom of the documentation of classes and structs. If set to YES the # list will mention the files that were used to generate the documentation. SHOW_USED_FILES = YES # If the sources in your project are distributed over multiple directories # then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy # in the documentation. The default is NO. SHOW_DIRECTORIES = NO # Set the SHOW_FILES tag to NO to disable the generation of the Files page. # This will remove the Files entry from the Quick Index and from the # Folder Tree View (if specified). The default is YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the # Namespaces page. # This will remove the Namespaces entry from the Quick Index # and from the Folder Tree View (if specified). The default is YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command , where is the value of # the FILE_VERSION_FILTER tag, and is the name of an input file # provided by doxygen. Whatever the program writes to standard output # is used as the file version. See the manual for examples. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. The create the layout file # that represents doxygen's defaults, run doxygen with the -l option. # You can optionally specify a file name after the option, if omitted # DoxygenLayout.xml will be used as the name of the layout file. LAYOUT_FILE = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated # by doxygen. Possible values are YES and NO. If left blank NO is used. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated by doxygen. Possible values are YES and NO. If left blank # NO is used. WARNINGS = YES # If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings # for undocumented members. If EXTRACT_ALL is set to YES then this flag will # automatically be disabled. WARN_IF_UNDOCUMENTED = YES # If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some # parameters in a documented function, or documenting parameters that # don't exist or using markup commands wrongly. WARN_IF_DOC_ERROR = YES # The WARN_NO_PARAMDOC option can be enabled to get warnings for # functions that are documented, but have no documentation for their parameters # or return value. If set to NO (the default) doxygen will only warn about # wrong or incomplete parameter documentation, but not about the absence of # documentation. WARN_NO_PARAMDOC = NO # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text # tags, which will be replaced by the file and line number from which the # warning originated and the warning text. Optionally the format may contain # $version, which will be replaced by the version of the file (if it could # be obtained via FILE_VERSION_FILTER) WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning # and error messages should be written. If left blank the output is written # to stderr. WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag can be used to specify the files and/or directories that contain # documented source files. You may enter file names like "myfile.cpp" or # directories like "/usr/src/myproject". Separate the files or directories # with spaces. INPUT = include src # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # also the default input encoding. Doxygen uses libiconv (or the iconv built # into libc) for the transcoding. See http://www.gnu.org/software/libiconv for # the list of possible encodings. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank the following patterns are tested: # *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh # *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py # *.f90 *.f *.for *.vhd *.vhdl FILE_PATTERNS = # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. # If left blank NO is used. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. Note that the wildcards are matched # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or # directories that contain example code fragments that are included (see # the \include command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp # and *.h) to filter out the source-files in the directories. If left # blank all files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude # commands irrespective of the value of the RECURSIVE tag. # Possible values are YES and NO. If left blank NO is used. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or # directories that contain image that are included in the documentation (see # the \image command). IMAGE_PATH = images/ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command , where # is the value of the INPUT_FILTER tag, and is the name of an # input file. Doxygen will then use the output that the filter program writes # to standard output. # If FILTER_PATTERNS is specified, this tag will be # ignored. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. # Doxygen will compare the file name with each pattern and apply the # filter if there is a match. # The filters are a list of the form: # pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further # info on how filters are used. If FILTER_PATTERNS is empty or if # non of the patterns match the file name, INPUT_FILTER is applied. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will be used to filter the input files when producing source # files to browse (i.e. when SOURCE_BROWSER is set to YES). FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) # and it is also possible to disable source filtering for a specific pattern # using *.ext= (so without naming a filter). This option only has effect when # FILTER_SOURCE_FILES is enabled. FILTER_SOURCE_PATTERNS = #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will # be generated. Documented entities will be cross-referenced with these sources. # Note: To get rid of all source code in the generated output, make sure also # VERBATIM_HEADERS is set to NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body # of functions and classes directly in the documentation. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct # doxygen to hide any special comment blocks from generated source code # fragments. Normal C and C++ comments will always remain visible. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES # then for each documented function all documented # functions referencing it will be listed. REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES # then for each documented function all documented entities # called/used by that function will be listed. REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES (the default) # and SOURCE_BROWSER tag is set to YES, then the hyperlinks from # functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will # link to the source code. # Otherwise they will link to the documentation. REFERENCES_LINK_SOURCE = YES # If the USE_HTAGS tag is set to YES then the references to source code # will point to the HTML generated by the htags(1) tool instead of doxygen # built-in source browser. The htags tool is part of GNU's global source # tagging system (see http://www.gnu.org/software/global/global.html). You # will need version 4.8.6 or higher. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen # will generate a verbatim copy of the header file for each class for # which an include is specified. Set to NO to disable this. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index # of all compounds will be generated. Enable this if the project # contains a lot of classes, structs, unions or interfaces. ALPHABETICAL_INDEX = NO # If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then # the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns # in which this list will be split (can be a number in the range [1..20]) COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all # classes will be put under the same header in the alphabetical index. # The IGNORE_PREFIX tag can be used to specify one or more prefixes that # should be ignored while generating the index headers. IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `html' will be used as the default path. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for # each generated HTML page (for example: .htm,.php,.asp). If it is left blank # doxygen will generate files with .html extension. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a personal HTML header for # each generated HTML page. If it is left blank doxygen will generate a # standard header. Note that when using a custom header you are responsible # for the proper inclusion of any scripts and style sheets that doxygen # needs, which is dependent on the configuration options used. # It is adviced to generate a default header using "doxygen -w html # header.html footer.html stylesheet.css YourConfigFile" and then modify # that header. Note that the header is subject to change so you typically # have to redo this when upgrading to a newer version of doxygen or when changing the value of configuration settings such as GENERATE_TREEVIEW! HTML_HEADER = # The HTML_FOOTER tag can be used to specify a personal HTML footer for # each generated HTML page. If it is left blank doxygen will generate a # standard footer. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading # style sheet that is used by each HTML page. It can be used to # fine-tune the look of the HTML output. If the tag is left blank doxygen # will generate a default style sheet. Note that doxygen will try to copy # the style sheet file to the HTML output directory, so don't put your own # stylesheet in the HTML output directory as well, or it will be erased! HTML_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that # the files will be copied as-is; there are no commands or markers available. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. # Doxygen will adjust the colors in the stylesheet and background images # according to this color. Hue is specified as an angle on a colorwheel, # see http://en.wikipedia.org/wiki/Hue for more information. # For instance the value 0 represents red, 60 is yellow, 120 is green, # 180 is cyan, 240 is blue, 300 purple, and 360 is red again. # The allowed range is 0 to 359. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of # the colors in the HTML output. For a value of 0 the output will use # grayscales only. A value of 255 will produce the most vivid colors. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to # the luminance component of the colors in the HTML output. Values below # 100 gradually make the output lighter, whereas values above 100 make # the output darker. The value divided by 100 is the actual gamma applied, # so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, # and 100 does not change the gamma. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting # this to NO can help when comparing the output of multiple runs. HTML_TIMESTAMP = YES # If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, # files or namespaces will be aligned in HTML using tables. If set to # NO a bullet list will be used. HTML_ALIGN_MEMBERS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. For this to work a browser that supports # JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox # Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). HTML_DYNAMIC_SECTIONS = NO # If the GENERATE_DOCSET tag is set to YES, additional index files # will be generated that can be used as input for Apple's Xcode 3 # integrated development environment, introduced with OSX 10.5 (Leopard). # To create a documentation set, doxygen will generate a Makefile in the # HTML output directory. Running make will produce the docset in that # directory and running "make install" will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find # it at startup. # See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. GENERATE_DOCSET = NO # When GENERATE_DOCSET tag is set to YES, this tag determines the name of the # feed. A documentation feed provides an umbrella under which multiple # documentation sets from a single provider (such as a company or product suite) # can be grouped. DOCSET_FEEDNAME = "Doxygen generated docs" # When GENERATE_DOCSET tag is set to YES, this tag specifies a string that # should uniquely identify the documentation set bundle. This should be a # reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen # will append .docset to the name. DOCSET_BUNDLE_ID = org.doxygen.Project # When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES, additional index files # will be generated that can be used as input for tools like the # Microsoft HTML help workshop to generate a compiled HTML help file (.chm) # of the generated HTML documentation. GENERATE_HTMLHELP = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can # be used to specify the file name of the resulting .chm file. You # can add a path in front of the file if the result should not be # written to the html output directory. CHM_FILE = # If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can # be used to specify the location (absolute path including file name) of # the HTML help compiler (hhc.exe). If non-empty doxygen will try to run # the HTML help compiler on the generated index.hhp. HHC_LOCATION = # If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag # controls if a separate .chi index file is generated (YES) or that # it should be included in the master .chm file (NO). GENERATE_CHI = NO # If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING # is used to encode HtmlHelp index (hhk), content (hhc) and project file # content. CHM_INDEX_ENCODING = # If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag # controls whether a binary table of contents is generated (YES) or a # normal table of contents (NO) in the .chm file. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members # to the contents of the HTML help documentation and to the tree view. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated # that can be used as input for Qt's qhelpgenerator to generate a # Qt Compressed Help (.qch) of the generated HTML documentation. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can # be used to specify the file name of the resulting .qch file. # The path specified is relative to the HTML output folder. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#namespace QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating # Qt Help Project output. For more information please see # http://doc.trolltech.com/qthelpproject.html#virtual-folders QHP_VIRTUAL_FOLDER = doc # If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to # add. For more information please see # http://doc.trolltech.com/qthelpproject.html#custom-filters QHP_CUST_FILTER_NAME = # The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see # # Qt Help Project / Custom Filters. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's # filter section matches. # # Qt Help Project / Filter Attributes. QHP_SECT_FILTER_ATTRS = # If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can # be used to specify the location of Qt's qhelpgenerator. # If non-empty doxygen will try to run qhelpgenerator on the generated # .qhp file. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files # will be generated, which together with the HTML files, form an Eclipse help # plugin. To install this plugin and make it available under the help contents # menu in Eclipse, the contents of the directory containing the HTML and XML # files needs to be copied into the plugins directory of eclipse. The name of # the directory within the plugins directory should be the same as # the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before # the help appears. GENERATE_ECLIPSEHELP = NO # A unique identifier for the eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have # this name. ECLIPSE_DOC_ID = org.doxygen.Project # The DISABLE_INDEX tag can be used to turn on/off the condensed index at # top of each HTML page. The value NO (the default) enables the index and # the value YES disables it. DISABLE_INDEX = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values # (range [0,1..20]) that doxygen will group on one line in the generated HTML # documentation. Note that a value of 0 will completely suppress the enum # values from appearing in the overview section. ENUM_VALUES_PER_LINE = 4 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. # If the tag value is set to YES, a side panel will be generated # containing a tree-like index structure (just like the one that # is generated for HTML Help). For this to work a browser that supports # JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). # Windows users are probably better off using the HTML help feature. GENERATE_TREEVIEW = YES # By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, # and Class Hierarchy pages using a tree view instead of an ordered list. USE_INLINE_TREES = NO # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be # used to set the initial width (in pixels) of the frame in which the tree # is shown. TREEVIEW_WIDTH = 250 # When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open # links to external symbols imported via tag files in a separate window. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of Latex formulas included # as images in the HTML documentation. The default is 10. Note that # when you change the font size after a successful doxygen run you need # to manually remove any form_*.png images from the HTML output directory # to force them to be regenerated. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are # not supported properly for IE 6.0, but are supported on all modern browsers. # Note that when changing this option you need to delete any form_*.png files # in the HTML output before the changes have effect. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax # (see http://www.mathjax.org) which uses client side Javascript for the # rendering instead of using prerendered bitmaps. Use this if you do not # have LaTeX installed or if you want to formulas look prettier in the HTML # output. When enabled you also need to install MathJax separately and # configure the path to it using the MATHJAX_RELPATH option. USE_MATHJAX = NO # When MathJax is enabled you need to specify the location relative to the # HTML output directory using the MATHJAX_RELPATH option. The destination # directory should contain the MathJax.js script. For instance, if the mathjax # directory is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the # mathjax.org site, so you can quickly see the result without installing # MathJax, but it is strongly recommended to install a local copy of MathJax # before deployment. MATHJAX_RELPATH = http://www.mathjax.org/mathjax # When the SEARCHENGINE tag is enabled doxygen will generate a search box # for the HTML output. The underlying search engine uses javascript # and DHTML and should work on any modern browser. Note that when using # HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets # (GENERATE_DOCSET) there is already a search function so this one should # typically be disabled. For large projects the javascript based search engine # can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. SEARCHENGINE = NO # When the SERVER_BASED_SEARCH tag is enabled the search engine will be # implemented using a PHP enabled web server instead of at the web client # using Javascript. Doxygen will generate the search PHP script and index # file to put on the web server. The advantage of the server # based approach is that it scales better to large projects and allows # full text search. The disadvantages are that it is more difficult to setup # and does not have live searching capabilities. SERVER_BASED_SEARCH = NO #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- # If the GENERATE_LATEX tag is set to YES (the default) Doxygen will # generate Latex output. GENERATE_LATEX = YES # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `latex' will be used as the default path. LATEX_OUTPUT = latex # The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be # invoked. If left blank `latex' will be used as the default command name. # Note that when enabling USE_PDFLATEX this option is only used for # generating bitmaps for formulas in the HTML output, but not in the # Makefile that is written to the output directory. LATEX_CMD_NAME = latex # The MAKEINDEX_CMD_NAME tag can be used to specify the command name to # generate index for LaTeX. If left blank `makeindex' will be used as the # default command name. MAKEINDEX_CMD_NAME = makeindex # If the COMPACT_LATEX tag is set to YES Doxygen generates more compact # LaTeX documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_LATEX = NO # The PAPER_TYPE tag can be used to set the paper type that is used # by the printer. Possible values are: a4, letter, legal and # executive. If left blank a4wide will be used. PAPER_TYPE = a4wide # The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX # packages that should be included in the LaTeX output. EXTRA_PACKAGES = # The LATEX_HEADER tag can be used to specify a personal LaTeX header for # the generated latex document. The header should contain everything until # the first chapter. If it is left blank doxygen will generate a # standard header. Notice: only use this tag if you know what you are doing! LATEX_HEADER = # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for # the generated latex document. The footer should contain everything after # the last chapter. If it is left blank doxygen will generate a # standard footer. Notice: only use this tag if you know what you are doing! LATEX_FOOTER = # If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated # is prepared for conversion to pdf (using ps2pdf). The pdf file will # contain links (just like the HTML output) instead of page references # This makes the output suitable for online browsing using a pdf viewer. PDF_HYPERLINKS = NO # If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of # plain latex in the generated Makefile. Set this option to YES to get a # higher quality PDF documentation. USE_PDFLATEX = NO # If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. # command to the generated LaTeX files. This will instruct LaTeX to keep # running if errors occur, instead of asking the user for help. # This option is also used when generating formulas in HTML. LATEX_BATCHMODE = NO # If LATEX_HIDE_INDICES is set to YES then doxygen will not # include the index chapters (such as File Index, Compound Index, etc.) # in the output. LATEX_HIDE_INDICES = NO # If LATEX_SOURCE_CODE is set to YES then doxygen will include # source code with syntax highlighting in the LaTeX output. # Note that which sources are shown also depends on other settings # such as SOURCE_BROWSER. LATEX_SOURCE_CODE = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- # If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output # The RTF output is optimized for Word 97 and may not look very pretty with # other RTF readers or editors. GENERATE_RTF = NO # The RTF_OUTPUT tag is used to specify where the RTF docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `rtf' will be used as the default path. RTF_OUTPUT = rtf # If the COMPACT_RTF tag is set to YES Doxygen generates more compact # RTF documents. This may be useful for small projects and may help to # save some trees in general. COMPACT_RTF = NO # If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated # will contain hyperlink fields. The RTF file will # contain links (just like the HTML output) instead of page references. # This makes the output suitable for online browsing using WORD or other # programs which support those fields. # Note: wordpad (write) and others do not support links. RTF_HYPERLINKS = NO # Load stylesheet definitions from file. Syntax is similar to doxygen's # config file, i.e. a series of assignments. You only have to provide # replacements, missing definitions are set to their default value. RTF_STYLESHEET_FILE = # Set optional variables used in the generation of an rtf document. # Syntax is similar to doxygen's config file. RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- # If the GENERATE_MAN tag is set to YES (the default) Doxygen will # generate man pages GENERATE_MAN = NO # The MAN_OUTPUT tag is used to specify where the man pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `man' will be used as the default path. MAN_OUTPUT = man # The MAN_EXTENSION tag determines the extension that is added to # the generated man pages (default is the subroutine's section .3) MAN_EXTENSION = .3 # If the MAN_LINKS tag is set to YES and Doxygen generates man output, # then it will generate one additional man file for each entity # documented in the real man page(s). These additional files # only source the real man page, but without them the man command # would be unable to find the correct page. The default is NO. MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- # If the GENERATE_XML tag is set to YES Doxygen will # generate an XML file that captures the structure of # the code including all documentation. GENERATE_XML = NO # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be # put in front of it. If left blank `xml' will be used as the default path. XML_OUTPUT = xml # The XML_SCHEMA tag can be used to specify an XML schema, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_SCHEMA = # The XML_DTD tag can be used to specify an XML DTD, # which can be used by a validating XML parser to check the # syntax of the XML files. XML_DTD = # If the XML_PROGRAMLISTING tag is set to YES Doxygen will # dump the program listings (including syntax highlighting # and cross-referencing information) to the XML output. Note that # enabling this will significantly increase the size of the XML output. XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- # If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will # generate an AutoGen Definitions (see autogen.sf.net) file # that captures the structure of the code including all # documentation. Note that this feature is still experimental # and incomplete at the moment. GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- # If the GENERATE_PERLMOD tag is set to YES Doxygen will # generate a Perl module file that captures the structure of # the code including all documentation. Note that this # feature is still experimental and incomplete at the # moment. GENERATE_PERLMOD = NO # If the PERLMOD_LATEX tag is set to YES Doxygen will generate # the necessary Makefile rules, Perl scripts and LaTeX code to be able # to generate PDF and DVI output from the Perl module output. PERLMOD_LATEX = NO # If the PERLMOD_PRETTY tag is set to YES the Perl module output will be # nicely formatted so it can be parsed by a human reader. # This is useful # if you want to understand what is going on. # On the other hand, if this # tag is set to NO the size of the Perl module output will be much smaller # and Perl will parse it just the same. PERLMOD_PRETTY = YES # The names of the make variables in the generated doxyrules.make file # are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. # This is useful so different doxyrules.make files included by the same # Makefile don't overwrite each other's variables. PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- # If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will # evaluate all C-preprocessor directives found in the sources and include # files. ENABLE_PREPROCESSING = NO # If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro # names in the source code. If set to NO (the default) only conditional # compilation will be performed. Macro expansion can be done in a controlled # way by setting EXPAND_ONLY_PREDEF to YES. MACRO_EXPANSION = NO # If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES # then the macro expansion is limited to the macros specified with the # PREDEFINED and EXPAND_AS_DEFINED tags. EXPAND_ONLY_PREDEF = NO # If the SEARCH_INCLUDES tag is set to YES (the default) the includes files # pointed to by INCLUDE_PATH will be searched when a #include is found. SEARCH_INCLUDES = YES # The INCLUDE_PATH tag can be used to specify one or more directories that # contain include files that are not input files but should be processed by # the preprocessor. INCLUDE_PATH = # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the # directories. If left blank, the patterns specified with FILE_PATTERNS will # be used. INCLUDE_FILE_PATTERNS = # The PREDEFINED tag can be used to specify one or more macro names that # are defined before the preprocessor is started (similar to the -D option of # gcc). The argument of the tag is a list of macros of the form: name # or name=definition (no spaces). If the definition and the = are # omitted =1 is assumed. To prevent a macro definition from being # undefined via #undef or recursively expanded use the := operator # instead of the = operator. PREDEFINED = # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. # The macro definition that is found in the sources will be used. # Use the PREDEFINED tag if you want to use a different macro definition that # overrules the definition found in the source code. EXPAND_AS_DEFINED = # If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then # doxygen's preprocessor will remove all references to function-like macros # that are alone on a line, have an all uppercase name, and do not end with a # semicolon, because these will confuse the parser if not removed. SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- # The TAGFILES option can be used to specify one or more tagfiles. # Optionally an initial location of the external documentation # can be added for each tagfile. The format of a tag file without # this location is as follows: # # TAGFILES = file1 file2 ... # Adding location for the tag files is done as follows: # # TAGFILES = file1=loc1 "file2 = loc2" ... # where "loc1" and "loc2" can be relative or absolute paths or # URLs. If a location is present for each tag, the installdox tool # does not have to be run to correct the links. # Note that each tag file must have a unique name # (where the name does NOT include the path) # If a tag file is not located in the directory in which doxygen # is run, you must also specify the path to the tagfile here. TAGFILES = # When a file name is specified after GENERATE_TAGFILE, doxygen will create # a tag file that is based on the input files it reads. GENERATE_TAGFILE = # If the ALLEXTERNALS tag is set to YES all external classes will be listed # in the class index. If set to NO only the inherited external classes # will be listed. ALLEXTERNALS = NO # If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed # in the modules index. If set to NO, only the current project's groups will # be listed. EXTERNAL_GROUPS = YES # The PERL_PATH should be the absolute path and name of the perl script # interpreter (i.e. the result of `which perl'). PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- # If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will # generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base # or super classes. Setting the tag to NO turns the diagrams off. Note that # this option also works with HAVE_DOT disabled, but it is recommended to # install and use dot, since it yields more powerful graphs. CLASS_DIAGRAMS = YES # You can define message sequence charts within doxygen comments using the \msc # command. Doxygen will then run the mscgen tool (see # http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the # documentation. The MSCGEN_PATH tag allows you to specify the directory where # the mscgen tool resides. If left empty the tool is assumed to be found in the # default search path. MSCGEN_PATH = # If set to YES, the inheritance and collaboration graphs will hide # inheritance and usage relations if the target is undocumented # or is not a class. HIDE_UNDOC_RELATIONS = YES # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz, a graph visualization # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will # base this on the number of processors available in the system. You can set it # explicitly to a value larger than 0 to get control over the balance # between CPU load and processing speed. DOT_NUM_THREADS = 0 # By default doxygen will write a font called Helvetica to the output # directory and reference it in all dot files that doxygen generates. # When you want a differently looking font you can specify the font name # using DOT_FONTNAME. You need to make sure dot is able to find the font, # which can be done by putting it in a standard location or by setting the # DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory # containing the font. DOT_FONTNAME = Helvetica # The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. # The default size is 10pt. DOT_FONTSIZE = 10 # By default doxygen will tell dot to use the output directory to look for the # FreeSans.ttf font (which doxygen will put there itself). If you specify a # different font using DOT_FONTNAME you can set the path where dot # can find it using this tag. DOT_FONTPATH = # If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect inheritance relations. Setting this tag to YES will force the # the CLASS_DIAGRAMS tag to NO. CLASS_GRAPH = YES # If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen # will generate a graph for each documented class showing the direct and # indirect implementation dependencies (inheritance, containment, and # class references variables) of the class with other documented classes. COLLABORATION_GRAPH = YES # If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen # will generate a graph for groups, showing the direct groups dependencies GROUP_GRAPHS = YES # If the UML_LOOK tag is set to YES doxygen will generate inheritance and # collaboration diagrams in a style similar to the OMG's Unified Modeling # Language. UML_LOOK = NO # If set to YES, the inheritance and collaboration graphs will show the # relations between templates and their instances. TEMPLATE_RELATIONS = NO # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT # tags are set to YES then doxygen will generate a graph for each documented # file showing the direct and indirect include dependencies of the file with # other documented files. INCLUDE_GRAPH = YES # If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and # HAVE_DOT tags are set to YES then doxygen will generate a graph for each # documented header file showing the documented files that directly or # indirectly include this file. INCLUDED_BY_GRAPH = YES # If the CALL_GRAPH and HAVE_DOT options are set to YES then # doxygen will generate a call dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable call graphs # for selected functions only using the \callgraph command. CALL_GRAPH = NO # If the CALLER_GRAPH and HAVE_DOT tags are set to YES then # doxygen will generate a caller dependency graph for every global function # or class method. Note that enabling this option will significantly increase # the time of a run. So in most cases it will be better to enable caller # graphs for selected functions only using the \callergraph command. CALLER_GRAPH = NO # If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen # will generate a graphical hierarchy of all classes instead of a textual one. GRAPHICAL_HIERARCHY = YES # If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES # then doxygen will show the dependencies a directory has on other directories # in a graphical way. The dependency relations are determined by the #include # relations between the files in the directories. DIRECTORY_GRAPH = YES # The DOT_IMAGE_FORMAT tag can be used to set the image format of the images # generated by dot. Possible values are svg, png, jpg, or gif. # If left blank png will be used. DOT_IMAGE_FORMAT = png # The tag DOT_PATH can be used to specify the path where the dot tool can be # found. If left blank, it is assumed the dot tool can be found in the path. DOT_PATH = /usr/bin/dot # The DOTFILE_DIRS tag can be used to specify one or more directories that # contain dot files that are included in the documentation (see the # \dotfile command). DOTFILE_DIRS = # The MSCFILE_DIRS tag can be used to specify one or more directories that # contain msc files that are included in the documentation (see the # \mscfile command). MSCFILE_DIRS = # The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of # nodes that will be shown in the graph. If the number of nodes in a graph # becomes larger than this value, doxygen will truncate the graph, which is # visualized by representing a node as a red box. Note that doxygen if the # number of direct children of the root node in a graph is already larger than # DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note # that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. DOT_GRAPH_MAX_NODES = 50 # The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the # graphs generated by dot. A depth value of 3 means that only nodes reachable # from the root by following a path via at most 3 edges will be shown. Nodes # that lay further from the root node will be omitted. Note that setting this # option to 1 or 2 may greatly reduce the computation time needed for large # code bases. Also note that the size of a graph can be further restricted by # DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. MAX_DOT_GRAPH_DEPTH = 0 # Set the DOT_TRANSPARENT tag to YES to generate images with a transparent # background. This is disabled by default, because dot on Windows does not # seem to support this out of the box. Warning: Depending on the platform used, # enabling this option may lead to badly anti-aliased labels on the edges of # a graph (i.e. they become hard to read). DOT_TRANSPARENT = NO # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This # makes dot run faster, but since only newer versions of dot (>1.8.10) # support this, this feature is disabled by default. DOT_MULTI_TARGETS = NO # If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will # generate a legend page explaining the meaning of the various boxes and # arrows in the dot generated graphs. GENERATE_LEGEND = YES # If the DOT_CLEANUP tag is set to YES (the default) Doxygen will # remove the intermediate dot files that are used to generate # the various graphs. DOT_CLEANUP = YES ================================================ FILE: Makefile.am ================================================ AUTOMAKE_OPTIONS = foreign dist-bzip2 1.6 ACLOCAL_AMFLAGS = -I m4 INCLUDES = $(all_includes) -I$(top_srcdir)/include SUBDIRS = include src pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = librtlsdr.pc BUILT_SOURCES = $(top_srcdir)/.version $(top_srcdir)/.version: echo $(VERSION) > $@-t && mv $@-t $@ dist-hook: echo $(VERSION) > $(distdir)/.tarball-version install-udev-rules: $(INSTALL_DATA) rtl-sdr.rules /etc/udev/rules.d uninstall-udev-rules: rm -rf /etc/udev/rules.d/rtl-sdr.rules EXTRA_DIST = git-version-gen .version if HAVE_DOXYGEN pkgdocdir=$(docdir)/$(PACKAGE)-$(VERSION) doc_htmldir=$(pkgdocdir)/html doc_html_DATA = $(top_builddir)/doc/html.tar $(doc_html_DATA): $(top_builddir)/doc/html/index.html cd $(top_builddir)/doc && tar cf html.tar html $(top_builddir)/doc/html/index.html: $(SOURCES) Doxyfile @rm -rf doc mkdir -p doc $(DOXYGEN) Doxyfile install-data-hook: cd $(DESTDIR)$(doc_htmldir) && tar xf html.tar --strip-components 1 && rm -f html.tar uninstall-hook: rm -rf $(DESTDIR)/$(doc_htmldir) DX_CLEAN = doc/{html,latex}/* doc/html.tar endif MOSTLYCLEANFILES = $(DX_CLEAN) ================================================ FILE: README.md ================================================ [![librtlsdr version](https://img.shields.io/github/tag/librtlsdr/librtlsdr.svg?style=flat&label=librtlsdr)](https://github.com/librtlsdr/librtlsdr/releases) [![GPLv2 License](http://img.shields.io/badge/license-GPLv2-brightgreen.svg)](https://tldrlegal.com/license/gnu-general-public-license-v2) # Description rtl-sdr turns your Realtek RTL2832 based DVB dongle into a SDR receiver # New enhancements and features in this version Many different developments have been taken in this release. For an overview, see [improvements](README_improvements.md) # Build / Install (on debian/ubuntu) ## prerequisites development tools have to be installed: ``` sudo apt-get install build-essential cmake git ``` install the libusb-1.0 development package:: ``` sudo apt-get install libusb-dev libusb-1.0-0-dev ``` ## retrieve the sources - right branch ``` git clone https://github.com/librtlsdr/librtlsdr.git ``` in case you want the *development* branch, e.g. for testing or preparing patches: ``` cd librtlsdr git checkout development ``` by default, you should have the *master* branch, in doubt: ``` cd librtlsdr git status git checkout master ``` ## build run cmake and start compilation. cmake will accept some options, e.g. * `-DINSTALL_UDEV_RULES=ON`, default is `OFF` * `-DDETACH_KERNEL_DRIVER=ON`, default is `OFF` * `-DPROVIDE_UDP_SERVER=ON`, default is `OFF` * `-DWITH_RPC=ON`, default is `OFF` * `-DLINK_RTLTOOLS_AGAINST_STATIC_LIB=ON`, default is `OFF` * `-DRTL_STATIC_BUILD=OFF`, default is `ON`: for MINGW on WIN32 all cmake options are optional ``` mkdir build && cd build cmake ../ -DINSTALL_UDEV_RULES=ON make ``` ## install setup into prefix, usually will require `sudo`: ``` sudo make install sudo ldconfig ``` # Development builds / binaries [GitHub Actions](https://github.com/librtlsdr/librtlsdr/actions) is used for development builds - for Linux (x86), MacOS and Windows x86 32/64. Cross-builds for Windows from a Linux machine: see [cross_build_mingw32.sh](cross_build_mingw32.sh) or [cross_build_mingw64.sh](cross_build_mingw64.sh) # For more information see: http://superkuh.com/rtlsdr.html https://osmocom.org/projects/rtl-sdr/wiki/Rtl-sdr # Setup for SDR only use - without DVB compatibility: - a special USB vendor/product id got reserved at http://pid.codes/ : 0x1209/0x2832 - for such devices the linux kernel's DVB modules are not loaded automatically, thus can be used without blacklisting *dvb_usb_rtl28xxu* below /etc/modprobe.d/ - this allows to use a second RTL dongle for use with DVB in parallel - the IDs can be programmed with '`rtl_eeprom -n`' or '`rtl_eeprom -g realtek_sdr`' - for permanent blacklisting you might check/call following from the clone git directory ```./install-blacklist.sh``` # Contributing Pull requests are always welcome but please make changes to, and pull request from, the development branch. ## Initial setup: - fork the librtlsdr repo via GitHub - clone your fork locally and cd to the cloned repo's folder - add the upstream development repo: * `git remote add upstream git@github.com:librtlsdr/librtlsdr.git` - track the development branch: * `git branch --track development origin/development` ## Normal workflow: - checkout the development branch and make your changes - commit your changes - sync your local development branch with the upstream development branch: * `git fetch upstream` * `git merge upstream/development` - push your commit/s to your forked repo - do a pull request via GitHub ================================================ FILE: README.rtlfm_cmdfile ================================================ rtl_fm now has option '-C' for a command file, from which a list of frequencies are read. So it's similar to using a frequency range, as with "-f 118M:137M:25k" The difference is, that you can parametrize one frequency per line together with - the tuner gain - condition for triggering - measurement duration and a command to execute. Lines starting with '#' are skipped / interpreted as comments. Parameters a seperated by comma. Here's an example: --- # freq in Hz or special keyword, gain in dB, trig_crit (in/out/lt/gt), trig_level, trig_tolerance, #meas, #blocks, trigger_command # windows: rtl_fm -f 105.2m -E rdc -w 350k -s 200k -m 2.2m -B 200000 -C cmdfile.csv -n -v # rtl_fm -f 105.2m -E rdc -w 350k -s 200k -m 2.2m -W 4 -B 10000 -C cmdfile.csv -n -v # linux: ./rtl_fm -f 105.2m -E rdc -w 350k -s 200k -m 2.2m -B 200000 -C cmdfile.csv -n -v # # windows command examples: # cmd.exe, /C echo hello world # cmd.exe, /C start notepad # calc.exe # # linux examples: # ssmtp # sendxmpp ## for piping some message to ssmtp or sendxmpp you'll need to write small scripts # 'adcmax' keyword in first column activates measurement of max adc value at capture rate to determine optimum gain and avoid oversteering # 'adcrms' keyword activates rms calculation at capture rate. for usual it's similar to adcmax. there are differences in case of oversteering # activate verbose output (option '-v') to get the output. The maximum possible sample value is 128. # You should have approx. 6 dB headroom to allow measuring stronger signals, that means measured baseline values should be below 64. # An the other side you'll want to measure weaker signals, so the measured baseline value should be above a minimum of approx 8 or 16. adcmax, adcrms, 100.7m, 30, in, 0, 1, 10, 100, 33.0m, 20,out,60, 3, 10, 400, /home/odroid/test/simple.sh, frq !freq! gain !gain! measured !mlevel! tenth dB !crit! { !reflevel! +/- !reftol! tenth dB } # now check for optimal gain in some steps, which should be done per frequency!: 100.7m, 0, gt, 400, 1, 1, 100, 100.7m, 3, gt, 400, 1, 1, 100, 100.7m, 6, gt, 400, 1, 1, 100, 100.7m, 12, gt, 400, 1, 1, 100, 100.7m, 18, gt, 400, 1, 1, 100, --- * first frequency is 100.7 MHz, tuned with ~ 30 dB tuner gain; condition is 'in' { 0 +/- 1 } dB, with 10 measurements, averaging the rms level in dB. Each single measurement is processed after decimation of block/buffer-size many samples (see option -W). The resulting number of decimated samples might get too small to allow a reliable measurement. This number also depends on the capture rate: ensure minimum capture rate with option '-m'. This is also important for reducing aliases. Check the output 'block length after decimation is ... samples'! If condition for measured level is true, then a command can be triggered, which is executed in background. Then, a next trigger for this frequency is blocked for 100 measurements. There is nothing triggered for 100.7 MHz. * 2nd frequency is 33.0 MHz, tuned with ~ 20 dB tuner gain; condition is 'out' { 60 +/- 3 } dB, with 10 measurements. That means, the trigger is activated when averaged level is below 57 dB or above 63 dB. Next trigger for this frequency is blocked for 400 measurements. Triggered command is the shell script '/home/odroid/test/simple.sh', with the arguments 'frq !freq! gain !gain! measured !mlevel! tenth dB !crit! { !reflevel! +/- !reftol! tenth dB }'. You can use following keywords in the arguments, which need to be free standing!: - !freq! current frequency in Hz - !gain! current tuner gain in tenth dB to allow easier evaluation from scripts. - !mlevel! average measured level in tenth dB - !crit! one of "in", "out", "<" or ">" for the tested condition - !reflevel! condition's reference level in tenth dB - !reftol! condition's reference tolerance in tenth dB Application might be monitoring of some stations and triggering a notification, e.g. via ssmtp or sendxmpp, when a stations power level is below it's expected value. Another application might be triggering a recording with a second RTL dongle. Send comments, suggestions or reports to Hayati Ayguen ================================================ FILE: README.rtlsdr_rpc ================================================ This implementation of librtlsdr makes remote dongles appear to the local software as if they were on the same computer. It works by forwarding librtlsdr calls to the remote computer over TCP. It allows one to use existing tools without modifying them. Also, it allows a developer to use the same API no matter weither the dongle is local or distant. To use it, one must compile and install the library with CMAKE the usual way. Note that you may need to uninstall the existing librtlsdr, as people reported runtime errors due to conflicting installs. Then, a server (called rtl_rpcd) must be run on the remote location. In my case, the dongle is in a beagle bone black is at address 192.168.0.43: beagleboneblack #> ./rtl_rpcd Then, the existing tool (for instance rtlizer) can be run on the local computer using: RTLSDR_RPC_IS_ENABLED=1 RTLSDR_RPC_SERV_ADDR=192.168.0.43 \ rtlizer This implementation still has some limitations, but works well in most cases. Please report any bug to texane@gmail.com Also, note that the latest version of libusb should be used as librtlsdr crashed when used with older version (esp. the rtlsdr_read_async routine): https://github.com/libusb/libusb.git list of known working software: rtl_fm rtl_power rtlsdr-waterfall rtlizer gnuradio-companion cubicsdr gqrx linrad ================================================ FILE: README_improvements.md ================================================ # improvements, compared to the osmocom sources ## versions comparing osmocom's git git://git.osmocom.org/rtl-sdr.git dated from 2020-08-19 against librtlsdr's development branch (initially same date, but updated 2020-10-04) ## "Driver" Library Features * added support for special USB (vendor) VID 0x1209 (product) PID 0x2832: "Generic RTL2832U": * A special USB vendor/product id got reserved at http://pid.codes/1209/2832/ * for such devices the linux kernel's DVB modules are not loaded automatically, thus can be used without blacklisting dvb_usb_rtl28xxu below /etc/modprobe.d/ * this allows to use a second RTL dongle for use with DVB in parallel * the IDs can be programmed with '`rtl_eeprom -n`' or '`rtl_eeprom -g realtek_sdr`' * see https://www.rtl-sdr.com/watching-dvb-t-tv-and-using-sdr-mode-at-the-same-time-with-two-rtl-sdrs/ * added support for using RTLSDR-Dongle from remote - see rtl_rpcd and [README.rtlsdr_rpc](README.rtlsdr_rpc) * support for all GPIO pins of RTL2832 through API and rtl_biast * functions were provided from Marko Cebokli at http://lea.hamradio.si/~s57uuu/mischam/rtlsdr/ports.html * improvements for R820T/2 tuner also see https://codingspirit.de/librtlsdr-driver.pdf several features from https://github.com/old-dab/rtlsdr/ * added better bandwidth support * added smaller bandwidths, improving selectivity: 290, 375, 420, 470, 600, 860, 950, 1100, 1200, 1300, 1500, 1600, 1750, 1800, 1950, 2200, 3000, 5000 kHz. These are coarse measured values .. which might get adjusted in future. * bandwidth filters utilize tuner's low- and highpass filters at IF * added spectrum flipping (inside tuner) - and back in RTL2832 * the band edges (low/high-pass) have different steepness; the steeper edge can be selected with the mixer sideband (`rtlsdr_set_tuner_sideband()`), to achieve better attenuation depending on signal scenario * added (automatic) control over VGA (variable gain amplifier) * VGA gain (besides LNA and Mixer) can be utilized and set to automatic, letting it controlled from RTL2832U. Having all automatic (AGC) including activation of digital AGC in RTL2832 (`rtlsdr_set_agc_mode()`), oversteering effects got reduced (a lot). * total gain range now up to 100 dB * deactivated "Filter extension under weak signal" for a stable filter characteristic * added shifting of IF-center, to receive away from DC. see `rtlsdr_set_tuner_band_center()` * harmonic reception for R820T/2 tuner: * allow reception for frequencies above ~ 1.76 GHz * tested in lab conditions up to 6.0 GHz * 5th harmonic is used automatically by default when direct reception is not possible (when tuner-PLL doesn't lock) * 3rd harmonic looks also promising. harmonic factor is parametrizable with passthrough driver option '**harm=**' to set n-th harmonic * reception in real world from antenna is very likely to require a suitable high pass or bandpass filter and an LNA in front of the RTLSDR dongle * passthrough driver options: * all the rtlsdr tools below support option '**-O**' followed by a '**:**' separated string of specific options, which are passed to the library by calling `rtlsdr_set_opt_string()`. * process options from environment variable **LIBRTLSDR_OPT** for applications which don't support/use `rtlsdr_set_opt_string()` and don't support some of the features. * there are many options, e.g. * **bw=** set the tuner bandwidth in kHz * **agc=** activate tuner AGC * **gain=** set tuner's gain value manually in tenth dB * **dagc=** set digital AGC of RTL2832 * **t=** set bias tee for RTL-SDR V3 or compatible, see https://www.rtl-sdr.com/rtl-sdr-blog-v-3-dongles-user-guide/ * **tp=** set pin for bias tee * **ds=** set direct sampling (HF mode) for RTL-SDR V3 or compatible, see https://www.rtl-sdr.com/rtl-sdr-blog-v-3-dongles-user-guide/ * **dm=** set direct sampling mode * many of the options are R820T/2-tuner specific: * **bc=** set the the reception bands' center in Hz * **sb=** set tuner sideband * **ifm=** set IF mode (AGC, fixed gain, ..) for VGA (variable gain amplifier) * **harm=** set n-th harmonic reception * **vcocmin=** set minimum VCO current * **vcocmax=** set maximum VCO current * **vcoalgo=** set VCO algorithm * probably some more: it's highly probable, that this list is incomplete ## "Driver" Library API * added rtlsdr_set_and_get_tuner_bandwidth(), which also delivers the bandwidth. [ with rtlsdr_set_tuner_bandwidth() does not deliver the bandwidth ] * added rtlsdr_set_tuner_band_center(), to set center of the filtered tuner band * added rtlsdr_set_tuner_sideband(), to set mixer sideband * added rtlsdr_set_tuner_gain_ext(), special for R820T/2 tuner * added rtlsdr_set_tuner_if_mode(), sets AGC modes in detail * added rtlsdr_set_dithering(), to allow disabling frequency dithering for R820T/2 tuner * added rtlsdr_set_ds_mode() including threshold frequency * added rtlsdr_ir_query() * added rtlsdr_set_opt_string() and rtlsdr_get_opt_help() for configuration of 'driver' - especially from command line. new alternative: environment variable **LIBRTLSDR_OPT** * added rtlsdr_set_tuner_i2c_register(), rtlsdr_get_tuner_i2c_register() and rtlsdr_set_tuner_i2c_override() exposing hacking of tuner-specific I2C registers * added rtlsdr_get_ver_id(), to allow discrimination between osmocom library - or this fork * added rtlsdr_get_version() * added rtlsdr_set_gpio_output(), rtlsdr_set_gpio_input(), rtlsdr_set_gpio_bit(), rtlsdr_get_gpio_bit(), rtlsdr_set_gpio_byte(), rtlsdr_get_gpio_byte() and rtlsdr_set_gpio_status() * added rtlsdr_set_center_freq64(), to set frequencies above ~4.29 GHz, the 32-bit limit * added rtlsdr_get_center_freq64() * added rtlsdr_set_harmonic_rx() to activate/change harmonic reception ## Added Tools * added rtl_ir: display received IR signals. * requires the IR diode of an RTL-SDR - which might not exist! * added rtl_rpcd: a Remote Procedure Call server for RTL-SDR dongles. * for use, set environment variable "**RTLSDR_RPC_IS_ENABLED**" * optionally set environment varibales "**RTLSDR_RPC_SERV_ADDR**" and "**RTLSDR_RPC_SERV_PORT**". These default to "127.0.0.1" and "40000". * requires cmake option **WITH_RPC** * added rtl_raw2wav: save rtl_sdr or rtl_fm's output (pipe) into a wave file, including some meta information like timestamp and frequency * added rtl_udp: same as rtl_tcp - just using UDP instead of TCP * added rtl_wavestat: display wave file meta information * added rtl_wavestream: stream raw data (in specified format) ## Improved Tools * rtl_fm: * added command file option '-C', which can trigger actions depending on signal. have a look at [README.rtlfm_cmdfile](README.rtlfm_cmdfile). * added command line interface option '-E rdc', to enable dc blocking on raw I/Q data at capture rate * added CLI option '-E rtlagc', to enable rtl2832's digital agc * added CLI option '-E bclo', to use tuner bandwidths low corner as band center * added CLI option '-E bchi', to use tuner bandwidths high corner as band center * added CLI option '-O', to set RTL driver options separated with ':', e.g. -O 'bc=30000:agc=0' * added CLI option '-R', to specify number of seconds to run * added CLI option '-H', to write wave Header to file, producing a wave file with meta information, compatible with several SDR programs * added CLI option '-o', to request oversampling (4 recommended) for processing gain * rtl_biast: * several options for reading/writing other GPIOs * many tools have more options. compare all the details by starting with command line option '-h'. ## "Driver" Library's UDP-Server * enabled by cmake option **PROVIDE_UDP_SERVER** for tests. OFF by default * activated by rtlsdr_set_opt_string(): "**port=1**" or "**port=**", default port number: 32323 * purpose is to allow configuration at runtime with a simple text protocol, e.g. with netcat * for detailed protocol, see comment section in parse() of librtlsdr.c. or look for sections with '#ifdef WITH_UDP_SERVER' * this feature was copied and enhanced from https://sourceforge.net/projects/librtlsdr-wincontrol/ developed from [sourceforge user randaller](https://sourceforge.net/u/randaller/profile/), which in turn is an adaption of https://github.com/gat3way/r820tweak, which was developed by Milen Rangelov * simple usage from command line, e.g. to retrieve help ``` echo "h" | timeout 1 netcat -u 127.0.0.1 32323 ``` without `timeout`, press Ctrl+C the output is: ``` g # get content of I2C .. s [] # set conten S [] # set content - keeping value in future i # set IF frequency [0 .. 28'800'000] f # set center frequency b # set tuner bandwidth c # set tuner bw center in output [-1'600'000 .. 1'600'000] v # set tuner sideband: 0 for LSB, 1 for USB a # set VGA: 0 for auto; in tenth dB or 10000+idx m # set tuner gain M # 0 .. 3: digital rtl agc (0..1) * 2 + tuner agc (0..1) ``` ## RTL_TCP TCP-PROTOCOL * allows non-GPL programs to utilize the RTLSDR stuff in a license compliant way * added several control functions in rtl_tcp, not existing in osmocom release: UDP_ESTABLISH, UDP_TERMINATE, SET_I2C_TUNER_REGISTER, SET_I2C_TUNER_OVERRIDE, SET_TUNER_BW_IF_CENTER, SET_TUNER_IF_MODE, SET_SIDEBAND, REPORT_I2C_REGS, SET_FREQ_HI32 * control functions documented in rtl_tcp.h * (by default) control port number 1234, configurable via command-line-interface (CLI) * response(s) at +1 of control port: 1235, configurable via CLI * protocol details in protocol_rtl_tcp.txt ================================================ FILE: cmake/Modules/FindLibUSB.cmake ================================================ if(NOT LIBUSB_FOUND) pkg_check_modules (LIBUSB_PKG libusb-1.0) find_path(LIBUSB_INCLUDE_DIR NAMES libusb.h PATHS ${LIBUSB_PKG_INCLUDE_DIRS} /usr/include/libusb-1.0 /usr/include /usr/local/include ) #standard library name for libusb-1.0 set(libusb1_library_names usb-1.0 libusb-1.0) #libusb-1.0 compatible library on freebsd if((CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") OR (CMAKE_SYSTEM_NAME STREQUAL "kFreeBSD")) list(APPEND libusb1_library_names usb) endif() find_library(LIBUSB_LIBRARIES NAMES ${libusb1_library_names} PATHS ${LIBUSB_PKG_LIBRARY_DIRS} /usr/lib /usr/local/lib ) include(CheckFunctionExists) if(LIBUSB_INCLUDE_DIRS) set(CMAKE_REQUIRED_INCLUDES ${LIBUSB_INCLUDE_DIRS}) endif() if(LIBUSB_LIBRARIES) set(CMAKE_REQUIRED_LIBRARIES ${LIBUSB_LIBRARIES}) endif() CHECK_FUNCTION_EXISTS("libusb_handle_events_timeout_completed" HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED) if(HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED) add_definitions(-DHAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED=1) endif(HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED) CHECK_FUNCTION_EXISTS("libusb_error_name" HAVE_LIBUSB_ERROR_NAME) if(HAVE_LIBUSB_ERROR_NAME) add_definitions(-DHAVE_LIBUSB_ERROR_NAME=1) endif(HAVE_LIBUSB_ERROR_NAME) if(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) set(LIBUSB_FOUND TRUE CACHE INTERNAL "libusb-1.0 found") message(STATUS "Found libusb-1.0: ${LIBUSB_INCLUDE_DIR}, ${LIBUSB_LIBRARIES}") else(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) set(LIBUSB_FOUND FALSE CACHE INTERNAL "libusb-1.0 found") message(STATUS "libusb-1.0 not found.") endif(LIBUSB_INCLUDE_DIR AND LIBUSB_LIBRARIES) mark_as_advanced(LIBUSB_INCLUDE_DIR LIBUSB_LIBRARIES) endif(NOT LIBUSB_FOUND) ================================================ FILE: cmake/Modules/FindThreads.cmake ================================================ # Updated FindThreads.cmake that supports pthread-win32 # Downloaded from http://www.vtk.org/Bug/bug_view_advanced_page.php?bug_id=6399 # - This module determines the thread library of the system. # # The following variables are set # CMAKE_THREAD_LIBS_INIT - the thread library # CMAKE_USE_SPROC_INIT - are we using sproc? # CMAKE_USE_WIN32_THREADS_INIT - using WIN32 threads? # CMAKE_USE_PTHREADS_INIT - are we using pthreads # CMAKE_HP_PTHREADS_INIT - are we using hp pthreads # # If use of pthreads-win32 is desired, the following variables # can be set. # # THREADS_USE_PTHREADS_WIN32 - # Setting this to true searches for the pthreads-win32 # port (since CMake 2.8.0) # # THREADS_PTHREADS_WIN32_EXCEPTION_SCHEME # C = no exceptions (default) # (NOTE: This is the default scheme on most POSIX thread # implementations and what you should probably be using) # CE = C++ Exception Handling # SE = Structure Exception Handling (MSVC only) # (NOTE: Changing this option from the default may affect # the portability of your application. See pthreads-win32 # documentation for more details.) # #====================================================== # Example usage where threading library # is provided by the system: # # find_package(Threads REQUIRED) # add_executable(foo foo.cc) # target_link_libraries(foo ${CMAKE_THREAD_LIBS_INIT}) # # Example usage if pthreads-win32 is desired on Windows # or a system provided thread library: # # set(THREADS_USE_PTHREADS_WIN32 true) # find_package(Threads REQUIRED) # include_directories(${THREADS_PTHREADS_INCLUDE_DIR}) # # add_executable(foo foo.cc) # target_link_libraries(foo ${CMAKE_THREAD_LIBS_INIT}) # INCLUDE (CheckIncludeFiles) INCLUDE (CheckLibraryExists) SET(Threads_FOUND FALSE) IF(WIN32 AND NOT CYGWIN AND THREADS_USE_PTHREADS_WIN32) SET(_Threads_ptwin32 true) ENDIF() # Do we have sproc? IF(CMAKE_SYSTEM MATCHES IRIX) CHECK_INCLUDE_FILES("sys/types.h;sys/prctl.h" CMAKE_HAVE_SPROC_H) ENDIF() IF(CMAKE_HAVE_SPROC_H) # We have sproc SET(CMAKE_USE_SPROC_INIT 1) ELSEIF(_Threads_ptwin32) IF(NOT DEFINED THREADS_PTHREADS_WIN32_EXCEPTION_SCHEME) # Assign the default scheme SET(THREADS_PTHREADS_WIN32_EXCEPTION_SCHEME "C") ELSE() # Validate the scheme specified by the user IF(NOT THREADS_PTHREADS_WIN32_EXCEPTION_SCHEME STREQUAL "C" AND NOT THREADS_PTHREADS_WIN32_EXCEPTION_SCHEME STREQUAL "CE" AND NOT THREADS_PTHREADS_WIN32_EXCEPTION_SCHEME STREQUAL "SE") MESSAGE(FATAL_ERROR "See documentation for FindPthreads.cmake, only C, CE, and SE modes are allowed") ENDIF() IF(NOT MSVC AND THREADS_PTHREADS_WIN32_EXCEPTION_SCHEME STREQUAL "SE") MESSAGE(FATAL_ERROR "Structured Exception Handling is only allowed for MSVC") ENDIF(NOT MSVC AND THREADS_PTHREADS_WIN32_EXCEPTION_SCHEME STREQUAL "SE") ENDIF() FIND_PATH(THREADS_PTHREADS_INCLUDE_DIR pthread.h) # Determine the library filename IF(MSVC) SET(_Threads_pthreads_libname pthreadV${THREADS_PTHREADS_WIN32_EXCEPTION_SCHEME}2) ELSEIF(MINGW) SET(_Threads_pthreads_libname pthreadG${THREADS_PTHREADS_WIN32_EXCEPTION_SCHEME}2) ELSE() MESSAGE(FATAL_ERROR "This should never happen") ENDIF() # Use the include path to help find the library if possible SET(_Threads_lib_paths "") IF(THREADS_PTHREADS_INCLUDE_DIR) GET_FILENAME_COMPONENT(_Threads_root_dir ${THREADS_PTHREADS_INCLUDE_DIR} PATH) SET(_Threads_lib_paths ${_Threads_root_dir}/lib) ENDIF() FIND_LIBRARY(THREADS_PTHREADS_WIN32_LIBRARY NAMES ${_Threads_pthreads_libname} PATHS ${_Threads_lib_paths} DOC "The Portable Threads Library for Win32" NO_SYSTEM_PATH ) IF(THREADS_PTHREADS_INCLUDE_DIR AND THREADS_PTHREADS_WIN32_LIBRARY) MARK_AS_ADVANCED(THREADS_PTHREADS_INCLUDE_DIR) SET(CMAKE_THREAD_LIBS_INIT ${THREADS_PTHREADS_WIN32_LIBRARY}) SET(CMAKE_HAVE_THREADS_LIBRARY 1) SET(Threads_FOUND TRUE) ENDIF() MARK_AS_ADVANCED(THREADS_PTHREADS_WIN32_LIBRARY) ELSE() # Do we have pthreads? CHECK_INCLUDE_FILES("pthread.h" CMAKE_HAVE_PTHREAD_H) IF(CMAKE_HAVE_PTHREAD_H) # # We have pthread.h # Let's check for the library now. # SET(CMAKE_HAVE_THREADS_LIBRARY) IF(NOT THREADS_HAVE_PTHREAD_ARG) # Do we have -lpthreads CHECK_LIBRARY_EXISTS(pthreads pthread_create "" CMAKE_HAVE_PTHREADS_CREATE) IF(CMAKE_HAVE_PTHREADS_CREATE) SET(CMAKE_THREAD_LIBS_INIT "-lpthreads") SET(CMAKE_HAVE_THREADS_LIBRARY 1) SET(Threads_FOUND TRUE) ENDIF() # Ok, how about -lpthread CHECK_LIBRARY_EXISTS(pthread pthread_create "" CMAKE_HAVE_PTHREAD_CREATE) IF(CMAKE_HAVE_PTHREAD_CREATE) SET(CMAKE_THREAD_LIBS_INIT "-lpthread") SET(Threads_FOUND TRUE) SET(CMAKE_HAVE_THREADS_LIBRARY 1) ENDIF() IF(CMAKE_SYSTEM MATCHES "SunOS.*") # On sun also check for -lthread CHECK_LIBRARY_EXISTS(thread thr_create "" CMAKE_HAVE_THR_CREATE) IF(CMAKE_HAVE_THR_CREATE) SET(CMAKE_THREAD_LIBS_INIT "-lthread") SET(CMAKE_HAVE_THREADS_LIBRARY 1) SET(Threads_FOUND TRUE) ENDIF() ENDIF(CMAKE_SYSTEM MATCHES "SunOS.*") ENDIF(NOT THREADS_HAVE_PTHREAD_ARG) IF(NOT CMAKE_HAVE_THREADS_LIBRARY) # If we did not found -lpthread, -lpthread, or -lthread, look for -pthread IF("THREADS_HAVE_PTHREAD_ARG" MATCHES "^THREADS_HAVE_PTHREAD_ARG") MESSAGE(STATUS "Check if compiler accepts -pthread") TRY_RUN(THREADS_PTHREAD_ARG THREADS_HAVE_PTHREAD_ARG ${CMAKE_BINARY_DIR} ${CMAKE_ROOT}/Modules/CheckForPthreads.c CMAKE_FLAGS -DLINK_LIBRARIES:STRING=-pthread COMPILE_OUTPUT_VARIABLE OUTPUT) IF(THREADS_HAVE_PTHREAD_ARG) IF(THREADS_PTHREAD_ARG MATCHES "^2$") SET(Threads_FOUND TRUE) MESSAGE(STATUS "Check if compiler accepts -pthread - yes") ELSE() MESSAGE(STATUS "Check if compiler accepts -pthread - no") FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Determining if compiler accepts -pthread returned ${THREADS_PTHREAD_ARG} instead of 2. The compiler had the following output:\n${OUTPUT}\n\n") ENDIF() ELSE() MESSAGE(STATUS "Check if compiler accepts -pthread - no") FILE(APPEND ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeError.log "Determining if compiler accepts -pthread failed with the following output:\n${OUTPUT}\n\n") ENDIF() ENDIF("THREADS_HAVE_PTHREAD_ARG" MATCHES "^THREADS_HAVE_PTHREAD_ARG") IF(THREADS_HAVE_PTHREAD_ARG) SET(Threads_FOUND TRUE) SET(CMAKE_THREAD_LIBS_INIT "-pthread") ENDIF() ENDIF(NOT CMAKE_HAVE_THREADS_LIBRARY) ENDIF(CMAKE_HAVE_PTHREAD_H) ENDIF() IF(CMAKE_THREAD_LIBS_INIT) SET(CMAKE_USE_PTHREADS_INIT 1) SET(Threads_FOUND TRUE) ENDIF() IF(CMAKE_SYSTEM MATCHES "Windows" AND NOT THREADS_USE_PTHREADS_WIN32) SET(CMAKE_USE_WIN32_THREADS_INIT 1) SET(Threads_FOUND TRUE) ENDIF() IF(CMAKE_USE_PTHREADS_INIT) IF(CMAKE_SYSTEM MATCHES "HP-UX-*") # Use libcma if it exists and can be used. It provides more # symbols than the plain pthread library. CMA threads # have actually been deprecated: # http://docs.hp.com/en/B3920-90091/ch12s03.html#d0e11395 # http://docs.hp.com/en/947/d8.html # but we need to maintain compatibility here. # The CMAKE_HP_PTHREADS setting actually indicates whether CMA threads # are available. CHECK_LIBRARY_EXISTS(cma pthread_attr_create "" CMAKE_HAVE_HP_CMA) IF(CMAKE_HAVE_HP_CMA) SET(CMAKE_THREAD_LIBS_INIT "-lcma") SET(CMAKE_HP_PTHREADS_INIT 1) SET(Threads_FOUND TRUE) ENDIF(CMAKE_HAVE_HP_CMA) SET(CMAKE_USE_PTHREADS_INIT 1) ENDIF() IF(CMAKE_SYSTEM MATCHES "OSF1-V*") SET(CMAKE_USE_PTHREADS_INIT 0) SET(CMAKE_THREAD_LIBS_INIT ) ENDIF() IF(CMAKE_SYSTEM MATCHES "CYGWIN_NT*") SET(CMAKE_USE_PTHREADS_INIT 1) SET(Threads_FOUND TRUE) SET(CMAKE_THREAD_LIBS_INIT ) SET(CMAKE_USE_WIN32_THREADS_INIT 0) ENDIF() ENDIF(CMAKE_USE_PTHREADS_INIT) INCLUDE(FindPackageHandleStandardArgs) IF(_Threads_ptwin32) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Threads DEFAULT_MSG THREADS_PTHREADS_WIN32_LIBRARY THREADS_PTHREADS_INCLUDE_DIR) ELSE() FIND_PACKAGE_HANDLE_STANDARD_ARGS(Threads DEFAULT_MSG Threads_FOUND) ENDIF() ================================================ FILE: cmake/Modules/Version.cmake ================================================ # Copyright 2013 OSMOCOM Project # # This file is part of rtl-sdr # # rtl-sdr is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3, or (at your option) # any later version. # # rtl-sdr is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with rtl-sdr; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. if(DEFINED __INCLUDED_VERSION_CMAKE) return() endif() set(__INCLUDED_VERSION_CMAKE TRUE) # VERSION_INFO_* variables must be provided by user set(MAJOR_VERSION ${VERSION_INFO_MAJOR_VERSION}) set(MINOR_VERSION ${VERSION_INFO_MINOR_VERSION}) set(PATCH_VERSION ${VERSION_INFO_PATCH_VERSION}) ######################################################################## # Extract the version string from git describe. ######################################################################## find_package(Git QUIET) if(GIT_FOUND AND EXISTS ${PROJECT_SOURCE_DIR}/.git) message(STATUS "Extracting version information from git describe...") execute_process( COMMAND ${GIT_EXECUTABLE} describe --always --abbrev=4 --long OUTPUT_VARIABLE GIT_DESCRIBE OUTPUT_STRIP_TRAILING_WHITESPACE WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} ) else() set(GIT_DESCRIBE "v${MAJOR_VERSION}.${MINOR_VERSION}.x-xxx-xunknown") endif() ######################################################################## # Use the logic below to set the version constants ######################################################################## if("${PATCH_VERSION}" STREQUAL "git") # VERSION: 3.6git-xxx-gxxxxxxxx # LIBVER: 3.6git set(VERSION "${GIT_DESCRIBE}") set(LIBVER "${MAJOR_VERSION}.${MINOR_VERSION}${PATCH_VERSION}") else() # This is a numbered release. # VERSION: 3.6.1 # LIBVER: 3.6.1 set(VERSION "${MAJOR_VERSION}.${MINOR_VERSION}.${PATCH_VERSION}") set(LIBVER "${VERSION}") endif() ================================================ FILE: cmake/cmake_uninstall.cmake.in ================================================ # http://www.vtk.org/Wiki/CMake_FAQ#Can_I_do_.22make_uninstall.22_with_CMake.3F IF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") MESSAGE(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") ENDIF(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") FILE(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) STRING(REGEX REPLACE "\n" ";" files "${files}") FOREACH(file ${files}) MESSAGE(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") IF(EXISTS "$ENV{DESTDIR}${file}") EXEC_PROGRAM( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) IF(NOT "${rm_retval}" STREQUAL 0) MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") ENDIF(NOT "${rm_retval}" STREQUAL 0) ELSEIF(IS_SYMLINK "$ENV{DESTDIR}${file}") EXEC_PROGRAM( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) IF(NOT "${rm_retval}" STREQUAL 0) MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") ENDIF(NOT "${rm_retval}" STREQUAL 0) ELSE(EXISTS "$ENV{DESTDIR}${file}") MESSAGE(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") ENDIF(EXISTS "$ENV{DESTDIR}${file}") ENDFOREACH(file) ================================================ FILE: configure.ac ================================================ AC_INIT([librtlsdr], m4_esyscmd([./git-version-gen .tarball-version]), [osmocom-sdr@lists.osmocom.org]) AM_INIT_AUTOMAKE([dist-bzip2]) dnl kernel style compile messages m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) dnl checks for programs AC_PROG_MAKE_SET AC_PROG_CC AC_PROG_INSTALL AM_PROG_CC_C_O LT_INIT AC_PROG_LIBTOOL PKG_CHECK_MODULES(LIBUSB, libusb-1.0 >= 1.0) LIBS="$LIBS $LIBUSB_LIBS" CFLAGS="$CFLAGS $LIBUSB_CFLAGS" AC_PATH_PROG(DOXYGEN,doxygen,false) AM_CONDITIONAL(HAVE_DOXYGEN, test $DOXYGEN != false) AC_CONFIG_MACRO_DIR([m4]) dnl checks for header files AC_HEADER_STDC AC_CHECK_HEADERS(sys/types.h) AC_CHECK_HEADERS(pthread.h,, [AC_MSG_ERROR([pthread.h required])]) # pc variables AC_SUBST(RTLSDR_PC_LIBS,["$LIBS"]) AC_SUBST(RTLSDR_PC_CFLAGS,["$CFLAGS"]) dnl checks for required libraries dnl pthreads AC_CHECK_LIB(pthread, pthread_create, [LIBS="$LIBS -lpthread"]) dnl libmath (for rtl_fm) AC_CHECK_LIB(m, atan2, [LIBS="$LIBS -lm"]) dnl libmath (for rtl_adsb) AC_CHECK_LIB(m, sqrt, [LIBS="$LIBS -lm"]) dnl libmath (for rtl_power) AC_CHECK_LIB(m, atan2, [LIBS="$LIBS -lm"]) dnl librealtime (for rtl_test) AC_CHECK_LIB(rt, clock_gettime, [LIBS="$LIBS -lrt"]) AC_ARG_ENABLE(sanitize, [AS_HELP_STRING([--enable-sanitize], [Compile with address sanitizer enabled], )], [sanitize=$enableval], [sanitize="no"]) if test x"$sanitize" = x"yes" then CFLAGS="$CFLAGS -fsanitize=address -fsanitize=undefined" CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" fi AC_ARG_ENABLE(werror, [AS_HELP_STRING( [--enable-werror], [Turn all compiler warnings into errors, with exceptions: a) deprecation (allow upstream to mark deprecation without breaking builds); b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds) ] )], [werror=$enableval], [werror="no"]) if test x"$werror" = x"yes" then WERROR_FLAGS="-Werror" WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" CFLAGS="$CFLAGS $WERROR_FLAGS" CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" fi # The following test is taken from WebKit's webkit.m4 saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fvisibility=hidden " AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([char foo;])], [ AC_MSG_RESULT([yes]) SYMBOL_VISIBILITY="-fvisibility=hidden"], AC_MSG_RESULT([no])) CFLAGS="$saved_CFLAGS" AC_SUBST(SYMBOL_VISIBILITY) AC_MSG_CHECKING(whether compiler understands -Wall) old_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wextra -Wno-unused-parameter -Wno-unused -Wsign-compare -Wdeclaration-after-statement" AC_TRY_COMPILE([],[], AC_MSG_RESULT(yes), AC_MSG_RESULT(no) CFLAGS="$old_CFLAGS") AC_ARG_ENABLE(driver-detach, [ --enable-driver-detach Enable detaching of kernel driver (disabled by default)], [if test x$enableval = xyes; then CFLAGS="$CFLAGS -DDETACH_KERNEL_DRIVER" fi]) dnl Generate the output AC_CONFIG_HEADER(config.h) AC_OUTPUT( librtlsdr.pc include/Makefile src/Makefile Makefile Doxyfile ) ================================================ FILE: contrib/jenkins.sh ================================================ #!/usr/bin/env bash # jenkins build helper script for openbsc. This is how we build on jenkins.osmocom.org if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !" exit 2 fi set -ex base="$PWD" deps="$base/deps" inst="$deps/install" export deps inst osmo-clean-workspace.sh mkdir "$deps" || true set +x echo echo echo echo " =============================== rtl-sdr ===============================" echo set -x cd "$base" autoreconf --install --force ./configure --enable-sanitize --enable-werror $MAKE $PARALLEL_MAKE LD_LIBRARY_PATH="$inst/lib" $MAKE check \ || cat-testlogs.sh LD_LIBRARY_PATH="$inst/lib" \ DISTCHECK_CONFIGURE_FLAGS="--enable-werror" \ $MAKE distcheck \ || cat-testlogs.sh osmo-clean-workspace.sh ================================================ FILE: cross_build_mingw32.sh ================================================ #!/bin/bash # requires debian/ubuntu packages: zip gcc-mingw-w64 REPO_DIR=$(pwd) if [ -z "$1" ]; then echo "usage: $0 " exit 1 fi ZIP_POST="$1" shift CROSS="i686-w64-mingw32" WN="w32" TOOLCHAIN="mingw-w32-i686.cmake" # libusb if /bin/true; then cd ${REPO_DIR} && rm -rf libusb_${WN} cd ${REPO_DIR} && git clone --branch v1.0.23 https://github.com/libusb/libusb.git libusb_${WN} echo -e "\n\n********************************************************" echo "start build of libusb_${WN}" cd ${REPO_DIR}/libusb_${WN} && ./bootstrap.sh && \ CC=${CROSS}-gcc \ AR=${CROSS}-ar \ RANLIB=${CROSS}-ranlib \ ./configure --prefix=${REPO_DIR}/mingw_libusb_${WN} --host=${CROSS} --disable-shared && \ make && make install echo -e "\n\nlisting of ${REPO_DIR}/mingw_libusb_${WN}" ls -alh ${REPO_DIR}/mingw_libusb_${WN} echo -e "\nlisting of ${REPO_DIR}/mingw_libusb_${WN}/include" ls -alh ${REPO_DIR}/mingw_libusb_${WN}/include echo -e "\nlisting of ${REPO_DIR}/mingw_libusb_${WN}/lib" ls -alh ${REPO_DIR}/mingw_libusb_${WN}/lib echo -e "\n" fi # librtlsdr if /bin/true; then cd ${REPO_DIR} && rm -rf build_${WN} echo -e "\n\n********************************************************" echo "start build of librtlsdr_${WN}" mkdir ${REPO_DIR}/build_${WN} && cd ${REPO_DIR}/build_${WN} && \ cmake -DCMAKE_TOOLCHAIN_FILE=${REPO_DIR}/${TOOLCHAIN} \ -DCMAKE_INSTALL_PREFIX=${REPO_DIR}/rtlsdr-bin-${WN}_${ZIP_POST} \ -DRTL_STATIC_BUILD=ON "$@" \ -DLIBUSB_INCLUDE_DIR=${REPO_DIR}/mingw_libusb_${WN}/include/libusb-1.0 \ -DLIBUSB_LIBRARIES=${REPO_DIR}/mingw_libusb_${WN}/lib/libusb-1.0.a \ ../ && \ make && make install md5sum ${REPO_DIR}/rtlsdr-bin-${WN}_${ZIP_POST}/bin/* >${REPO_DIR}/rtlsdr-bin-${WN}_${ZIP_POST}/bin/md5sums.txt sha1sum ${REPO_DIR}/rtlsdr-bin-${WN}_${ZIP_POST}/bin/* >${REPO_DIR}/rtlsdr-bin-${WN}_${ZIP_POST}/bin/sha1sums.txt fi ================================================ FILE: cross_build_mingw64.sh ================================================ #!/bin/bash # requires debian/ubuntu packages: zip gcc-mingw-w64 REPO_DIR=$(pwd) if [ -z "$1" ]; then echo "usage: $0 " exit 1 fi ZIP_POST="$1" shift CROSS="x86_64-w64-mingw32" WN="w64" TOOLCHAIN="mingw-w64-x64_64.cmake" # libusb if /bin/true; then cd ${REPO_DIR} && rm -rf libusb_${WN} cd ${REPO_DIR} && git clone --branch v1.0.23 https://github.com/libusb/libusb.git libusb_${WN} echo -e "\n\n********************************************************" echo "start build of libusb_${WN}" cd ${REPO_DIR}/libusb_${WN} && ./bootstrap.sh && \ CC=${CROSS}-gcc \ AR=${CROSS}-ar \ RANLIB=${CROSS}-ranlib \ ./configure --prefix=${REPO_DIR}/mingw_libusb_${WN} --host=${CROSS} --disable-shared && \ make && make install echo -e "\n\nlisting of ${REPO_DIR}/mingw_libusb_${WN}" ls -alh ${REPO_DIR}/mingw_libusb_${WN} echo -e "\nlisting of ${REPO_DIR}/mingw_libusb_${WN}/include" ls -alh ${REPO_DIR}/mingw_libusb_${WN}/include echo -e "\nlisting of ${REPO_DIR}/mingw_libusb_${WN}/lib" ls -alh ${REPO_DIR}/mingw_libusb_${WN}/lib echo -e "\n" fi # librtlsdr if /bin/true; then cd ${REPO_DIR} && rm -rf build_${WN} echo -e "\n\n********************************************************" echo "start build of librtlsdr_${WN}" mkdir ${REPO_DIR}/build_${WN} && cd ${REPO_DIR}/build_${WN} && \ cmake -DCMAKE_TOOLCHAIN_FILE=${REPO_DIR}/${TOOLCHAIN} \ -DCMAKE_INSTALL_PREFIX=${REPO_DIR}/rtlsdr-bin-${WN}_${ZIP_POST} \ -DRTL_STATIC_BUILD=ON "$@" \ -DLIBUSB_INCLUDE_DIR=${REPO_DIR}/mingw_libusb_${WN}/include/libusb-1.0 \ -DLIBUSB_LIBRARIES=${REPO_DIR}/mingw_libusb_${WN}/lib/libusb-1.0.a \ ../ && \ make && make install md5sum ${REPO_DIR}/rtlsdr-bin-${WN}_${ZIP_POST}/bin/* >${REPO_DIR}/rtlsdr-bin-${WN}_${ZIP_POST}/bin/md5sums.txt sha1sum ${REPO_DIR}/rtlsdr-bin-${WN}_${ZIP_POST}/bin/* >${REPO_DIR}/rtlsdr-bin-${WN}_${ZIP_POST}/bin/sha1sums.txt fi ================================================ FILE: debian/.gitignore ================================================ *.deb ================================================ FILE: debian/README.Debian ================================================ rtl-sdr for Debian ------------------- In the beginning Antti Palosaari noticed that some digital video receiver tuners can be turned into a cheap software defined radio. Since there is also support in the Linux kernel to use these devices as digital video receivers, by default the hardware will be claimed by Linux keernel drivers for that purpose. Having these rtl-sdr packages installed likely means that these devices should be available for the alternate software defined radio use. The librtlsdr0 package in Debian has configuration files to help manage the conflicting uses: 1. Blacklists DVB-T kernel modules provided by the Linux kernel ------------------------------------------------------------------- Config file: /etc/modprobe.d/librtlsdr-blacklist.conf contains lines to blacklist dvb_usb_rtl28xxu, e4000 and rtl2832 kernel modules. Should you wish to use a device via the Linux video receiver software while still having the librtlsdr0 package installed you may edit this file. (Placing a # at the beginning os a line makes it a comment.) Then unplug/plug the USB stick. Not that if rtl-sdr applications are then run, they will complain about failing to open the device. In that case, restore the blacklist and unplug/plug the USB stick. If librtlsdr-blacklist.conf does not exist, then rtl-sdr was built with the DETACH_KERNEL_DRIVER option. 2. Permissions -------------- Devices are available to users in the plugdev group. The librtlsdr0 package installs these default rules: /lib/udev/rules.d/60-librtlsdr0.rules If you have permissions issues, you may override these values with your own rules in /etc: /etc/udev/rules.d/60-librtlsdr0.rules After editing udev rules, run as root: udevadm control --reload-rules ================================================ FILE: debian/changelog ================================================ rtl-sdr (0.7git) unstable; urgency=medium * Accumulated changes from librtlsdr community -- Karl Semich <0xloem@gmail.com> Wed, 03 Oct 2018 15:09:44 +0000 rtl-sdr (0.6git) unstable; urgency=medium * New upstream release -- Harald Welte Sun, 06 Jun 2018 15:09:42 +0200 rtl-sdr (0.5.4-1) unstable; urgency=medium * New upstream release * update to v0.5.4-3-ga854ae8 use USB zero-copy transfers if possible -- A. Maitland Bottoms Sat, 12 May 2018 16:49:43 -0400 rtl-sdr (0.5.3-14) unstable; urgency=medium * update to v0.5.3-20-g4520f00 (Closes: #892974) * minimal ipv6 support (Closes: #870804) * VCS to salsa * AppStream metadata.xml -- A. Maitland Bottoms Mon, 16 Apr 2018 20:45:53 -0400 rtl-sdr (0.5.3-13) unstable; urgency=medium * build with libusb-1.0-0-dev stub on hurd-i386 * initial ipv6 support for rtl_tcp... -- A. Maitland Bottoms Thu, 23 Nov 2017 15:59:40 -0500 rtl-sdr (0.5.3-12) unstable; urgency=medium * add new HanfTek dongle * Bias T support (Closes: #854378, #842249) -- A. Maitland Bottoms Wed, 23 Aug 2017 23:31:27 -0400 rtl-sdr (0.5.3-11) unstable; urgency=medium * correct invocation of rm_conffile (Thanks Chris!) (Closes: #838161) * drop uploaders line on advice of MIA team. (Closes: #836590) -- A. Maitland Bottoms Sat, 08 Oct 2016 11:17:47 -0400 rtl-sdr (0.5.3-10) unstable; urgency=medium * remove rtl-sdr-blacklist.conf on upgrade. Thanks Bob! (Closes: #829517) -- A. Maitland Bottoms Sat, 09 Jul 2016 23:38:24 -0400 rtl-sdr (0.5.3-9) unstable; urgency=medium * Edit of debian/librtlsdr0.udev in 0.5.3-8 was a no-op. Real fix done to debian/patches/use-udev-uaccess-rules. (Closes: #825073) -- A. Maitland Bottoms Wed, 25 May 2016 17:19:57 -0400 rtl-sdr (0.5.3-8) unstable; urgency=high * Fix syntax errors for systemd-udevd in udev rules (Closes: #825073) -- A. Maitland Bottoms Tue, 24 May 2016 21:08:50 -0400 rtl-sdr (0.5.3-7) unstable; urgency=medium * better udev rules (more like camera devices in libgphoto2-6) -- A. Maitland Bottoms Tue, 10 May 2016 19:20:27 -0400 rtl-sdr (0.5.3-6) unstable; urgency=medium * Use ENV{ID_SOFTWARE_RADIO}=1 in udev rules (Closes: #823089) * Enable DETACH_KERNEL_DRIVER (Closes: 823022) * Make myself maintainer so I get the bug reports -- A. Maitland Bottoms Sun, 08 May 2016 12:12:13 -0400 rtl-sdr (0.5.3-5) unstable; urgency=medium * Add watch fiule * place rtl-sdr -n comm section (Closes: #758077) * improve-librtlsdr-pc-file (Closes: #784912) * improve-scanning-range-parsing (LP: #1469478) -- A. Maitland Bottoms Sun, 23 Aug 2015 10:35:42 -0400 rtl-sdr (0.5.3-4) unstable; urgency=low * Update to v0.5.3-12-ge3c03f7 -- A. Maitland Bottoms Sat, 08 Aug 2015 23:43:54 -0400 rtl-sdr (0.5.3-3) unstable; urgency=low * Update to v0.5.3-5-g6ee5573 lib: handle events after canceling transfers lib: change default number of transfers to 15 rtl_tcp: make all global variables static -- A. Maitland Bottoms Sun, 13 Apr 2014 10:48:49 -0400 rtl-sdr (0.5.3-2) unstable; urgency=low * Upstream: lib: only print to stderr in tuner_r82xx_set_pll() * Update man pages (New -M (modulation) and -E (option) and ppm setting) * Have librtlsdr0 also install a blacklist for linux video drivers -- A. Maitland Bottoms Sat, 08 Feb 2014 22:40:06 -0500 rtl-sdr (0.5.3-1) unstable; urgency=low * New upstream git tag release -- A. Maitland Bottoms Thu, 06 Feb 2014 20:45:38 -0500 rtl-sdr (0.5.2.7.3ab6-1~bpo70+1) wheezy-backports; urgency=low * Rebuild for wheezy-backports. -- A. Maitland Bottoms Tue, 21 Jan 2014 19:34:16 -0500 rtl-sdr (0.5.2.7.3ab6-1) unstable; urgency=low * New upstream snapshot -- A. Maitland Bottoms Sun, 29 Dec 2013 21:37:19 -0500 rtl-sdr (0.5.1.14.360d-1~wheezy) stable; urgency=low * New upstream snapshot * GNU Radio LiveDVD 2013-1110 -- A. Maitland Bottoms Mon, 11 Nov 2013 12:46:00 -0500 rtl-sdr (0.5.0.4.4914-2) unstable; urgency=low * Use kfreebsd libusb -- A. Maitland Bottoms Fri, 01 Nov 2013 17:16:42 -0400 rtl-sdr (0.5.0.4.4914-1) stable; urgency=low * New upstream snapshot (Closes: #701018). * Match GNU Radio live distribution version * Sponsored upload -- A. Maitland Bottoms Sat, 28 Sep 2013 16:55:08 -0400 rtl-sdr (0.5.0+git20130715-1) unstable; urgency=low * Initial release (Closes: #701018). -- Adam Cécile (Le_Vert) Mon, 15 Jul 2013 15:51:05 +0200 librtlsdr (0.0git3198f14-1) unstable; urgency=low * New upstream git -- A. Maitland Bottoms Mon, 14 May 2012 20:28:18 -0400 ================================================ FILE: debian/compat ================================================ 9 ================================================ FILE: debian/control ================================================ Source: rtl-sdr Section: comm Priority: optional Maintainer: A. Maitland Bottoms Build-Depends: cmake, debhelper (>= 9.0.0~), libusb-1.0-0-dev [linux-any], libusb-dev [hurd-i386], libusb2-dev [kfreebsd-any] Standards-Version: 4.1.4 Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr Vcs-Browser: https://salsa.debian.org/bottoms/pkg-rtl-sdr Vcs-Git: https://salsa.debian.org/bottoms/pkg-rtl-sdr.git Package: librtlsdr-dev Section: libdevel Architecture: any Pre-Depends: ${misc:Pre-Depends} Depends: librtlsdr0 (= ${binary:Version}), libusb-1.0-0-dev [!kfreebsd-any], libusb2-dev [kfreebsd-any], ${misc:Depends} Description: Software defined radio receiver for Realtek RTL2832U (development) rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. . This package contains development files. Package: librtlsdr0 Section: libs Architecture: any Pre-Depends: ${misc:Pre-Depends} Depends: ${misc:Depends}, ${shlibs:Depends} Multi-Arch: same Description: Software defined radio receiver for Realtek RTL2832U (library) rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. . This package contains the shared library. Package: rtl-sdr Architecture: any Depends: librtlsdr0 (= ${binary:Version}), ${misc:Depends}, ${shlibs:Depends} Description: Software defined radio receiver for Realtek RTL2832U (tools) rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. . This package contains a set of command line utilities: * rtl_adsb: a simple ADS-B decoder for RTL2832 based DVB-T receivers * rtl_eeprom: an EEPROM programming tool for RTL2832 based DVB-T receivers * rtl_fm: a narrow band FM demodulator for RTL2832 based DVB-T receivers * rtl_sdr: an I/Q recorder for RTL2832 based DVB-T receivers * rtl_tcp: an I/Q spectrum server for RTL2832 based DVB-T receivers * rtl_test: a benchmark tool for RTL2832 based DVB-T receivers ================================================ FILE: debian/copyright ================================================ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: rtl-sdr Upstream-Contact: http://sdr.osmocom.org/trac/wiki/rtl-sdr Source: git clone git://git.osmocom.org/rtl-sdr.git The upstream package source tarball was generated from the tag: git archive --format=tar --prefix=rtl-sdr-0.5.2.7.3ab6/ 3ab6ff | gzip > ../rtl-sdr_0.5.2.7.3ab6.orig.tar.gz Comment: Debian packages sponsored by A. Maitland Bottoms, based upon packaging work by Adam Cécile. . Upstream Authors: Steve Markgraf Dimitri Stolnikov Hoernchen Kyle Keen Copyright: 2012,2013 OSMOCOM Project License: GPL-2.0+ Files: * Copyright: 2012, 2013, OSMOCOM Project License: GPL-3+ Files: debian/* Copyright: 2013 Adam Cécile (Le_Vert) 2012,2013 A. Maitland Bottoms License: GPL-2.0+ Files: debian/librtlsdr0.udev Copyright: 2012, 2013, Osmocom rtl-sdr project License: GPL-3+ Files: debian/rtl_adsb.1 Copyright: Copyright (c) 2013, A. Maitland Bottoms License: GPL-2.0+ Files: debian/rtl_eeprom.1 debian/rtl_test.1 Copyright: Copyright (c) 2013, A. Maitland Bottoms License: GPL-2+ Files: git-version-gen Copyright: 2007-2010, Free Software Foundation, Inc. License: GPL-3+ Files: include/* Copyright: 2012, Steve Markgraf 2012, Hans-Frieder Vogt License: GPL-2+ Files: include/CMakeLists.txt Copyright: 2012, 2013, OSMOCOM Project License: GPL-3+ Files: include/rtl-sdr.h Copyright: 2012, Dimitri Stolnikov 2012, 2013, Steve Markgraf License: GPL-2+ Files: include/rtl-sdr_export.h Copyright: 2012, Hoernchen License: GPL-2+ Files: include/tuner_e4k.h Copyright: 2012, Sylvain Munaut 2012, Hoernchen 2011, 2012, Harald Welte License: GPL-2+ Files: include/tuner_fc2580.h Copyright: Steve Markgraf License: GPL-2.0+ Files: include/tuner_r82xx.h Copyright: 2013, Steve Markgraf 2013, Mauro Carvalho Chehab License: GPL-2+ Files: rtl-sdr.rules Copyright: 2012, 2013, Osmocom rtl-sdr project License: GPL-3+ Files: src/* Copyright: 2012, Steve Markgraf License: GPL-2+ Files: src/CMakeLists.txt Copyright: 2012, 2013, OSMOCOM Project License: GPL-3+ Files: src/Makefile.am Copyright: 2012 Steve Markgraf 2012 Dimitri Stolnikov License: GPL-2.0+ Files: src/convenience/* Copyright: 2014, Kyle Keen License: GPL-2+ Files: src/getopt/* Copyright: 88-96, 98, 99, 1987, 2000, 2001 License: LGPL-2.1+ Files: src/getopt/getopt.h Copyright: 1989-1994, 1996-1999, 2001, Free Software Foundation, Inc. License: LGPL-2.1+ Files: src/librtlsdr.c Copyright: 2012-2014, Steve Markgraf 2012, Dimitri Stolnikov License: GPL-2+ Files: src/rtl_adsb.c Copyright: 2012, Youssef Touil 2012, Steve Markgraf 2012, Kyle Keen 2012, Ian Gilmour 2012, Hoernchen License: GPL-2+ Files: src/rtl_fm.c Copyright: 2013, Elias Oenal 2012, Steve Markgraf 2012, Kyle Keen 2012, Hoernchen License: GPL-2+ Files: src/rtl_power.c Copyright: 2012, Steve Markgraf 2012, Kyle Keen 2012, Hoernchen License: GPL-2+ Files: src/rtl_tcp.c Copyright: 2012, Steve Markgraf 2012, 2013, Hoernchen License: GPL-2+ Files: src/rtl_test.c Copyright: 2014, Michael Tatarinov 2012-2014, Steve Markgraf 2012-2014, Kyle Keen License: GPL-2+ Files: src/tuner_e4k.c Copyright: 2012, Sylvain Munaut 2012, Hoernchen 2011, 2012, Harald Welte License: GPL-2+ Files: src/tuner_fc0012.c Copyright: 2012, Steve Markgraf 2012, Hans-Frieder Vogt License: GPL-2+ Files: src/tuner_fc0013.c Copyright: 2012, Steve Markgraf 2012, Hans-Frieder Vogt 2010, Fitipower Integrated Technology Inc License: GPL-2+ Files: src/tuner_fc2580.c Copyright: Steve Markgraf Dimitri Stolnikov License: GPL-2.0+ Files: src/tuner_r82xx.c Copyright: 2013, Steve Markgraf 2013, Mauro Carvalho Chehab License: GPL-2+ License: GPL-2+ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; version 2 dated June, 1991, or (at your option) any later version. . On Debian systems, the complete text of version 2 of the GNU General Public License can be found in '/usr/share/common-licenses/GPL-2'. License: GPL-2.0+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. . This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . On Debian systems, the complete text of the GNU General Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". License: GPL-3+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. . This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . On Debian systems, the complete text of the GNU Lesser General Public License can be found in "/usr/share/common-licenses/GPL-3". License: LGPL-2.1+ This package is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. . This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . On Debian systems, the complete text of the GNU Lesser General Public License can be found in "/usr/share/common-licenses/LGPL-2". ================================================ FILE: debian/copyright-scan-patterns.yml ================================================ ignore : suffixes : - in ================================================ FILE: debian/debianize_armhf ================================================ #!/bin/bash REPO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/../" G_REV=`git rev-parse --short=8 HEAD` DATE=`date +"%Y%m%d%H%M%S"` #VERSION="0.5.3-git+${DATE}.${G_REV}~$1" VERSION=`git describe | cut -dv -f2` # # librtlsdr0 # rm -fr /tmp/librtlsdr0/ mkdir -p /tmp/librtlsdr0/ mkdir -p /tmp/librtlsdr0/usr/lib/arm-linux-gnueabihf/ mkdir -p /tmp/librtlsdr0/DEBIAN cat <<- EOF > /tmp/librtlsdr0/DEBIAN/control Package: librtlsdr0 Source: rtl-sdr Version: ${VERSION} Architecture: armhf Maintainer: Lucas Teske Pre-Depends: multiarch-support Depends: libc6 (>= 2.14), libusb-1.0-0 (>= 2:1.0.9) Section: libs Priority: extra Multi-Arch: same Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr Description: Software defined radio receiver for Realtek RTL2832U (library) rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. . This package contains the shared library. EOF DEB_PKG="librtlsdr0_${VERSION}_armhf.deb" cp -rf ${REPO_DIR}/build/src/lib*so* /tmp/librtlsdr0/usr/lib/arm-linux-gnueabihf/ dpkg-deb -b /tmp/librtlsdr0/ ./${DEB_PKG} echo ${DEB_PKG} # # rtl-sdr # rm -fr /tmp/rtl-sdr/ mkdir -p /tmp/rtl-sdr/ mkdir -p /tmp/rtl-sdr/usr/bin/ mkdir -p /tmp/rtl-sdr/DEBIAN cat <<- EOF > /tmp/rtl-sdr/DEBIAN/control Package: rtl-sdr Version: ${VERSION} Architecture: armhf Maintainer: Lucas Teske Depends: librtlsdr0 (= ${VERSION}), libc6 (>= 2.15) Section: libs Priority: extra Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr Description: Software defined radio receiver for Realtek RTL2832U (tools) rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. . This package contains a set of command line utilities: * rtl_adsb: a simple ADS-B decoder for RTL2832 based DVB-T receivers * rtl_eeprom: an EEPROM programming tool for RTL2832 based DVB-T receivers * rtl_fm: a narrow band FM demodulator for RTL2832 based DVB-T receivers * rtl_sdr: an I/Q recorder for RTL2832 based DVB-T receivers * rtl_tcp: an I/Q spectrum server for RTL2832 based DVB-T receivers * rtl_test: a benchmark tool for RTL2832 based DVB-T receivers EOF DEB_PKG="rtl-sdr_${VERSION}_armhf.deb" cp -rf ${REPO_DIR}/build/src/rtl_* /tmp/rtl-sdr/usr/bin/ dpkg-deb -b /tmp/rtl-sdr/ ./${DEB_PKG} echo ${DEB_PKG} # # librtlsdr-dev # rm -fr /tmp/librtlsdr-dev/ mkdir -p /tmp/librtlsdr-dev/ mkdir -p /tmp/librtlsdr-dev/usr/include mkdir -p /tmp/librtlsdr-dev/usr/lib/pkgconfig mkdir -p /tmp/librtlsdr-dev/DEBIAN cat <<- EOF > /tmp/librtlsdr-dev/DEBIAN/control Package: librtlsdr-dev Source: rtl-sdr Version: ${VERSION} Architecture: armhf Maintainer: Lucas Teske Pre-Depends: multiarch-support Depends: librtlsdr0 (= ${VERSION}) Section: libdevel Priority: extra Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr Description: Software defined radio receiver for Realtek RTL2832U (development files) rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. . This package contains development files. EOF DEB_PKG="librtlsdr-dev_${VERSION}_armhf.deb" cp -rf ${REPO_DIR}/include/*.h /tmp/librtlsdr-dev/usr/include cp ${REPO_DIR}/build/librtlsdr.pc /tmp/librtlsdr-dev/usr/lib/pkgconfig/ dpkg-deb -b /tmp/librtlsdr-dev/ ./${DEB_PKG} echo ${DEB_PKG} ================================================ FILE: debian/debianize_x32 ================================================ #!/bin/bash REPO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/../" G_REV=`git rev-parse --short=8 HEAD` DATE=`date +"%Y%m%d%H%M%S"` #VERSION="0.5.3-git+${DATE}.${G_REV}~$1" VERSION=`git describe | cut -dv -f2` # # librtlsdr0 # rm -fr /tmp/librtlsdr0/ mkdir -p /tmp/librtlsdr0/ mkdir -p /tmp/librtlsdr0/usr/lib/i386-linux-gnu/ mkdir -p /tmp/librtlsdr0/DEBIAN cat <<- EOF > /tmp/librtlsdr0/DEBIAN/control Package: librtlsdr0 Source: rtl-sdr Version: ${VERSION} Architecture: i386 Maintainer: Lucas Teske Pre-Depends: multiarch-support Depends: libc6 (>= 2.14), libusb-1.0-0 (>= 2:1.0.9) Section: libs Priority: extra Multi-Arch: same Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr Description: Software defined radio receiver for Realtek RTL2832U (library) rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. . This package contains the shared library. EOF DEB_PKG="librtlsdr0_${VERSION}_i386.deb" cp -rf ${REPO_DIR}/build/src/lib*so* /tmp/librtlsdr0/usr/lib/i386-linux-gnu/ dpkg-deb -b /tmp/librtlsdr0/ ./${DEB_PKG} echo ${DEB_PKG} # # rtl-sdr # rm -fr /tmp/rtl-sdr/ mkdir -p /tmp/rtl-sdr/ mkdir -p /tmp/rtl-sdr/usr/bin/ mkdir -p /tmp/rtl-sdr/DEBIAN cat <<- EOF > /tmp/rtl-sdr/DEBIAN/control Package: rtl-sdr Version: ${VERSION} Architecture: i386 Maintainer: Lucas Teske Depends: librtlsdr0 (= ${VERSION}), libc6 (>= 2.15) Section: libs Priority: extra Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr Description: Software defined radio receiver for Realtek RTL2832U (tools) rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. . This package contains a set of command line utilities: * rtl_adsb: a simple ADS-B decoder for RTL2832 based DVB-T receivers * rtl_eeprom: an EEPROM programming tool for RTL2832 based DVB-T receivers * rtl_fm: a narrow band FM demodulator for RTL2832 based DVB-T receivers * rtl_sdr: an I/Q recorder for RTL2832 based DVB-T receivers * rtl_tcp: an I/Q spectrum server for RTL2832 based DVB-T receivers * rtl_test: a benchmark tool for RTL2832 based DVB-T receivers EOF DEB_PKG="rtl-sdr_${VERSION}_i386.deb" cp -rf ${REPO_DIR}/build/src/rtl_* /tmp/rtl-sdr/usr/bin/ dpkg-deb -b /tmp/rtl-sdr/ ./${DEB_PKG} echo ${DEB_PKG} # # librtlsdr-dev # rm -fr /tmp/librtlsdr-dev/ mkdir -p /tmp/librtlsdr-dev/ mkdir -p /tmp/librtlsdr-dev/usr/include mkdir -p /tmp/librtlsdr-dev/usr/lib/pkgconfig mkdir -p /tmp/librtlsdr-dev/DEBIAN cat <<- EOF > /tmp/librtlsdr-dev/DEBIAN/control Package: librtlsdr-dev Source: rtl-sdr Version: ${VERSION} Architecture: i386 Maintainer: Lucas Teske Pre-Depends: multiarch-support Depends: librtlsdr0 (= ${VERSION}) Section: libdevel Priority: extra Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr Description: Software defined radio receiver for Realtek RTL2832U (development files) rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. . This package contains development files. EOF DEB_PKG="librtlsdr-dev_${VERSION}_i386.deb" cp -rf ${REPO_DIR}/include/*.h /tmp/librtlsdr-dev/usr/include cp ${REPO_DIR}/build/librtlsdr.pc /tmp/librtlsdr-dev/usr/lib/pkgconfig/ dpkg-deb -b /tmp/librtlsdr-dev/ ./${DEB_PKG} echo ${DEB_PKG} ================================================ FILE: debian/debianize_x64 ================================================ #!/bin/bash REPO_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/../" G_REV=`git rev-parse --short=8 HEAD` DATE=`date +"%Y%m%d%H%M%S"` #VERSION="0.5.3-git+${DATE}.${G_REV}~$1" VERSION=`git describe | cut -dv -f2` # # librtlsdr0 # rm -fr /tmp/librtlsdr0/ mkdir -p /tmp/librtlsdr0/ mkdir -p /tmp/librtlsdr0/usr/lib/x86_64-linux-gnu/ mkdir -p /tmp/librtlsdr0/DEBIAN cat <<- EOF > /tmp/librtlsdr0/DEBIAN/control Package: librtlsdr0 Source: rtl-sdr Version: ${VERSION} Architecture: amd64 Maintainer: Lucas Teske Pre-Depends: multiarch-support Depends: libc6 (>= 2.14), libusb-1.0-0 (>= 2:1.0.9) Section: libs Priority: extra Multi-Arch: same Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr Description: Software defined radio receiver for Realtek RTL2832U (library) rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. . This package contains the shared library. EOF DEB_PKG="librtlsdr0_${VERSION}_amd64.deb" cp -rf ${REPO_DIR}/build/src/lib*so* /tmp/librtlsdr0/usr/lib/x86_64-linux-gnu/ dpkg-deb -b /tmp/librtlsdr0/ ./${DEB_PKG} echo ${DEB_PKG} # # rtl-sdr # rm -fr /tmp/rtl-sdr/ mkdir -p /tmp/rtl-sdr/ mkdir -p /tmp/rtl-sdr/usr/bin/ mkdir -p /tmp/rtl-sdr/DEBIAN cat <<- EOF > /tmp/rtl-sdr/DEBIAN/control Package: rtl-sdr Version: ${VERSION} Architecture: amd64 Maintainer: Lucas Teske Depends: librtlsdr0 (= ${VERSION}), libc6 (>= 2.15) Section: libs Priority: extra Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr Description: Software defined radio receiver for Realtek RTL2832U (tools) rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. . This package contains a set of command line utilities: * rtl_adsb: a simple ADS-B decoder for RTL2832 based DVB-T receivers * rtl_eeprom: an EEPROM programming tool for RTL2832 based DVB-T receivers * rtl_fm: a narrow band FM demodulator for RTL2832 based DVB-T receivers * rtl_sdr: an I/Q recorder for RTL2832 based DVB-T receivers * rtl_tcp: an I/Q spectrum server for RTL2832 based DVB-T receivers * rtl_test: a benchmark tool for RTL2832 based DVB-T receivers EOF DEB_PKG="rtl-sdr_${VERSION}_amd64.deb" cp -rf ${REPO_DIR}/build/src/rtl_* /tmp/rtl-sdr/usr/bin/ dpkg-deb -b /tmp/rtl-sdr/ ./${DEB_PKG} echo ${DEB_PKG} # # librtlsdr-dev # rm -fr /tmp/librtlsdr-dev/ mkdir -p /tmp/librtlsdr-dev/ mkdir -p /tmp/librtlsdr-dev/usr/include mkdir -p /tmp/librtlsdr-dev/usr/lib/pkgconfig mkdir -p /tmp/librtlsdr-dev/DEBIAN cat <<- EOF > /tmp/librtlsdr-dev/DEBIAN/control Package: librtlsdr-dev Source: rtl-sdr Version: ${VERSION} Architecture: amd64 Maintainer: Lucas Teske Pre-Depends: multiarch-support Depends: librtlsdr0 (= ${VERSION}) Section: libdevel Priority: extra Homepage: http://sdr.osmocom.org/trac/wiki/rtl-sdr Description: Software defined radio receiver for Realtek RTL2832U (development files) rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip. . This package contains development files. EOF DEB_PKG="librtlsdr-dev_${VERSION}_amd64.deb" cp -rf ${REPO_DIR}/include/*.h /tmp/librtlsdr-dev/usr/include cp ${REPO_DIR}/build/librtlsdr.pc /tmp/librtlsdr-dev/usr/lib/pkgconfig/ dpkg-deb -b /tmp/librtlsdr-dev/ ./${DEB_PKG} echo ${DEB_PKG} ================================================ FILE: debian/heatmap.py ================================================ #! /usr/bin/env python2 from PIL import Image, ImageDraw, ImageFont import sys, gzip, math, colorsys, datetime from collections import defaultdict from itertools import * # todo: matplotlib powered --interactive # arbitrary freq marker spacing path = sys.argv[1] output = sys.argv[2] raw_data = lambda: open(path) if path.endswith('.gz'): raw_data = lambda: gzip.open(path, 'rb') def frange(start, stop, step): i = 0 while (i*step + start <= stop): yield i*step + start i += 1 print("loading") freqs = set() f_cache = set() times = set() labels = set() min_z = 0 max_z = -100 start, stop = None, None for line in raw_data(): line = [s.strip() for s in line.strip().split(',')] line = [line[0], line[1]] + [float(s) for s in line[2:] if s] low = line[2] high = line[3] step = line[4] f_key = (int(low), int(high), step) if f_key not in f_cache: freqs.update(list(frange(int(low), int(high), step))) freqs.add(high) labels.add(low) f_cache.add(f_key) t = line[0] + ' ' + line[1] times.add(t) zs = line[6:] min_z = min(min_z, min(z for z in zs if not math.isinf(z))) max_z = max(max_z, max(zs)) if start is None: start = datetime.datetime.strptime(line[0] + ' ' + line[1], '%Y-%m-%d %H:%M:%S') stop = datetime.datetime.strptime(line[0] + ' ' + line[1], '%Y-%m-%d %H:%M:%S') freqs = list(sorted(list(freqs))) times = list(sorted(list(times))) labels = list(sorted(list(labels))) if len(labels) == 1: delta = (max(freqs) - min(freqs)) / (len(freqs) / 500) delta = round(delta / 10**int(math.log10(delta))) * 10**int(math.log10(delta)) delta = int(delta) lower = int(math.ceil(min(freqs) / delta) * delta) labels = list(range(lower, int(max(freqs)), delta)) print("x: %i, y: %i, z: (%f, %f)" % (len(freqs), len(times), min_z, max_z)) def rgb2(z): g = (z - min_z) / (max_z - min_z) return (int(g*255), int(g*255), 50) def rgb3(z): g = (z - min_z) / (max_z - min_z) c = colorsys.hsv_to_rgb(0.65-(g-0.08), 1, 0.2+g) return (int(c[0]*256),int(c[1]*256),int(c[2]*256)) print("drawing") img = Image.new("RGB", (len(freqs), len(times))) pix = img.load() x_size = img.size[0] for line in raw_data(): line = [s.strip() for s in line.strip().split(',')] line = [line[0], line[1]] + [float(s) for s in line[2:] if s] t = line[0] + ' ' + line[1] if t not in times: continue # happens with live files y = times.index(t) low = line[2] high = line[3] step = line[4] x_start = freqs.index(low) for i in range(len(line[6:])): x = x_start + i if x >= x_size: continue z = line[6+i] # fast check for nan/-inf if not z >= min_z: z = min_z pix[x,y] = rgb2(z) print("labeling") draw = ImageDraw.Draw(img) font = ImageFont.load_default() pixel_width = step for label in labels: y = 10 #x = freqs.index(label) x = int((label-min(freqs)) / pixel_width) s = '%.3fMHz' % (label/1000000.0) draw.text((x, y), s, font=font, fill='white') duration = stop - start duration = duration.seconds pixel_height = duration / len(times) hours = int(duration / 3600) minutes = int((duration - 3600*hours) / 60) draw.text((2, img.size[1] - 45), 'Duration: %i:%02i' % (hours, minutes), font=font, fill='white') draw.text((2, img.size[1] - 35), 'Range: %.2fMHz - %.2fMHz' % (min(freqs)/1e6, max(freqs)/1e6), font=font, fill='white') draw.text((2, img.size[1] - 25), 'Pixel: %.2fHz x %is' % (pixel_width, int(round(pixel_height))), font=font, fill='white') draw.text((2, img.size[1] - 15), 'Started: {0}'.format(start), font=font, fill='white') # bin size print("saving") img.save(output) ================================================ FILE: debian/librtlsdr-dev.dirs ================================================ usr/lib usr/include ================================================ FILE: debian/librtlsdr-dev.install ================================================ usr/include/* usr/lib/*/lib*.a usr/lib/*/lib*.so usr/lib/*/pkgconfig/* ================================================ FILE: debian/librtlsdr0.dirs ================================================ etc/modprobe.d usr/lib ================================================ FILE: debian/librtlsdr0.install ================================================ usr/lib/*/lib*.so.* debian/librtlsdr0.metainfo.xml usr/share/metainfo ================================================ FILE: debian/librtlsdr0.maintscript ================================================ rm_conffile /etc/modprobe.d/rtl-sdr-blacklist.conf 0.5.3-10~ ================================================ FILE: debian/librtlsdr0.metainfo.xml ================================================ librtlsdr0 GPL-2+ librtlsdr0 Control of rtl-sdr radio receiver

rtl-sdr is a software defined radio (SDR) receiver software for certain low-cost DVB-T/DAB(+) USB dongles based on the Realtek RTL2832U chip.

usb:v0BDAp2832d* usb:v0BDAp2838d* usb:v0413p6680d* usb:v0413p6F0Fd* usb:v0458p707Fd* usb:v0CCDp00A9d* usb:v0CCDp00B3d* usb:v0CCDp00B4d* usb:v0CCDp00B5d* usb:v0CCDp00B7d* usb:v0CCDp00B8d* usb:v0CCDp00B9d* usb:v0CCDp00C0d* usb:v0CCDp00C6d* usb:v0CCDp00D3d* usb:v0CCDp00D7d* usb:v0CCDp00E0d* usb:v1554p5020d* usb:v15F4p0131d* usb:v15F4p0133d* usb:v185Bp0620d* usb:v185Bp0650d* usb:v185Bp0680d* usb:v1B80pD393d* usb:v1B80pD394d* usb:v1B80pD395d* usb:v1B80pD397d* usb:v1B80pD398d* usb:v1B80pD39Dd* usb:v1B80pD3A4d* usb:v1B80pD3A8d* usb:v1B80pD3AFd* usb:v1B80pD3B0d* usb:v1D19p1101d* usb:v1D19p1102d* usb:v1D19p1103d* usb:v1D19p1104d* usb:v1F4DpA803d* usb:v1F4DpB803d* usb:v1F4DpC803d* usb:v1F4DpD286d* usb:v1F4DpD803d*
================================================ FILE: debian/librtlsdr0.udev ================================================ # # Copyright 2012-2013 Osmocom rtl-sdr project # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # original RTL2832U vid/pid (hama nano, for example) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2832", MODE:="0666" # RTL2832U OEM vid/pid, e.g. ezcap EzTV668 (E4000), Newsky TV28T (E4000/R820T) etc. SUBSYSTEMS=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838", MODE:="0666" # DigitalNow Quad DVB-T PCI-E card (4x FC0012?) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0413", ATTRS{idProduct}=="6680", MODE:="0666" # Leadtek WinFast DTV Dongle mini D (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0413", ATTRS{idProduct}=="6f0f", MODE:="0666" # Genius TVGo DVB-T03 USB dongle (Ver. B) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0458", ATTRS{idProduct}=="707f", MODE:="0666" # Terratec Cinergy T Stick Black (rev 1) (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00a9", MODE:="0666" # Terratec NOXON rev 1 (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b3", MODE:="0666" # Terratec Deutschlandradio DAB Stick (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b4", MODE:="0666" # Terratec NOXON DAB Stick - Radio Energy (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b5", MODE:="0666" # Terratec Media Broadcast DAB Stick (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b7", MODE:="0666" # Terratec BR DAB Stick (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b8", MODE:="0666" # Terratec WDR DAB Stick (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b9", MODE:="0666" # Terratec MuellerVerlag DAB Stick (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00c0", MODE:="0666" # Terratec Fraunhofer DAB Stick (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00c6", MODE:="0666" # Terratec Cinergy T Stick RC (Rev.3) (E4000) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00d3", MODE:="0666" # Terratec T Stick PLUS (E4000) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00d7", MODE:="0666" # Terratec NOXON rev 2 (E4000) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00e0", MODE:="0666" # PixelView PV-DT235U(RN) (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1554", ATTRS{idProduct}=="5020", MODE:="0666" # Astrometa DVB-T/DVB-T2 (R828D) SUBSYSTEMS=="usb", ATTRS{idVendor}=="15f4", ATTRS{idProduct}=="0131", MODE:="0666" # HanfTek DAB+FM+DVB-T SUBSYSTEMS=="usb", ATTRS{idVendor}=="15f4", ATTRS{idProduct}=="0133", MODE:="0666" # Compro Videomate U620F (E4000) SUBSYSTEMS=="usb", ATTRS{idVendor}=="185b", ATTRS{idProduct}=="0620", MODE:="0666" # Compro Videomate U650F (E4000) SUBSYSTEMS=="usb", ATTRS{idVendor}=="185b", ATTRS{idProduct}=="0650", MODE:="0666" # Compro Videomate U680F (E4000) SUBSYSTEMS=="usb", ATTRS{idVendor}=="185b", ATTRS{idProduct}=="0680", MODE:="0666" # GIGABYTE GT-U7300 (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d393", MODE:="0666" # DIKOM USB-DVBT HD SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d394", MODE:="0666" # Peak 102569AGPK (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d395", MODE:="0666" # KWorld KW-UB450-T USB DVB-T Pico TV (TUA9001) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d397", MODE:="0666" # Zaapa ZT-MINDVBZP (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d398", MODE:="0666" # SVEON STV20 DVB-T USB & FM (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d39d", MODE:="0666" # Twintech UT-40 (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d3a4", MODE:="0666" # ASUS U3100MINI_PLUS_V2 (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d3a8", MODE:="0666" # SVEON STV27 DVB-T USB & FM (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d3af", MODE:="0666" # SVEON STV21 DVB-T USB & FM SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d3b0", MODE:="0666" # Dexatek DK DVB-T Dongle (Logilink VG0002A) (FC2580) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d19", ATTRS{idProduct}=="1101", MODE:="0666" # Dexatek DK DVB-T Dongle (MSI DigiVox mini II V3.0) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d19", ATTRS{idProduct}=="1102", MODE:="0666" # Dexatek DK 5217 DVB-T Dongle (FC2580) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d19", ATTRS{idProduct}=="1103", MODE:="0666" # MSI DigiVox Micro HD (FC2580) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d19", ATTRS{idProduct}=="1104", MODE:="0666" # Sweex DVB-T USB (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1f4d", ATTRS{idProduct}=="a803", MODE:="0666" # GTek T803 (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1f4d", ATTRS{idProduct}=="b803", MODE:="0666" # Lifeview LV5TDeluxe (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1f4d", ATTRS{idProduct}=="c803", MODE:="0666" # MyGica TD312 (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1f4d", ATTRS{idProduct}=="d286", MODE:="0666" # PROlectrix DV107669 (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1f4d", ATTRS{idProduct}=="d803", MODE:="0666" ================================================ FILE: debian/rtl-sdr-blacklist.conf ================================================ # This system has librtlsdr0 installed in order to # use digital video broadcast receivers as generic # software defined radios. blacklist dvb_usb_rtl28xxu blacklist e4000 blacklist rtl2832 ================================================ FILE: debian/rtl-sdr.dirs ================================================ usr/bin ================================================ FILE: debian/rtl-sdr.examples ================================================ debian/heatmap.py ================================================ FILE: debian/rtl-sdr.install ================================================ usr/bin/* ================================================ FILE: debian/rtl-sdr.manpages ================================================ debian/rtl_adsb.1 debian/rtl_eeprom.1 debian/rtl_fm.1 debian/rtl_power.1 debian/rtl_sdr.1 debian/rtl_tcp.1 debian/rtl_test.1 ================================================ FILE: debian/rtl_adsb.1 ================================================ .TH "rtl_adsb" 1 "0.5.0" RTL-SDR "User Commands" .SH NAME rtl_adsb \- a simple ADS-B decoder .SH DESCRIPTION Uses a re-purposed DVB-T receiver as a software defined radio to receive and decode ADS-B data. Written by Kyle Keen and incorporated in the osmocom rtl-sdr project. .LP Automatic dependent surveillance-broadcast, ADS-B, consists of position and other status data transmitted by aircraft in support of air traffic control in order to improve safety of flight. .LP Much software is available for the RTL2832. Most of the user-level packages rely on the librtlsdr library which comes as part of the rtl-sdr codebase. This codebase contains both the library itself and also a number of command line tools such as rtl_test, rtl_sdr, rtl_tcp, and rtl_fm. These command line tools use the library to test for the existence of RTL2832 devices and to perform basic data transfer functions to and from the device. .LP Because most of the RTL2832 devices are connected using USB, the librtlsdr library depends on the libusb library to communicate with the device. .SH USAGE With a suitable antenna for receiving the 1090 MHz signal attached to the rtl-sdr supported device, this program will output the data decoded from that signal. .SH SYNOPSIS .B rtl_adsb [-R] [-g gain] [-p ppm] [output file] .SH OPTIONS .IP "-d device_index (default: 0)" .IP "-V verbove output (default: off)" .IP "-S show short frames (default: off)" .IP "-Q quality (0: no sanity checks, 0.5: half bit, 1: one bit (default), 2: two bits)" .IP "-e allowed_errors (default: 5)" .IP "-g tuner_gain (default: automatic)" .IP "-p ppm_error (default: 0)" .IP tfilename (a '-' dumps samples to stdout) (omitting the filename also uses stdout) .SH EXAMPLES .IP "Streaming with netcat:" rtl_adsb | netcat -lp 8080 while true; do rtl_adsb | nc -lp 8080; done .IP "Streaming with socat:" rtl_adsb | socat -u - TCP4:sdrsharp.com:47806 .SH SEE ALSO RTL-SDR wiki documentation: .B http://sdr.osmocom.org/trac/wiki/rtl-sdr .LP Other rtl-sdr programs: .sp rtl_eeprom(1), rtl_fm(1), rtl_sdr(1), rtl_tcp(1), rtl_test(1) .SH AUTHOR This manual page was written by Maitland Bottoms for the Debian project (but may be used by others). .SH COPYRIGHT Copyright (c) 2013 A. Maitland Bottoms .LP This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. .LP This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ================================================ FILE: debian/rtl_eeprom.1 ================================================ .TH "rtl_eeprom" 1 "0.5.0" RTL-SDR "User Commands" .SH NAME rtl-eeprom \- EEPROM programming tool for RTL2832 based DVB-T receivers .SH DESCRIPTION Dumps configuration and also writes EEPROM configuration. Written by Steve Markgraf and incorporated in the osmocom rtl-sdr project. .LP Use at your own risk, especially -w! .LP Much software is available for the RTL2832. Most of the user-level packages rely on the librtlsdr library which comes as part of the rtl-sdr codebase. This codebase contains both the library itself and also a number of command line tools such as rtl_test, rtl_sdr, rtl_tcp, and rtl_fm. These command line tools use the library to test for the existence of RTL2832 devices and to perform basic data transfer functions to and from the device. .LP Because most of the RTL2832 devices are connected using USB, the librtlsdr library depends on the libusb library to communicate with the device. .SH USAGE Writing bad information to the EEPROM will make your device useless. .SH SYNOPSIS .B rtl_eeprom [OPTIONS] .SH OPTIONS .IP "-d device_index (default: 0)" .IP "-m set manufacturer string" .IP "-p set product string" .IP "-s set serial number string" .IP "-i <0,1> disable/enable IR-endpoint" .IP "-g generate default config and write to device" can be one of: realtek Realtek default (as without EEPROM) realtek_oem Realtek default OEM with EEPROM noxon Terratec NOXON DAB Stick terratec_black Terratec T Stick Black terratec_plus Terratec T Stick+ (DVB-T/DAB) .IP "-w write dumped file to device" .IP "-r dump EEPROM to file" .IP "-h display this help text" .LP Use on your own risk, especially -w! .SH SEE ALSO RTL-SDR wiki documentation: .B http://sdr.osmocom.org/trac/wiki/rtl-sdr .LP Other rtl-sdr programs: .sp rtl_adsb(1), rtl_fm(1), rtl_sdr(1), rtl_tcp(1), rtl_test(1) .SH AUTHOR This manual page was written by Maitland Bottoms for the Debian project (but may be used by others). .SH COPYRIGHT Copyright (c) 2013 A. Maitland Bottoms .LP This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. .LP This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ================================================ FILE: debian/rtl_fm.1 ================================================ .TH "rtl_adsb" 1 "0.5.0" RTL-SDR "User Commands" .SH NAME rtl_fm \- a simple FM demodulator for RTL2832 based DVB-T receivers .SH DESCRIPTION Uses a re-purposed DVB-T receiver as a software defined radio to receive narrow band FM signals and demodulate to audio. Written for and incorporated in the osmocom rtl-sdr project. .LP Narrowband FM is commonly used by public service agencies and commercial dispatch operations in the VHF and UHF bands. Also can demodulate Wideband FM, as found in the 88-108 MHz FM broadcast band. Experimental options include AM, LSB, USB and DSB demodulation. .LP Much software is available for the RTL2832. Most of the user-level packages rely on the librtlsdr library which comes as part of the rtl-sdr codebase. This codebase contains both the library itself and also a number of command line tools such as rtl_test, rtl_sdr, rtl_tcp, and rtl_fm. These command line tools use the library to test for the existence of RTL2832 devices and to perform basic data transfer functions to and from the device. .LP Because most of the RTL2832 devices are connected using USB, the librtlsdr library depends on the libusb library to communicate with the device. .SH USAGE With a suitable antenna for receiving the signal attached to the rtl-sdr supported device, this program will output the digital audio data decoded from that signal. The data can be listened to by piping to Sox or aplay applications to play the stream on the computer sound card. .SH SYNOPSIS .B rtl_fm [-f freq] [-options] [filename] .SH OPTIONS .IP "-f frequency_to_tune_to [Hz]" use multiple -f for scanning, (requires squelch) ranges supported, -f 118M:137M:25k .IP "[-M modulation (default: fm)]" fm, wbfm, raw, am, usb, lsb wbfm == -M fm -s 170k -o 4 -A fast -r 32k -l 0 -E deemp raw mode outputs 2x16 bit IQ pairs .IP "-s sample_rate (default: 24k)" .IP "-d device_index (default: 0)" .IP "-g tuner_gain (default: automatic)" .IP "-l squelch_level (default: 0/off)" .IP "-o oversampling (default: 1, 4 recommended)" for fm squelch is inverted .IP "[-o oversampling (default: 1, 4 recommended)]" .IP "-p ppm_error (default: 0)" .IP "[-E enable_option (default: none)]" use multiple -E to enable multiple options edge: enable lower edge tuning dc: enable dc blocking filter deemp: enable de-emphasis filter direct: enable direct sampling offset: enable offset tuning .IP "filename ('-' means stdout)" omitting the filename also uses stdout .SH Experimental options .IP "[-r resample_rate (default: none / same as -s)]" .IP "[-t squelch_delay (default: 10)]" +values will mute/scan, -values will exit .IP "[-F fir_size (default: off)]" enables low-leakage downsample filter size can be 0 or 9. 0 has bad roll off .IP "-A std/fast/lut choose atan math (default: std)" .IP filename (a '-' dumps samples to stdout) (omitting the filename also uses stdout) .SH EXAMPLES Produces signed 16 bit ints, use Sox or aplay to hear them. .IP "rtl_fm ... - | play -t raw -r 24k -es -b 16 -c 1 -V1 -" | aplay -r 24k -f S16_LE -t raw -c 1 -M wbfm | play -r 32k ... .IP "rtl_fm ... -s 22050 - | multimon -t raw /dev/stdin" .SH SEE ALSO RTL-SDR wiki documentation: .B http://sdr.osmocom.org/trac/wiki/rtl-sdr .LP Rtl_fm Guide: .B http://kmkeen.com/rtl-demod-guide/ .LP .sp sox(1), play(1), aplay(1) .LP Other rtl-sdr programs: .sp rtl_adsb(1), rtl_eeprom(1), rtl_sdr(1), rtl_tcp(1), rtl_test(1) .SH AUTHOR This manual page was written by Maitland Bottoms for the Debian project (but may be used by others). .SH COPYRIGHT Copyright (c) 2013 A. Maitland Bottoms .LP This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. .LP This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ================================================ FILE: debian/rtl_power.1 ================================================ .TH rtl_power: "1" "0.5.1" RTL_SDR "User Commands" .SH NAME rtl_power: \- wideband spectrum monitor utility .SH DESCRIPTION Uses a re-purposed DVB-T receiver as a software defined radio to receive signals in I/Q data form. Written for and incorporated in the osmocom rtl-sdr project. .SH USAGE rtl_power, a simple FFT logger for RTL2832 based DVB\-T receivers .PP This tool gathers signal data over a very wide area of the frequency spectrum, and then that data can be used to find active areas of the spectrum. .PP Use: rtl_power \fB\-f\fR freq_range [\-options] [filename] .HP \fB\-f\fR lower:upper:bin_size [Hz] .IP (bin size is a maximum, smaller more convenient bins .TP will be used. valid range 1Hz \- 2.8MHz) .IP [\-i integration_interval (default: 10 seconds)] .IP (buggy if a full sweep takes longer than the interval) .IP [\-1 enables single\-shot mode (default: off)] [\-e exit_timer (default: off/0)] [\-d device_index (default: 0)] [\-g tuner_gain (default: automatic)] [\-p ppm_error (default: 0)] filename (a '\-' dumps samples to stdout) .IP (omitting the filename also uses stdout) .SS "Experimental options:" .IP [\-w window (default: rectangle)] .IP (hamming, blackman, blackman\-harris, hann\-poisson, bartlett, youssef) .IP [\-c crop_percent (default: 0%, recommended: 20%\-50%)] .IP (discards data at the edges, 100% discards everything) (has no effect for bins larger than 1MHz) .IP [\-F fir_size (default: disabled)] .IP (enables low\-leakage downsample filter, .TP fir_size can be 0 or 9. 0 has bad roll off, .IP try with '\-c 50%') .IP [\-P enables peak hold (default: off)] [\-D enable direct sampling (default: off)] [\-O enable offset tuning (default: off)] .SS "CSV FFT output columns:" .IP date, time, Hz low, Hz high, Hz step, samples, dbm, dbm, ... .SH EXAMPLES .IP rtl_power \fB\-f\fR 88M:108M:125k fm_stations.csv .IP (creates 160 bins across the FM band, .IP individual stations should be visible) .IP rtl_power \fB\-f\fR 100M:1G:1M \fB\-i\fR 5m \fB\-1\fR survey.csv .IP (a five minute low res scan of nearly everything) .IP rtl_power \fB\-f\fR ... \fB\-i\fR 15m \fB\-1\fR log.csv .IP (integrate for 15 minutes and exit afterwards) .IP rtl_power \fB\-f\fR ... \fB\-e\fR 1h | gzip > log.csv.gz .IP (collect data for one hour and compress it on the fly) .SS "Convert CSV to a waterfall graphic with:" .IP http://kmkeen.com/tmp/heatmap.py.txt .PP rtl_power, a simple FFT logger for RTL2832 based DVB\-T receivers .PP Use: rtl_power \fB\-f\fR freq_range [\-options] [filename] .HP \fB\-f\fR lower:upper:bin_size [Hz] .IP (bin size is a maximum, smaller more convenient bins .TP will be used. valid range 1Hz \- 2.8MHz) .IP [\-i integration_interval (default: 10 seconds)] .IP (buggy if a full sweep takes longer than the interval) .IP [\-1 enables single\-shot mode (default: off)] [\-e exit_timer (default: off/0)] [\-d device_index (default: 0)] [\-g tuner_gain (default: automatic)] [\-p ppm_error (default: 0)] filename (a '\-' dumps samples to stdout) .IP (omitting the filename also uses stdout) .SS "Experimental options:" .IP [\-w window (default: rectangle)] .IP (hamming, blackman, blackman\-harris, hann\-poisson, bartlett, youssef) .IP [\-c crop_percent (default: 0%, recommended: 20%\-50%)] .IP (discards data at the edges, 100% discards everything) (has no effect for bins larger than 1MHz) .IP [\-F fir_size (default: disabled)] .IP (enables low\-leakage downsample filter, .TP fir_size can be 0 or 9. 0 has bad roll off, .IP try with '\-c 50%') .IP [\-P enables peak hold (default: off)] [\-D enable direct sampling (default: off)] [\-O enable offset tuning (default: off)] .SS "CSV FFT output columns:" .IP date, time, Hz low, Hz high, Hz step, samples, dbm, dbm, ... .IP rtl_power \fB\-f\fR 88M:108M:125k fm_stations.csv .IP (creates 160 bins across the FM band, .IP individual stations should be visible) .IP rtl_power \fB\-f\fR 100M:1G:1M \fB\-i\fR 5m \fB\-1\fR survey.csv .IP (a five minute low res scan of nearly everything) .IP rtl_power \fB\-f\fR ... \fB\-i\fR 15m \fB\-1\fR log.csv .IP (integrate for 15 minutes and exit afterwards) .IP rtl_power \fB\-f\fR ... \fB\-e\fR 1h | gzip > log.csv.gz .IP (collect data for one hour and compress it on the fly) .SS "Convert CSV to a waterfall graphic with:" .IP http://kmkeen.com/tmp/heatmap.py.txt .SH "SEE ALSO" .LP RTL-SDR wiki documentation: .B http://sdr.osmocom.org/trac/wiki/rtl-sdr .LP Other rtl-sdr programs: .sp rtl_adsb(1), rtl_eeprom(1), rtl_fm(1), rtl_sdr(1), rtl_tcp(1), rtl_test(1) .SH AUTHOR This manual page was written by Maitland Bottoms for the Debian project (but may be used by others). .SH COPYRIGHT Copyright (c) 2013 A. Maitland Bottoms .LP This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. .LP This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ================================================ FILE: debian/rtl_sdr.1 ================================================ .TH "rtl_sdr" 1 "0.5.0" RTL-SDR "User Commands" .SH NAME rtl-sdr \- an I/Q recorder for RTL2832 based DVB-T receivers .SH DESCRIPTION Uses a re-purposed DVB-T receiver as a software defined radio to receive signals in I/Q data form. Written for and incorporated in the osmocom rtl-sdr project. .LP In-Phase and Quadrature Phase data can faithfully represent all of the information in a band of frequencies centered on a carrier signal frequency. .LP Much software is available for the RTL2832. Most of the user-level packages rely on the librtlsdr library which comes as part of the rtl-sdr codebase. This codebase contains both the library itself and also a number of command line tools such as rtl_test, rtl_sdr, rtl_tcp, and rtl_fm. These command line tools use the library to test for the existence of RTL2832 devices and to perform basic data transfer functions to and from the device. .LP Because most of the RTL2832 devices are connected using USB, the librtlsdr library depends on the libusb library to communicate with the device. .SH USAGE This program captures information from a band of frequencies and outputs the data in a form useful to other software radio programs. .SH SYNOPSIS .B rtl_adsb [-f freq] [OPTIONS] [output file] .SH OPTIONS .IP "-f frequency_to_tune_to [Hz]" .IP "-s samplerate (default: 2048000 Hz)" .IP "-d device_index (default: 0)" .IP "-g gain (default: 0 for auto)" .IP "-p ppm_error (default: 0)" .IP "-b output_block_size (default: 16 * 16384)" .IP "-n number of samples to read (default: 0, infinite)" .IP "-S force sync output (default: async)" .IP tfilename (a '-' dumps samples to stdout) .SH EXAMPLES .IP "Example: To tune to 392.0 MHz, and set the sample-rate to 1.8 MS/s, use:" ./rtl_sdr /tmp/capture.bin -s 1.8e6 -f 392e6 .LP to record samples to a file or to forward the data to a fifo. .LP If the device can't be opened, make sure you have the appropriate rights to access the device (install udev-rules from the repository, or run it as root). .SH SEE ALSO gnuradio(1) .LP RTL-SDR wiki documentation: .B http://sdr.osmocom.org/trac/wiki/rtl-sdr .LP Other rtl-sdr programs: .sp rtl_adsb(1), rtl_eeprom(1), rtl_fm(1), rtl_power(1), rtl_tcp(1), rtl_test(1) .SH AUTHOR This manual page was written by Maitland Bottoms for the Debian project (but may be used by others). .SH COPYRIGHT Copyright (c) 2013 A. Maitland Bottoms .LP This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. .LP This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ================================================ FILE: debian/rtl_tcp.1 ================================================ .TH "rtl_tcp" 1 "0.5.0" RTL-SDR "User Commands" .SH NAME rtl_tcp \- an I/Q spectrum server for RTL2832 based DVB-T receivers .SH DESCRIPTION Uses a re-purposed DVB-T receiver as a software defined radio to receive and send I/Q data via TCP network to another demodulation, decoding or logging apllication. Written for and incorporated into the osmocom rtl-sdr project. .LP Much software is available for the RTL2832. Most of the user-level packages rely on the librtlsdr library which comes as part of the rtl-sdr codebase. This codebase contains both the library itself and also a number of command line tools such as rtl_test, rtl_sdr, rtl_tcp, and rtl_fm. These command line tools use the library to test for the existence of RTL2832 devices and to perform basic data transfer functions to and from the device. .LP Because most of the RTL2832 devices are connected using USB, the librtlsdr library depends on the libusb library to communicate with the device. .SH USAGE Run this program on a machine with an rtl-sdr supported device connected and it will provide I/Q data to other applications via TCP/IP. .SH SYNOPSIS .B rtl_tcp [OPTIONS] .SH OPTIONS .IP "-a listen address" .IP "-p listen port (default: 1234)" .IP "-f frequency to tune to [Hz]" .IP "-g gain (default: 0 for auto)" .IP "-s samplerate in Hz (default: 2048000 Hz)" .IP "-b number of buffers (default: 32, set by library)" .IP "-n max number of linked list buffers to keep (default: 500)" .IP "-d device_index (default: 0)" .IP "-P ppm_error (default: 0)" .SH Example: .IP "rtl_tcp -a 10.0.0.2 [-p listen port (default: 1234)]" Found 1 device(s). Found Elonics E4000 tuner Using Generic RTL2832U (e.g. hama nano) Tuned to 100000000 Hz. listening... .LP Use the device argument 'rtl_tcp=10.0.0.2:1234' in OsmoSDR (gr-osmosdr) source to receive samples in GRC and control rtl_tcp parameters (frequency, gain, ...). .LP use the rtl_tcp=... device argument in gr-osmosdr source to receive the samples in GRC and control the rtl settings remotely. .LP This application has been successfully crosscompiled for ARM and MIPS devices and is providing IQ data in a networked ADS-B setup at a rate of 2.4MSps. The gr-osmosdr source is being used together with an optimized gr-air-modes version (see Known Apps below). It is also available as a package in OpenWRT. .LP A use case is described ​https://sites.google.com/site/embrtlsdr/ .SH SEE ALSO gnuradio(1) .LP RTL-SDR wiki documentation: .B http://sdr.osmocom.org/trac/wiki/rtl-sdr .LP Other rtl-sdr programs: .sp rtl_adsb(1), rtl_eeprom(1), rtl_fm(1), rtl_sdr(1), rtl_test(1) .SH AUTHOR This manual page was written by Maitland Bottoms for the Debian project (but may be used by others). .SH COPYRIGHT Copyright (c) 2013 A. Maitland Bottoms .LP This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. .LP This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ================================================ FILE: debian/rtl_test.1 ================================================ .TH "rtl_test" 1 "0.5.0" RTL-SDR "User Commands" .SH NAME rtl_test \- a benchmark tool for RTL2832 based DVB-T receivers .SH DESCRIPTION Test tuning range and functional sample rates of your device on your system. Uses a re-purposed DVB-T receiver as a software defined radio. Written for and incorporated into the osmocom rtl-sdr project. .LP Much software is available for the RTL2832. Most of the user-level packages rely on the librtlsdr library which comes as part of the rtl-sdr codebase. This codebase contains both the library itself and also a number of command line tools such as rtl_test, rtl_sdr, rtl_tcp, and rtl_fm. These command line tools use the library to test for the existence of RTL2832 devices and to perform basic data transfer functions to and from the device. .LP Because most of the RTL2832 devices are connected using USB, the librtlsdr library depends on the libusb library to communicate with the device. .SH SYNOPSIS .B rtl_test [OPTIONS] .SH OPTIONS .IP "-s samplerate (default: 2048000 Hz)" .IP "-d device_index (default: 0)" .IP "-t enable Elonics E4000 tuner benchmark]" .IP "-p enable PPM error measurement" .IP "-b output_block_size (default: 16 * 16384)" .IP "-S force sync output (default: async)" .SH EXAMPLES .IP "To check the possible tuning range (may heavily vary by some MHz depending on device and temperature), call" rtl_test -t .IP "To check the maximum samplerate possible on your machine, type (change the rate down until no sample loss occurs):" rtl_test -s 3.2e6 .LP A samplerate of 2.4e6 is known to work even over tcp connections (see rtl_tcp above). A sample rate of 2.88e6 may work without lost samples but this may depend on your PC/Laptop's host interface. .SH SEE ALSO RTL-SDR wiki documentation: .B http://sdr.osmocom.org/trac/wiki/rtl-sdr .LP Other rtl-sdr programs: .sp rtl_adsb(1), rtl_eeprom(1), rtl_fm(1), rtl_sdr(1), rtl_tcp(1) .SH AUTHOR This manual page was written by Maitland Bottoms for the Debian project (but may be used by others). .SH COPYRIGHT Copyright (c) 2013 A. Maitland Bottoms .LP This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. .LP This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. ================================================ FILE: debian/rules ================================================ #!/usr/bin/make -f DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) export DEB_HOST_MULTIARCH %: dh $@ override_dh_auto_configure: debian/librtlsdr0.udev dh_auto_configure -- -DLIB_INSTALL_DIR=lib/$(DEB_HOST_MULTIARCH) -DDETACH_KERNEL_DRIVER=ON debian/librtlsdr0.udev: rtl-sdr.rules cp -p rtl-sdr.rules debian/librtlsdr0.udev ================================================ FILE: debian/source/format ================================================ 3.0 (native) ================================================ FILE: debian/watch ================================================ version=4 opts="mode=git, gitmode=full, pgpmode=none" \ git://git.osmocom.org/rtl-sdr.git \ refs/tags/v([\d\.]+) debian uupdate ================================================ FILE: git-version-gen ================================================ #!/bin/sh # Print a version string. scriptversion=2010-01-28.01 # Copyright (C) 2007-2010 Free Software Foundation, Inc. # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/. # It may be run two ways: # - from a git repository in which the "git describe" command below # produces useful output (thus requiring at least one signed tag) # - from a non-git-repo directory containing a .tarball-version file, which # presumes this script is invoked like "./git-version-gen .tarball-version". # In order to use intra-version strings in your project, you will need two # separate generated version string files: # # .tarball-version - present only in a distribution tarball, and not in # a checked-out repository. Created with contents that were learned at # the last time autoconf was run, and used by git-version-gen. Must not # be present in either $(srcdir) or $(builddir) for git-version-gen to # give accurate answers during normal development with a checked out tree, # but must be present in a tarball when there is no version control system. # Therefore, it cannot be used in any dependencies. GNUmakefile has # hooks to force a reconfigure at distribution time to get the value # correct, without penalizing normal development with extra reconfigures. # # .version - present in a checked-out repository and in a distribution # tarball. Usable in dependencies, particularly for files that don't # want to depend on config.h but do want to track version changes. # Delete this file prior to any autoconf run where you want to rebuild # files to pick up a version string change; and leave it stale to # minimize rebuild time after unrelated changes to configure sources. # # It is probably wise to add these two files to .gitignore, so that you # don't accidentally commit either generated file. # # Use the following line in your configure.ac, so that $(VERSION) will # automatically be up-to-date each time configure is run (and note that # since configure.ac no longer includes a version string, Makefile rules # should not depend on configure.ac for version updates). # # AC_INIT([GNU project], # m4_esyscmd([build-aux/git-version-gen .tarball-version]), # [bug-project@example]) # # Then use the following lines in your Makefile.am, so that .version # will be present for dependencies, and so that .tarball-version will # exist in distribution tarballs. # # BUILT_SOURCES = $(top_srcdir)/.version # $(top_srcdir)/.version: # echo $(VERSION) > $@-t && mv $@-t $@ # dist-hook: # echo $(VERSION) > $(distdir)/.tarball-version case $# in 1) ;; *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;; esac tarball_version_file=$1 nl=' ' # First see if there is a tarball-only version file. # then try "git describe", then default. if test -f $tarball_version_file then v=`cat $tarball_version_file` || exit 1 case $v in *$nl*) v= ;; # reject multi-line output [0-9]*) ;; *) v= ;; esac test -z "$v" \ && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2 fi if test -n "$v" then : # use $v elif v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \ || git describe --abbrev=4 HEAD 2>/dev/null` \ && case $v in [0-9]*) ;; v[0-9]*) ;; *) (exit 1) ;; esac then # Is this a new git that lists number of commits since the last # tag or the previous older version that did not? # Newer: v6.10-77-g0f8faeb # Older: v6.10-g0f8faeb case $v in *-*-*) : git describe is okay three part flavor ;; *-*) : git describe is older two part flavor # Recreate the number of commits and rewrite such that the # result is the same as if we were using the newer version # of git describe. vtag=`echo "$v" | sed 's/-.*//'` numcommits=`git rev-list "$vtag"..HEAD | wc -l` v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`; ;; esac # Change the first '-' to a '.', so version-comparing tools work properly. # Remove the "g" in git describe's output string, to save a byte. v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`; else v=UNKNOWN fi v=`echo "$v" |sed 's/^v//'` # Don't declare a version "dirty" merely because a time stamp has changed. git status > /dev/null 2>&1 dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty= case "$dirty" in '') ;; *) # Append the suffix only if there isn't one already. case $v in *-dirty) ;; *) v="$v-dirty" ;; esac ;; esac # Omit the trailing newline, so that m4_esyscmd can use the result directly. echo "$v" | tr -d '\012' # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-end: "$" # End: ================================================ FILE: include/CMakeLists.txt ================================================ # Copyright 2012 OSMOCOM Project # # This file is part of rtl-sdr # # GNU Radio is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3, or (at your option) # any later version. # # GNU Radio is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Radio; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. ######################################################################## # Install public header files ######################################################################## install(FILES rtl-sdr.h rtl_tcp.h rtl-sdr_export.h DESTINATION include ) ================================================ FILE: include/Makefile.am ================================================ rtlsdr_HEADERS = rtl-sdr.h rtl-sdr_export.h noinst_HEADERS = reg_field.h rtlsdr_i2c.h tuner_e4k.h tuner_fc0012.h tuner_fc0013.h tuner_fc2580.h tuner_r82xx.h rtlsdrdir = $(includedir) ================================================ FILE: include/controlThread.h ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2019 <> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __RTL_CONTROL_THREAD_H #define __RTL_CONTROL_THREAD_H #ifdef __cplusplus extern "C" { #endif typedef struct { rtlsdr_dev_t *dev; int port; int wait; int report_i2c; char *addr; int* pDoExit; } ctrl_thread_data_t; void *ctrl_thread_fn(void *arg); #ifdef __cplusplus } #endif #endif ================================================ FILE: include/reg_field.h ================================================ #ifndef _REG_FIELD_H #define _REG_FIELD_H #include #include enum cmd_op { CMD_OP_GET = (1 << 0), CMD_OP_SET = (1 << 1), CMD_OP_EXEC = (1 << 2), }; enum pstate { ST_IN_CMD, ST_IN_ARG, }; struct strbuf { uint8_t idx; char buf[32]; }; struct cmd_state { struct strbuf cmd; struct strbuf arg; enum pstate state; void (*out)(const char *format, va_list ap); }; struct cmd { const char *cmd; uint32_t ops; int (*cb)(struct cmd_state *cs, enum cmd_op op, const char *cmd, int argc, char **argv); const char *help; }; /* structure describing a field in a register */ struct reg_field { uint8_t reg; uint8_t shift; uint8_t width; }; struct reg_field_ops { const struct reg_field *fields; const char **field_names; uint32_t num_fields; void *data; int (*write_cb)(void *data, uint32_t reg, uint32_t val); uint32_t (*read_cb)(void *data, uint32_t reg); }; uint32_t reg_field_read(struct reg_field_ops *ops, struct reg_field *field); int reg_field_write(struct reg_field_ops *ops, struct reg_field *field, uint32_t val); int reg_field_cmd(struct cmd_state *cs, enum cmd_op op, const char *cmd, int argc, char **argv, struct reg_field_ops *ops); #endif ================================================ FILE: include/rtl-sdr.h ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2012-2013 by Steve Markgraf * Copyright (C) 2012 by Dimitri Stolnikov * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __RTL_SDR_H #define __RTL_SDR_H #ifdef __cplusplus extern "C" { #endif #include #include #include typedef struct rtlsdr_dev rtlsdr_dev_t; RTLSDR_API uint32_t rtlsdr_get_device_count(void); RTLSDR_API const char* rtlsdr_get_device_name(uint32_t index); /*! * Get USB device strings. * * NOTE: The string arguments must provide space for up to 256 bytes. * * \param index the device index * \param manufact manufacturer name, may be NULL * \param product product name, may be NULL * \param serial serial number, may be NULL * \return 0 on success */ RTLSDR_API int rtlsdr_get_device_usb_strings(uint32_t index, char *manufact, char *product, char *serial); /*! * Get device index by USB serial string descriptor. * * \param serial serial string of the device * \return device index of first device where the name matched * \return -1 if name is NULL * \return -2 if no devices were found at all * \return -3 if devices were found, but none with matching name */ RTLSDR_API int rtlsdr_get_index_by_serial(const char *serial); /*! * Open device index by index. * * \param pointer where to save the device handle, which again is a pointer * \param serial serial string of the device * \return device index to be opened * \return -1 if device or libusb is inaccessible * \return -3 if device permissions don't fit: * check, if udev rules file rtl-sdr.rules needs to be installed * \return negative, for a libusb error code or some other initialization error */ RTLSDR_API int rtlsdr_open(rtlsdr_dev_t **dev, uint32_t index); /*! * Close device. * * \param dev the device handle given by rtlsdr_open() * \return -1 if device handle was already close - or never opened */ RTLSDR_API int rtlsdr_close(rtlsdr_dev_t *dev); /* configuration functions */ /*! * Set crystal oscillator frequencies used for the RTL2832 and the tuner IC. * * Usually both ICs use the same clock. Changing the clock may make sense if * you are applying an external clock to the tuner or to compensate the * frequency (and samplerate) error caused by the original (cheap) crystal. * * NOTE: Call this function only if you fully understand the implications. * * \param dev the device handle given by rtlsdr_open() * \param rtl_freq frequency value used to clock the RTL2832 in Hz * \param tuner_freq frequency value used to clock the tuner IC in Hz * \return 0 on success */ RTLSDR_API int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, uint32_t tuner_freq); /*! * Get crystal oscillator frequencies used for the RTL2832 and the tuner IC. * * Usually both ICs use the same clock. * * \param dev the device handle given by rtlsdr_open() * \param rtl_freq frequency value used to clock the RTL2832 in Hz * \param tuner_freq frequency value used to clock the tuner IC in Hz * \return 0 on success */ RTLSDR_API int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq, uint32_t *tuner_freq); /*! * Get USB device strings. * * NOTE: The string arguments must provide space for up to 256 bytes. * * \param dev the device handle given by rtlsdr_open() * \param manufact manufacturer name, may be NULL * \param product product name, may be NULL * \param serial serial number, may be NULL * \return 0 on success */ RTLSDR_API int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact, char *product, char *serial); /*! * Write the device EEPROM * * \param dev the device handle given by rtlsdr_open() * \param data buffer of data to be written * \param offset address where the data should be written * \param len length of the data * \return 0 on success * \return -1 if device handle is invalid * \return -2 if EEPROM size is exceeded * \return -3 if no EEPROM was found */ RTLSDR_API int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_t len); /*! * Read the device EEPROM * * \param dev the device handle given by rtlsdr_open() * \param data buffer where the data should be written * \param offset address where the data should be read from * \param len length of the data * \return 0 on success * \return -1 if device handle is invalid * \return -2 if EEPROM size is exceeded * \return -3 if no EEPROM was found */ RTLSDR_API int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_t len); /*! * Set the frequency the device is tuned to. * * \param dev the device handle given by rtlsdr_open() * \param frequency in Hz * \return 0 on success * \return < 0 if device handle is invalid or some other error */ RTLSDR_API int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq); /*! * Set the frequency the device is tuned to: allow frequency > 32 bit ~= 4.29 GHz * * \param dev the device handle given by rtlsdr_open() * \param frequency in Hz in 64 bit * \return 0 on success * \return < 0 if device handle is invalid or some other error */ RTLSDR_API int rtlsdr_set_center_freq64(rtlsdr_dev_t *dev, uint64_t freq); /*! * Set harmonic reception - for R820T/2 tuner * * \param dev the device handle given by rtlsdr_open() * \param harmonic - receive n'th harmonic. 0 = default for disabling this * \return 0 on success * \return < 0 if device handle is invalid or some other error */ RTLSDR_API int rtlsdr_set_harmonic_rx(rtlsdr_dev_t *dev, int harmonic); /*! * Check, if tuner PLL (frequency) is still locked. * Tuner/PLL might loose lock (at high frequencies), * e.g. for temperature reasons * * \param dev the device handle given by rtlsdr_open() * \return 1: PLL is NOT locked * \return 0: PLL HAS lock * \return < 0: if device handle is invalid or some other error * \return -2: not supported for devices' tuner */ RTLSDR_API int rtlsdr_is_tuner_PLL_locked(rtlsdr_dev_t *dev); /*! * Get actual frequency the device is tuned to. * * \param dev the device handle given by rtlsdr_open() * \return 0 on error, frequency in Hz otherwise */ RTLSDR_API uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev); /*! * Get actual frequency the device is tuned to. * * \param dev the device handle given by rtlsdr_open() * \return 0 on error, frequency in Hz otherwise */ RTLSDR_API uint64_t rtlsdr_get_center_freq64(rtlsdr_dev_t *dev); /*! * Set the frequency correction value for the device. * * \param dev the device handle given by rtlsdr_open() * \param ppm correction value in parts per million (ppm) * \return 0 on success */ RTLSDR_API int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm); /*! * Get actual frequency correction value of the device. * * \param dev the device handle given by rtlsdr_open() * \return correction value in parts per million (ppm) * if dev is valid, no error can occur */ RTLSDR_API int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev); enum rtlsdr_tuner { RTLSDR_TUNER_UNKNOWN = 0, RTLSDR_TUNER_E4000, RTLSDR_TUNER_FC0012, RTLSDR_TUNER_FC0013, RTLSDR_TUNER_FC2580, RTLSDR_TUNER_R820T, /* or R820T2 - R820T and R820T2 is not disdinguishable */ RTLSDR_TUNER_R828D }; /*! * Get the tuner type. * * \param dev the device handle given by rtlsdr_open() * \return RTLSDR_TUNER_UNKNOWN on error, tuner type otherwise */ RTLSDR_API enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev); /*! * Get a list of gains supported by the tuner. * * NOTE: The gains argument must be preallocated by the caller. If NULL is * being given instead, the number of available gain values will be returned. * * \param dev the device handle given by rtlsdr_open() * \param gains array of gain values. In tenths of a dB, 115 means 11.5 dB. * \return <= 0 on error, number of available (returned) gain values otherwise */ RTLSDR_API int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains); /*! * Set the gain for the device. * Manual gain mode must be enabled for this to work. * * Valid gain values (in tenths of a dB) for the E4000 tuner: * -10, 15, 40, 65, 90, 115, 140, 165, 190, * 215, 240, 290, 340, 420, 430, 450, 470, 490 * * Valid gain values may be queried with \ref rtlsdr_get_tuner_gains function. * * \param dev the device handle given by rtlsdr_open() * \param gain in tenths of a dB, 115 means 11.5 dB. * \return 0 on success */ RTLSDR_API int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain); /*! * Set (and retrieve) the bandwidth for the device. * * \param dev the device handle given by rtlsdr_open() * \param bw bandwidth in Hz. Zero means automatic BW selection. * \param applied_bw is applied bandwidth in Hz, or 0 if unknown * \param apply_bw: 1 to really apply configure the tuner chip; 0 for just returning applied_bw * \return 0 on success */ RTLSDR_API int rtlsdr_set_and_get_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw, uint32_t *applied_bw, int apply_bw ); /*! * Set the bandwidth for the device. * * \param dev the device handle given by rtlsdr_open() * \param bw bandwidth in Hz. Zero means automatic BW selection. * \return 0 on success */ RTLSDR_API int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw ); /*! * Sets the center of the filtered tuner band(width) * * \param dev the device handle given by rtlsdr_open() * \param if_band_center_freq in Hz. Zero means, that band center shall be at zero (=default). * set if_band_center_freq = +samplerate/4 to have the filtered band centered at output's right half. * \return 0 on success */ RTLSDR_API int rtlsdr_set_tuner_band_center(rtlsdr_dev_t *dev, int32_t if_band_center_freq ); /*! * Set the mixer sideband for the device. * * \param dev the device handle given by rtlsdr_open() * \param sideband mixer sideband 0 means lower sideband, 1 means upper sideband. * \return 0 on success */ RTLSDR_API int rtlsdr_set_tuner_sideband(rtlsdr_dev_t *dev, int sideband); /*! * Get actual (RF / HF) gain the device is configured to - excluding the IF gain. * * \param dev the device handle given by rtlsdr_open() * \return 0 on error, gain in tenths of a dB, 115 means 11.5 dB. * unfortunately it's impossible to distinguish error against 0 dB */ RTLSDR_API int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev); /*! * Set LNA / Mixer / VGA Device Gain for R820T/2 device is configured to. * * \param dev the device handle given by rtlsdr_open() * \param lna_gain index in 0 .. 15: 0 == min; see tuner_r82xx.c table r82xx_lna_gain_steps[] * \param mixer_gain index in 0 .. 15: 0 == min; see tuner_r82xx.c table r82xx_mixer_gain_steps[] * \param vga_gain index in 0 .. 15: 0 == -12 dB; 15 == 40.5 dB; => 3.5 dB/step; * vga_gain index 16 activates AGC for VGA controlled from RTL2832 * see tuner_r82xx.c table r82xx_vga_gain_steps[] * \return 0 on success */ RTLSDR_API int rtlsdr_set_tuner_gain_ext(rtlsdr_dev_t *dev, int lna_gain, int mixer_gain, int vga_gain); /*! * Set the intermediate frequency gain for the device. * * \param dev the device handle given by rtlsdr_open() * \param stage intermediate frequency gain stage number (1 to 6 for E4000) * \param gain in tenths of a dB, -30 means -3.0 dB. * \return 0 on success */ RTLSDR_API int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain); /*! * Set the gain mode (automatic/manual) for the device. * Manual gain mode must be enabled for the gain setter function to work. * * \param dev the device handle given by rtlsdr_open() * \param manual gain mode, 1 means manual gain mode shall be enabled. * \return 0 on success */ RTLSDR_API int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual); /*! * Set the agc_variant for automatic gain mode for the device (only R820T/2). * Automatic gain mode must be enabled for the gain setter function to work. * * \param dev the device handle given by rtlsdr_open() * \param if_mode: * 0 set automatic VGA, which is controlled from RTL2832 * -2500 .. +2500: set fixed IF gain in tenths of a dB, 115 means 11.5 dB. * use -1 or +1 in case you neither want attenuation nor gain. * this equals the VGA gain for R820T/2 tuner. * exact values (R820T/2) are in range -47 .. 408 in tenth of a dB, * giving -4.7 .. +40.8 dB. these exact values may slightly change * with better measurements. * 10000 .. 10015: IF gain == VGA index from parameter if_mode * set if_mode by index: index := VGA_idx +10000 * 10016 .. 10031: same as 10000 .. 10015, but additionally set automatic VGA * 10011: for fixed VGA (=default) of -12 dB + 11 * 3.5 dB = 26.5 dB * * \return 0 on success */ RTLSDR_API int rtlsdr_set_tuner_if_mode(rtlsdr_dev_t *dev, int if_mode); /*! * Set the sample rate for the device, also selects the baseband filters * according to the requested sample rate for tuners where this is possible. * * \param dev the device handle given by rtlsdr_open() * \param samp_rate the sample rate to be set, possible values are: * 225001 - 300000 Hz * 900001 - 3200000 Hz * sample loss is to be expected for rates > 2400000 * \return 0 on success, -EINVAL on invalid rate */ RTLSDR_API int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t rate); /*! * Get actual sample rate the device is configured to. * * \param dev the device handle given by rtlsdr_open() * \return 0 on error, sample rate in Hz otherwise */ RTLSDR_API uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev); /*! * Enable test mode that returns an 8 bit counter instead of the samples. * The counter is generated inside the RTL2832. * * \param dev the device handle given by rtlsdr_open() * \param test mode, 1 means enabled, 0 disabled * \return 0 on success */ RTLSDR_API int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on); /*! * Enable or disable the internal digital AGC of the RTL2832. * * \param dev the device handle given by rtlsdr_open() * \param digital AGC mode, 1 means enabled, 0 disabled * \return 0 on success */ RTLSDR_API int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on); /*! * Enable or disable the direct sampling mode. When enabled, the IF mode * of the RTL2832 is activated, and rtlsdr_set_center_freq() will control * the IF-frequency of the DDC, which can be used to tune from 0 to 28.8 MHz * (xtal frequency of the RTL2832). * * \param dev the device handle given by rtlsdr_open() * \param on 0 means disabled, 1 I-ADC input enabled, 2 Q-ADC input enabled * \return 0 on success */ RTLSDR_API int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on); /*! * Get state of the direct sampling mode * * \param dev the device handle given by rtlsdr_open() * \return -1 on error, 0 means disabled, 1 I-ADC input enabled * 2 Q-ADC input enabled */ RTLSDR_API int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev); enum rtlsdr_ds_mode { RTLSDR_DS_IQ = 0, /* I/Q quadrature sampling of tuner output */ RTLSDR_DS_I, /* 1: direct sampling on I branch: usually not connected */ RTLSDR_DS_Q, /* 2: direct sampling on Q branch: HF on rtl-sdr v3 dongle */ RTLSDR_DS_I_BELOW, /* 3: direct sampling on I branch when frequency below 'DS threshold frequency' */ RTLSDR_DS_Q_BELOW /* 4: direct sampling on Q branch when frequency below 'DS threshold frequency' */ }; /*! * Set direct sampling mode with threshold * * \param dev the device handle given by rtlsdr_open() * \param mode static modes 0 .. 2 as in rtlsdr_set_direct_sampling(). other modes do automatic switching * \param freq_threshold direct sampling is used below this frequency, else quadrature mode through tuner * set 0 for using default setting per tuner - not fully implemented yet! * \return negative on error, 0 on success */ RTLSDR_API int rtlsdr_set_ds_mode(rtlsdr_dev_t *dev, enum rtlsdr_ds_mode mode, uint32_t freq_threshold); /*! * Enable or disable offset tuning for zero-IF tuners, which allows to avoid * problems caused by the DC offset of the ADCs and 1/f noise. * * \param dev the device handle given by rtlsdr_open() * \param on 0 means disabled, 1 enabled * \return 0 on success */ RTLSDR_API int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on); /*! * Get state of the offset tuning mode * * \param dev the device handle given by rtlsdr_open() * \return -1 on error, 0 means disabled, 1 enabled */ RTLSDR_API int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev); /*! * Enable or disable frequency dithering for r820t tuners. * Must be performed before freq_set(). * Fails for other tuners. * * \param dev the device handle given by rtlsdr_open() * \param on 0 means disabled, 1 enabled * \return 0 on success */ RTLSDR_API int rtlsdr_set_dithering(rtlsdr_dev_t *dev, int dither); /* streaming functions */ /*! * Reset buffer in RTL2832 * * \param dev the device handle given by rtlsdr_open() * \return 0 on success * \return -1 on error */ RTLSDR_API int rtlsdr_reset_buffer(rtlsdr_dev_t *dev); /*! * Read data synchronously * * \param dev the device handle given by rtlsdr_open() * \return 0 on success * \return -1 on error or error code from libusb */ RTLSDR_API int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read); typedef void(*rtlsdr_read_async_cb_t)(unsigned char *buf, uint32_t len, void *ctx); /*! * Read samples from the device asynchronously. This function will block until * it is being canceled using rtlsdr_cancel_async() * * NOTE: This function is deprecated and is subject for removal. * * \param dev the device handle given by rtlsdr_open() * \param cb callback function to return received samples * \param ctx user specific context to pass via the callback function * \return 0 on success */ RTLSDR_API int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx); /*! * Read samples from the device asynchronously. This function will block until * it is being canceled using rtlsdr_cancel_async() * * \param dev the device handle given by rtlsdr_open() * \param cb callback function to return received samples * \param ctx user specific context to pass via the callback function * \param buf_num optional buffer count, buf_num * buf_len = overall buffer size * set to 0 for default buffer count (15) * \param buf_len optional buffer length, must be multiple of 512, * should be a multiple of 16384 (URB size), set to 0 * for default buffer length (16 * 32 * 512) * \return 0 on success */ RTLSDR_API int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len); /*! * Cancel all pending asynchronous operations on the device. * Due to incomplete concurrency implementation, this should * only be called from within the callback function, so it is * in the correct thread. * * \param dev the device handle given by rtlsdr_open() * \return 0 on success */ RTLSDR_API int rtlsdr_cancel_async(rtlsdr_dev_t *dev); /*! * Read from the remote control (RC) infrared (IR) sensor * * \param dev the device handle given by rtlsdr_open() * \param buf buffer to write IR signal (MSB=pulse/space, 7LSB=duration*20usec), recommended 128-bytes * \param buf_len size of buf * \return 0 if no signal, >0 number of bytes written into buf, <0 for error */ RTLSDR_API int rtlsdr_ir_query(rtlsdr_dev_t *dev, uint8_t *buf, size_t buf_len); /*! * Enable or disable (the bias tee on) GPIO PIN 0 - if not reconfigured. * See rtlsdr_set_opt_string() option 'T'. * This works for rtl-sdr.com v3 dongles, see * http://www.rtl-sdr.com/rtl-sdr-blog-v-3-dongles-user-guide/ * Note: rtlsdr_close() does not clear GPIO lines, * so it leaves the (bias tee) line enabled if a client program * doesn't explictly disable it. * * \param dev the device handle given by rtlsdr_open() * \param on 1 for Bias T on. 0 for Bias T off. * \return -1 if device is not initialized. 0 otherwise. */ RTLSDR_API int rtlsdr_set_bias_tee(rtlsdr_dev_t *dev, int on); /*! * Enable or disable (the bias tee on) the given GPIO pin. * Note: rtlsdr_close() does not clear GPIO lines, * so it leaves the (bias tee) lines enabled if a client program * doesn't explictly disable it. * * \param dev the device handle given by rtlsdr_open() * \param gpio the gpio pin -- assuming this line is connected to Bias T. * gpio needs to be in 0 .. 7. BUT pin 4 is connected to Tuner RESET. * and for FC0012 is already connected/reserved pin 6 for switching V/U-HF. * \param on 1 for Bias T on. 0 for Bias T off. * \return -1 if device is not initialized. 0 otherwise. */ RTLSDR_API int rtlsdr_set_bias_tee_gpio(rtlsdr_dev_t *dev, int gpio, int on); /* * GPIO 0 .. 7 correspond to RTL2832U's pins as follows: * Pin information from http://lea.hamradio.si/~s57uuu/mischam/rtlsdr/ports.html * * GPIO0 (=pin 37): BiasT for RTL-SDR.com V3 * GPIO1 (=pin 32): usually free - on RTL-SDR.com V3's Expansion Ports * GPIO2 (=pin 31): usually free - on RTL-SDR.com V3's Expansion Ports * GPIO3 (=pin 36): usually free * GPIO4 (=pin 30): RESET for Tuners FC2580 and FC0012 / on RTL-SDR.com V3's Expansion Ports * GPIO5 (=pin 29): usually free - on RTL-SDR.com V3's Expansion Ports * GPIO6 (=pin 22): select V-band/U-band filter for FC0012-Tuner * GPIO7 (=pin 21): usually free * * CAUTION: The port pins use 3.3V logic levels and are static sensitive! * * most of following functions are also copied from Marko Cebokli's site * http://lea.hamradio.si/~s57uuu/mischam/rtlsdr/ports.html * but added return of error/status. * GPIO numbers 'gpio' in range 0 .. 7 for the following gpio functions * all \return negative values, on error */ RTLSDR_API int rtlsdr_set_gpio_output(rtlsdr_dev_t *dev, uint8_t gpio); RTLSDR_API int rtlsdr_set_gpio_input(rtlsdr_dev_t *dev, uint8_t gpio); RTLSDR_API int rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val); RTLSDR_API int rtlsdr_get_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int *val); RTLSDR_API int rtlsdr_set_gpio_byte(rtlsdr_dev_t *dev, int val); RTLSDR_API int rtlsdr_get_gpio_byte(rtlsdr_dev_t *dev, int *val); RTLSDR_API int rtlsdr_set_gpio_status(rtlsdr_dev_t *dev, int *status ); /*! * Sets multiple options from a string encoded like "bw=300:agc=0:gain=27.3:dagc=0:T=1". * this is a helper function, that programs don't need to implement every single option * at the command line interface. * Options are seperated by colon ':'. * There mustn't be extra spaces between option name and '='. * option 'f' set center frequency as in rtlsdr_set_center_freq() * option 'bw' sets tuner bandwidth as in rtlsdr_set_tuner_bandwidth() * - but value is in kHz. * option 'agc' sets tuner gain mode as with rtlsdr_set_tuner_gain_mode(): * '1' means manual gain mode shall be enabled. * option 'gain' sets tuner gain as with rtlsdr_set_tuner_gain(): * values in tenth dB. * option 'dagc' or 'dgc' de/activates digital agc as with rtlsdr_set_agc_mode(). * value 1 to enable. 0 to disable. * option 'ds' set direct sampling as with rtlsdr_set_direct_sampling(): * '0' to deactivate, '1' or 'i' for I-ADC input, '2' or 'q' for Q-ADC input * option 't' or 'T' for enabling bias tee on GPIO PIN 0 as with rtlsdr_set_bias_tee(): * '1' for Bias T on. '0' for Bias T off. * * \param dev the device handle given by rtlsdr_open() * \param opts described option string * \param verbose print parsed options to stderr */ RTLSDR_API int rtlsdr_set_opt_string(rtlsdr_dev_t *dev, const char *opts, int verbose); RTLSDR_API const char * rtlsdr_get_opt_help(int longInfo); /*! * Exposes/permits hacking of Tuner-specific I2C registers: set register once * * \param dev the device handle given by rtlsdr_open() * \param i2c_register register address * \param mask 8-bit bitmask, indicating which bits shall be set * \param data 8-bit data, which shall be set * \return -1 if device is not initialized. 0 otherwise. */ RTLSDR_API int rtlsdr_set_tuner_i2c_register(rtlsdr_dev_t *dev, unsigned i2c_register, unsigned mask, unsigned data); /* TODO: uint8_t */ RTLSDR_API int rtlsdr_get_tuner_i2c_register(rtlsdr_dev_t *dev, unsigned char* data, int len); /*! * Exposes/permits hacking of Tuner-specific I2C registers: set and keep register for future * * \param dev the device handle given by rtlsdr_open() * \param i2c_register register address * \param mask 8-bit bitmask, indicating which bits shall be set * \param data 8-bit data, which shall be set; data in 0 .. 255 sets override; data > 255 clears override * \return -1 if device is not initialized. 0 otherwise. */ RTLSDR_API int rtlsdr_set_tuner_i2c_override(rtlsdr_dev_t *dev, unsigned i2c_register, unsigned mask, unsigned data); /*! * request version id string to identify source and date of library * * \return pointer to C string, e.g. "github.com/librtlsdr" or "github.com/hayguen" or .. with build date (in parantheses) * string keeps owned by library */ RTLSDR_API const char * rtlsdr_get_ver_id(); /*! * request version numbers of library * * \return major version in upper 16 bit, minor revision in lower 16 bit */ RTLSDR_API uint32_t rtlsdr_get_version(); #ifdef __cplusplus } #endif #endif /* __RTL_SDR_H */ ================================================ FILE: include/rtl-sdr_export.h ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2012 by Hoernchen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef RTLSDR_EXPORT_H #define RTLSDR_EXPORT_H #if defined __GNUC__ # if __GNUC__ >= 4 # define __SDR_EXPORT __attribute__((visibility("default"))) # define __SDR_IMPORT __attribute__((visibility("default"))) # else # define __SDR_EXPORT # define __SDR_IMPORT # endif #elif _MSC_VER # define __SDR_EXPORT __declspec(dllexport) # define __SDR_IMPORT __declspec(dllimport) #else # define __SDR_EXPORT # define __SDR_IMPORT #endif #ifndef rtlsdr_STATIC # ifdef rtlsdr_EXPORTS # define RTLSDR_API __SDR_EXPORT # else # define RTLSDR_API __SDR_IMPORT # endif #else #define RTLSDR_API #endif #endif /* RTLSDR_EXPORT_H */ ================================================ FILE: include/rtl_tcp.h ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2012-2013 by Steve Markgraf * Copyright (C) 2012 by Dimitri Stolnikov * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __RTL_TCP_H #define __RTL_TCP_H #ifdef __cplusplus extern "C" { #endif /*! * This enum defines the possible commands in rtl_tcp * commands 0x01..0x0E are compatible to osmocom's rtlsdr * see https://github.com/osmocom/rtl-sdr/blob/master/src/rtl_tcp.c * commands >= 0x40 are extensions */ enum RTL_TCP_COMMANDS { SET_FREQUENCY = 0x01, /* sets frequency - amending hi word of SET_FREQ_HI32 if present */ SET_FREQ_HI32 = 0x56, /* in addition to SET_FREQUENCY */ SET_SAMPLE_RATE = 0x02, SET_GAIN_MODE = 0x03, SET_GAIN = 0x04, SET_FREQUENCY_CORRECTION = 0x05, SET_IF_STAGE = 0x06, SET_TEST_MODE = 0x07, SET_AGC_MODE = 0x08, SET_DIRECT_SAMPLING = 0x09, SET_OFFSET_TUNING = 0x0A, SET_RTL_CRYSTAL = 0x0B, SET_TUNER_CRYSTAL = 0x0C, SET_TUNER_GAIN_BY_INDEX = 0x0D, #if 1 /* development branch since 2018-10-03 */ SET_BIAS_TEE = 0x0E, SET_TUNER_BANDWIDTH = 0x40, #else /* prev code - used in ExtIO - to build compatible rtl_tcp.exe */ SET_TUNER_BANDWIDTH = 0x0E, SET_BIAS_TEE = 0x0F #endif UDP_ESTABLISH = 0x41, UDP_TERMINATE = 0x42, SET_I2C_TUNER_REGISTER = 0x43, /* for experiments: 32 bit data word: * 31 .. 20: register (12 bits) * 19 .. 12: mask (8 bits) * 11 .. 0: data (12 bits) */ SET_I2C_TUNER_OVERRIDE = 0x44, /* encoding as with SET_I2C_TUNER_REGISTER * data (bits 11 .. 0) > 255 removes override */ SET_TUNER_BW_IF_CENTER = 0x45, /* freq from SET_FREQUENCY stays in center; * the bandwidth (from SET_TUNER_BANDWIDTH) * is set to be centered at given IF frequency */ SET_TUNER_IF_MODE = 0x46, /* set tuner IF mode - or gain */ SET_SIDEBAND = 0x47, /* Mixer Sideband for R820T */ REPORT_I2C_REGS = 0x48, /* perodically report I2C registers * - if reverse channel is enabled */ GPIO_SET_OUTPUT_MODE = 0x49, /* rtlsdr_set_gpio_output() */ GPIO_SET_INPUT_MODE = 0x50, /* rtlsdr_set_gpio_input() */ GPIO_GET_IO_STATUS = 0x51, /* rtlsdr_set_gpio_status() */ GPIO_WRITE_PIN = 0x52, /* rtlsdr_set_gpio_output() and rtlsdr_set_gpio_bit() */ GPIO_READ_PIN = 0x53, /* rtlsdr_get_gpio_bit() */ GPIO_GET_BYTE = 0x54, /* rtlsdr_get_gpio_byte() */ IS_TUNER_PLL_LOCKED = 0x55, /* rtlsdr_is_tuner_PLL_locked() */ /* SET_FREQ_HI32 = 0x56, * rtlsdr_set_center_freq64() */ }; #ifdef __cplusplus } #endif #endif ================================================ FILE: include/rtlsdr_i2c.h ================================================ #ifndef __I2C_H #define __I2C_H uint32_t rtlsdr_get_tuner_clock(void *dev); int rtlsdr_i2c_write_fn(void *dev, uint8_t addr, uint8_t *buf, int len); int rtlsdr_i2c_read_fn(void *dev, uint8_t addr, uint8_t *buf, int len); #endif ================================================ FILE: include/rtlsdr_rpc.h ================================================ #ifndef RTLSDR_RPC_H_INCLUDED #define RTLSDR_RPC_H_INCLUDED #include #ifdef __cplusplus extern "C" { #endif typedef void (*rtlsdr_rpc_read_async_cb_t) (unsigned char*, uint32_t, void*); uint32_t rtlsdr_rpc_get_device_count(void); const char* rtlsdr_rpc_get_device_name (uint32_t nidex); int rtlsdr_rpc_get_device_usb_strings (uint32_t index, char* manufact, char* product, char* serial); int rtlsdr_rpc_get_index_by_serial (const char* serial); int rtlsdr_rpc_open (void** dev, uint32_t index); int rtlsdr_rpc_close (void* dev); int rtlsdr_rpc_set_xtal_freq (void* dev, uint32_t rtl_freq, uint32_t tuner_freq); int rtlsdr_rpc_get_xtal_freq (void* dev, uint32_t* rtl_freq, uint32_t* tuner_freq); int rtlsdr_rpc_get_usb_strings (void* dev, char* manufact, char* product, char* serial); int rtlsdr_rpc_write_eeprom (void* dev, uint8_t* data, uint8_t offset, uint16_t len); int rtlsdr_rpc_read_eeprom (void* dev, uint8_t* data, uint8_t offset, uint16_t len); int rtlsdr_rpc_set_center_freq (void* dev, uint32_t freq); uint32_t rtlsdr_rpc_get_center_freq (void* dev); int rtlsdr_rpc_set_freq_correction (void* dev, int ppm); int rtlsdr_rpc_get_freq_correction (void *dev); int rtlsdr_rpc_get_tuner_type (void* dev); int rtlsdr_rpc_get_tuner_gains (void* dev, int* gainsp); int rtlsdr_rpc_set_tuner_gain (void *dev, int gain); int rtlsdr_rpc_get_tuner_gain (void* dev); int rtlsdr_rpc_set_tuner_if_gain (void* dev, int stage, int gain); int rtlsdr_rpc_set_tuner_gain_mode (void* dev, int manual); int rtlsdr_rpc_set_and_get_tuner_bandwidth (void* devp, uint32_t bw, uint32_t *applied_bw, int apply_bw); int rtlsdr_rpc_set_sample_rate (void* dev, uint32_t rate); uint32_t rtlsdr_rpc_get_sample_rate (void* dev); int rtlsdr_rpc_set_testmode (void* dev, int on); int rtlsdr_rpc_set_agc_mode (void* dev, int on); int rtlsdr_rpc_set_direct_sampling (void* dev, int on); int rtlsdr_rpc_get_direct_sampling (void* dev); int rtlsdr_rpc_set_offset_tuning (void* dev, int on); int rtlsdr_rpc_get_offset_tuning (void* dev); int rtlsdr_rpc_reset_buffer (void* dev); int rtlsdr_rpc_read_sync (void* dev, void* buf, int len, int* n_read); int rtlsdr_rpc_wait_async (void* dev, rtlsdr_rpc_read_async_cb_t cb, void* ctx); int rtlsdr_rpc_read_async (void* dev, rtlsdr_rpc_read_async_cb_t cb, void* ctx, uint32_t buf_num, uint32_t buf_len); int rtlsdr_rpc_cancel_async (void* dev); unsigned int rtlsdr_rpc_is_enabled(void); #ifdef __cplusplus } #endif #endif /* RTLSDR_RPC_H_INCLUDED */ ================================================ FILE: include/rtlsdr_rpc_msg.h ================================================ #ifndef RTLSDR_RPC_MSG_H_INCLUDED #define RTLSDR_RPC_MSG_H_INCLUDED #include #include typedef enum { RTLSDR_RPC_OP_GET_DEVICE_COUNT = 0, RTLSDR_RPC_OP_GET_DEVICE_NAME, RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS, RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL, RTLSDR_RPC_OP_OPEN, RTLSDR_RPC_OP_CLOSE, RTLSDR_RPC_OP_SET_XTAL_FREQ, RTLSDR_RPC_OP_GET_XTAL_FREQ, RTLSDR_RPC_OP_GET_USB_STRINGS, RTLSDR_RPC_OP_WRITE_EEPROM, RTLSDR_RPC_OP_READ_EEPROM, RTLSDR_RPC_OP_SET_CENTER_FREQ, RTLSDR_RPC_OP_GET_CENTER_FREQ, RTLSDR_RPC_OP_SET_FREQ_CORRECTION, RTLSDR_RPC_OP_GET_FREQ_CORRECTION, RTLSDR_RPC_OP_GET_TUNER_TYPE, RTLSDR_RPC_OP_GET_TUNER_GAINS, RTLSDR_RPC_OP_SET_TUNER_GAIN, RTLSDR_RPC_OP_GET_TUNER_GAIN, RTLSDR_RPC_OP_SET_TUNER_IF_GAIN, RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE, RTLSDR_RPC_OP_SET_GET_TUNER_BW, RTLSDR_RPC_OP_SET_SAMPLE_RATE, RTLSDR_RPC_OP_GET_SAMPLE_RATE, RTLSDR_RPC_OP_SET_TESTMODE, RTLSDR_RPC_OP_SET_AGC_MODE, RTLSDR_RPC_OP_SET_DIRECT_SAMPLING, RTLSDR_RPC_OP_GET_DIRECT_SAMPLING, RTLSDR_RPC_OP_SET_OFFSET_TUNING, RTLSDR_RPC_OP_GET_OFFSET_TUNING, RTLSDR_RPC_OP_RESET_BUFFER, RTLSDR_RPC_OP_READ_SYNC, RTLSDR_RPC_OP_WAIT_ASYNC, RTLSDR_RPC_OP_READ_ASYNC, RTLSDR_RPC_OP_CANCEL_ASYNC, /* non api operations */ RTLSDR_RPC_OP_EVENT_STATE, RTLSDR_RPC_OP_INVALID } rtlsdr_rpc_op_t; typedef struct { /* raw network format */ uint32_t size; uint8_t op; uint8_t id; uint32_t err; uint8_t data[1]; } __attribute__((packed)) rtlsdr_rpc_fmt_t; typedef struct { size_t off; size_t size; uint8_t* fmt; } rtlsdr_rpc_msg_t; int rtlsdr_rpc_msg_init(rtlsdr_rpc_msg_t*, size_t); int rtlsdr_rpc_msg_fini(rtlsdr_rpc_msg_t*); void rtlsdr_rpc_msg_reset(rtlsdr_rpc_msg_t*); int rtlsdr_rpc_msg_realloc(rtlsdr_rpc_msg_t*, size_t); void rtlsdr_rpc_msg_set_size(rtlsdr_rpc_msg_t*, size_t); size_t rtlsdr_rpc_msg_get_size(const rtlsdr_rpc_msg_t*); void rtlsdr_rpc_msg_set_op(rtlsdr_rpc_msg_t*, rtlsdr_rpc_op_t); rtlsdr_rpc_op_t rtlsdr_rpc_msg_get_op(const rtlsdr_rpc_msg_t*); void rtlsdr_rpc_msg_set_id(rtlsdr_rpc_msg_t*, uint8_t); uint8_t rtlsdr_rpc_msg_get_id(const rtlsdr_rpc_msg_t*); void rtlsdr_rpc_msg_set_err(rtlsdr_rpc_msg_t*, int); int rtlsdr_rpc_msg_get_err(const rtlsdr_rpc_msg_t*); int rtlsdr_rpc_msg_push_int32(rtlsdr_rpc_msg_t*, int32_t); int rtlsdr_rpc_msg_push_uint32(rtlsdr_rpc_msg_t*, uint32_t); void rtlsdr_rpc_msg_push_uint32_safe(rtlsdr_rpc_msg_t*, uint32_t); int rtlsdr_rpc_msg_push_str(rtlsdr_rpc_msg_t*, const char*); int rtlsdr_rpc_msg_push_buf(rtlsdr_rpc_msg_t*, const uint8_t*, size_t); void rtlsdr_rpc_msg_skip_safe(rtlsdr_rpc_msg_t*, size_t); int rtlsdr_rpc_msg_pop_int32(rtlsdr_rpc_msg_t*, int32_t*); int rtlsdr_rpc_msg_pop_uint32(rtlsdr_rpc_msg_t*, uint32_t*); int rtlsdr_rpc_msg_pop_str(rtlsdr_rpc_msg_t*, const char**); int rtlsdr_rpc_msg_pop_buf(rtlsdr_rpc_msg_t*, const uint8_t**, size_t*); #endif /* RTLSDR_RPC_MSG_H_INCLUDED */ ================================================ FILE: include/tuner_e4k.h ================================================ #ifndef _E4K_TUNER_H #define _E4K_TUNER_H /* * Elonics E4000 tuner driver * * (C) 2011-2012 by Harald Welte * (C) 2012 by Sylvain Munaut * (C) 2012 by Hoernchen * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define E4K_I2C_ADDR 0xc8 #define E4K_CHECK_ADDR 0x02 #define E4K_CHECK_VAL 0x40 enum e4k_reg { E4K_REG_MASTER1 = 0x00, E4K_REG_MASTER2 = 0x01, E4K_REG_MASTER3 = 0x02, E4K_REG_MASTER4 = 0x03, E4K_REG_MASTER5 = 0x04, E4K_REG_CLK_INP = 0x05, E4K_REG_REF_CLK = 0x06, E4K_REG_SYNTH1 = 0x07, E4K_REG_SYNTH2 = 0x08, E4K_REG_SYNTH3 = 0x09, E4K_REG_SYNTH4 = 0x0a, E4K_REG_SYNTH5 = 0x0b, E4K_REG_SYNTH6 = 0x0c, E4K_REG_SYNTH7 = 0x0d, E4K_REG_SYNTH8 = 0x0e, E4K_REG_SYNTH9 = 0x0f, E4K_REG_FILT1 = 0x10, E4K_REG_FILT2 = 0x11, E4K_REG_FILT3 = 0x12, // gap E4K_REG_GAIN1 = 0x14, E4K_REG_GAIN2 = 0x15, E4K_REG_GAIN3 = 0x16, E4K_REG_GAIN4 = 0x17, // gap E4K_REG_AGC1 = 0x1a, E4K_REG_AGC2 = 0x1b, E4K_REG_AGC3 = 0x1c, E4K_REG_AGC4 = 0x1d, E4K_REG_AGC5 = 0x1e, E4K_REG_AGC6 = 0x1f, E4K_REG_AGC7 = 0x20, E4K_REG_AGC8 = 0x21, // gap E4K_REG_AGC11 = 0x24, E4K_REG_AGC12 = 0x25, // gap E4K_REG_DC1 = 0x29, E4K_REG_DC2 = 0x2a, E4K_REG_DC3 = 0x2b, E4K_REG_DC4 = 0x2c, E4K_REG_DC5 = 0x2d, E4K_REG_DC6 = 0x2e, E4K_REG_DC7 = 0x2f, E4K_REG_DC8 = 0x30, // gap E4K_REG_QLUT0 = 0x50, E4K_REG_QLUT1 = 0x51, E4K_REG_QLUT2 = 0x52, E4K_REG_QLUT3 = 0x53, // gap E4K_REG_ILUT0 = 0x60, E4K_REG_ILUT1 = 0x61, E4K_REG_ILUT2 = 0x62, E4K_REG_ILUT3 = 0x63, // gap E4K_REG_DCTIME1 = 0x70, E4K_REG_DCTIME2 = 0x71, E4K_REG_DCTIME3 = 0x72, E4K_REG_DCTIME4 = 0x73, E4K_REG_PWM1 = 0x74, E4K_REG_PWM2 = 0x75, E4K_REG_PWM3 = 0x76, E4K_REG_PWM4 = 0x77, E4K_REG_BIAS = 0x78, E4K_REG_CLKOUT_PWDN = 0x7a, E4K_REG_CHFILT_CALIB = 0x7b, E4K_REG_I2C_REG_ADDR = 0x7d, // FIXME }; #define E4K_MASTER1_RESET (1 << 0) #define E4K_MASTER1_NORM_STBY (1 << 1) #define E4K_MASTER1_POR_DET (1 << 2) #define E4K_SYNTH1_PLL_LOCK (1 << 0) #define E4K_SYNTH1_BAND_SHIF 1 #define E4K_SYNTH7_3PHASE_EN (1 << 3) #define E4K_SYNTH8_VCOCAL_UPD (1 << 2) #define E4K_FILT3_DISABLE (1 << 5) #define E4K_AGC1_LIN_MODE (1 << 4) #define E4K_AGC1_LNA_UPDATE (1 << 5) #define E4K_AGC1_LNA_G_LOW (1 << 6) #define E4K_AGC1_LNA_G_HIGH (1 << 7) #define E4K_AGC6_LNA_CAL_REQ (1 << 4) #define E4K_AGC7_MIX_GAIN_AUTO (1 << 0) #define E4K_AGC7_GAIN_STEP_5dB (1 << 5) #define E4K_AGC8_SENS_LIN_AUTO (1 << 0) #define E4K_AGC11_LNA_GAIN_ENH (1 << 0) #define E4K_DC1_CAL_REQ (1 << 0) #define E4K_DC5_I_LUT_EN (1 << 0) #define E4K_DC5_Q_LUT_EN (1 << 1) #define E4K_DC5_RANGE_DET_EN (1 << 2) #define E4K_DC5_RANGE_EN (1 << 3) #define E4K_DC5_TIMEVAR_EN (1 << 4) #define E4K_CLKOUT_DISABLE 0x96 #define E4K_CHFCALIB_CMD (1 << 0) #define E4K_AGC1_MOD_MASK 0xF enum e4k_agc_mode { E4K_AGC_MOD_SERIAL = 0x0, E4K_AGC_MOD_IF_PWM_LNA_SERIAL = 0x1, E4K_AGC_MOD_IF_PWM_LNA_AUTONL = 0x2, E4K_AGC_MOD_IF_PWM_LNA_SUPERV = 0x3, E4K_AGC_MOD_IF_SERIAL_LNA_PWM = 0x4, E4K_AGC_MOD_IF_PWM_LNA_PWM = 0x5, E4K_AGC_MOD_IF_DIG_LNA_SERIAL = 0x6, E4K_AGC_MOD_IF_DIG_LNA_AUTON = 0x7, E4K_AGC_MOD_IF_DIG_LNA_SUPERV = 0x8, E4K_AGC_MOD_IF_SERIAL_LNA_AUTON = 0x9, E4K_AGC_MOD_IF_SERIAL_LNA_SUPERV = 0xa, }; enum e4k_band { E4K_BAND_VHF2 = 0, E4K_BAND_VHF3 = 1, E4K_BAND_UHF = 2, E4K_BAND_L = 3, }; enum e4k_mixer_filter_bw { E4K_F_MIX_BW_27M = 0, E4K_F_MIX_BW_4M6 = 8, E4K_F_MIX_BW_4M2 = 9, E4K_F_MIX_BW_3M8 = 10, E4K_F_MIX_BW_3M4 = 11, E4K_F_MIX_BW_3M = 12, E4K_F_MIX_BW_2M7 = 13, E4K_F_MIX_BW_2M3 = 14, E4K_F_MIX_BW_1M9 = 15, }; enum e4k_if_filter { E4K_IF_FILTER_MIX, E4K_IF_FILTER_CHAN, E4K_IF_FILTER_RC }; struct e4k_pll_params { uint32_t fosc; uint32_t intended_flo; uint32_t flo; uint16_t x; uint8_t z; uint8_t r; uint8_t r_idx; uint8_t threephase; }; struct e4k_state { void *i2c_dev; uint8_t i2c_addr; enum e4k_band band; struct e4k_pll_params vco; void *rtl_dev; }; int e4k_init(struct e4k_state *e4k); int e4k_standby(struct e4k_state *e4k, int enable); int e4k_if_gain_set(struct e4k_state *e4k, uint8_t stage, int8_t value); int e4k_mixer_gain_set(struct e4k_state *e4k, int8_t value); int e4k_commonmode_set(struct e4k_state *e4k, int8_t value); int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq); int e4k_tune_params(struct e4k_state *e4k, struct e4k_pll_params *p); uint32_t e4k_compute_pll_params(struct e4k_pll_params *oscp, uint32_t fosc, uint32_t intended_flo); int e4k_if_filter_bw_get(struct e4k_state *e4k, enum e4k_if_filter filter); int e4k_if_filter_bw_set(struct e4k_state *e4k, enum e4k_if_filter filter, uint32_t bandwidth); int e4k_if_filter_chan_enable(struct e4k_state *e4k, int on); int e4k_rf_filter_set(struct e4k_state *e4k); int e4k_manual_dc_offset(struct e4k_state *e4k, int8_t iofs, int8_t irange, int8_t qofs, int8_t qrange); int e4k_dc_offset_calibrate(struct e4k_state *e4k); int e4k_dc_offset_gen_table(struct e4k_state *e4k); int e4k_set_lna_gain(struct e4k_state *e4k, int32_t gain); int e4k_enable_manual_gain(struct e4k_state *e4k, uint8_t manual); int e4k_set_enh_gain(struct e4k_state *e4k, int32_t gain); #endif /* _E4K_TUNER_H */ ================================================ FILE: include/tuner_fc0012.h ================================================ /* * Fitipower FC0012 tuner driver * * Copyright (C) 2012 Hans-Frieder Vogt * * modified for use in librtlsdr * Copyright (C) 2012 Steve Markgraf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef _FC0012_H_ #define _FC0012_H_ #define FC0012_I2C_ADDR 0xc6 #define FC0012_CHECK_ADDR 0x00 #define FC0012_CHECK_VAL 0xa1 int fc0012_init(void *dev); int fc0012_set_params(void *dev, uint32_t freq, uint32_t bandwidth); int fc0012_set_gain(void *dev, int gain); int fc0012_set_i2c_register(void *dev, unsigned i2c_register, unsigned data); int fc0012_get_i2c_register(void *dev, unsigned char* data, int len); #endif ================================================ FILE: include/tuner_fc0013.h ================================================ /* * Fitipower FC0013 tuner driver * * Copyright (C) 2012 Hans-Frieder Vogt * * modified for use in librtlsdr * Copyright (C) 2012 Steve Markgraf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #ifndef _FC0013_H_ #define _FC0013_H_ #define FC0013_I2C_ADDR 0xc6 #define FC0013_CHECK_ADDR 0x00 #define FC0013_CHECK_VAL 0xa3 int fc0013_init(void *dev); int fc0013_set_params(void *dev, uint32_t freq, uint32_t bandwidth); int fc0013_set_gain_mode(void *dev, int manual); int fc0013_set_lna_gain(void *dev, int gain); #endif ================================================ FILE: include/tuner_fc2580.h ================================================ #ifndef __TUNER_FC2580_H #define __TUNER_FC2580_H #define BORDER_FREQ 2600000 /* 2.6GHz : The border frequency which determines whether Low VCO or High VCO is used */ #define USE_EXT_CLK 0 /* 0 : Use internal XTAL Oscillator / 1 : Use External Clock input */ #define OFS_RSSI 57 #define FC2580_I2C_ADDR 0xac #define FC2580_CHECK_ADDR 0x01 #define FC2580_CHECK_VAL 0x56 /* 16.384 MHz (at least on the Logilink VG0002A) */ #define FC2580_XTAL_FREQ 16384000 typedef enum { FC2580_UHF_BAND, FC2580_L_BAND, FC2580_VHF_BAND, FC2580_NO_BAND } fc2580_band_type; typedef enum { FC2580_FCI_FAIL, FC2580_FCI_SUCCESS } fc2580_fci_result_type; enum FUNCTION_STATUS { FUNCTION_SUCCESS, FUNCTION_ERROR, }; extern void fc2580_wait_msec(void *pTuner, int a); fc2580_fci_result_type fc2580_i2c_write(void *pTuner, unsigned char reg, unsigned char val); fc2580_fci_result_type fc2580_i2c_read(void *pTuner, unsigned char reg, unsigned char *read_data); /*============================================================================== fc2580 initial setting This function is a generic function which gets called to initialize fc2580 in DVB-H mode or L-Band TDMB mode ifagc_mode type : integer 1 : Internal AGC 2 : Voltage Control Mode ==============================================================================*/ fc2580_fci_result_type fc2580_set_init(void *pTuner, int ifagc_mode, unsigned int freq_xtal ); /*============================================================================== fc2580 frequency setting This function is a generic function which gets called to change LO Frequency of fc2580 in DVB-H mode or L-Band TDMB mode f_lo Value of target LO Frequency in 'kHz' unit ex) 2.6GHz = 2600000 ==============================================================================*/ fc2580_fci_result_type fc2580_set_freq(void *pTuner, unsigned int f_lo, unsigned int freq_xtal ); /*============================================================================== fc2580 filter BW setting This function is a generic function which gets called to change Bandwidth frequency of fc2580's channel selection filter filter_bw 1 : 1.53MHz(TDMB) 6 : 6MHz 7 : 7MHz 8 : 7.8MHz ==============================================================================*/ fc2580_fci_result_type fc2580_set_filter( void *pTuner, unsigned char filter_bw, unsigned int freq_xtal ); // The following context is FC2580 tuner API source code // Definitions // AGC mode enum FC2580_AGC_MODE { FC2580_AGC_INTERNAL = 1, FC2580_AGC_EXTERNAL = 2, }; // Bandwidth mode enum FC2580_BANDWIDTH_MODE { FC2580_BANDWIDTH_1530000HZ = 1, FC2580_BANDWIDTH_6000000HZ = 6, FC2580_BANDWIDTH_7000000HZ = 7, FC2580_BANDWIDTH_8000000HZ = 8, }; // Manipulaing functions int fc2580_Initialize( void *pTuner ); int fc2580_SetRfFreqHz( void *pTuner, unsigned long RfFreqHz ); // Extra manipulaing functions int fc2580_SetBandwidthMode( void *pTuner, int BandwidthMode ); #endif ================================================ FILE: include/tuner_r82xx.h ================================================ /* * Rafael Micro R820T/R828D driver * * Copyright (C) 2013 Mauro Carvalho Chehab * Copyright (C) 2013 Steve Markgraf * * This driver is a heavily modified version of the driver found in the * Linux kernel: * http://git.linuxtv.org/linux-2.6.git/history/HEAD:/drivers/media/tuners/r820t.c * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef R82XX_H #define R82XX_H #define R820T_I2C_ADDR 0x34 #define R828D_I2C_ADDR 0x74 #define R828D_XTAL_FREQ 16000000 #define R82XX_CHECK_ADDR 0x00 #define R82XX_CHECK_VAL 0x69 #define R82XX_IF_FREQ 3570000 #define REG_SHADOW_START 5 #define NUM_REGS 32 #define NUM_IMR 5 #define IMR_TRIAL 9 #define VER_NUM 49 #define USE_R82XX_ENV_VARS 0 enum r82xx_chip { CHIP_R820T, CHIP_R620D, CHIP_R828D, CHIP_R828, CHIP_R828S, CHIP_R820C, }; enum r82xx_tuner_type { TUNER_RADIO = 1, TUNER_ANALOG_TV, TUNER_DIGITAL_TV }; enum r82xx_xtal_cap_value { XTAL_LOW_CAP_30P = 0, XTAL_LOW_CAP_20P, XTAL_LOW_CAP_10P, XTAL_LOW_CAP_0P, XTAL_HIGH_CAP_0P }; struct r82xx_config { uint8_t i2c_addr; uint8_t vco_curr_min; /* VCO min/max current for R18/0x12 bits [7:5] in 0 .. 7. use 0xff for default */ uint8_t vco_curr_max; /* value is inverted: programmed is 7-value, that 0 is lowest current */ uint8_t vco_algo; int harmonic; uint32_t xtal; enum r82xx_chip rafael_chip; unsigned int max_i2c_msg_len; int use_predetect; int verbose; }; struct r82xx_priv { struct r82xx_config *cfg; uint8_t regs[NUM_REGS]; uint8_t buf[NUM_REGS + 1]; uint8_t override_data[NUM_REGS]; uint8_t override_mask[NUM_REGS]; enum r82xx_xtal_cap_value xtal_cap_sel; uint16_t pll; /* kHz */ uint64_t rf_freq; /* frequency from r82xx_set_freq() */ uint32_t int_freq; /* if frequency at which to deliver towards RTL2832U */ int32_t if_band_center_freq; /* frequency relative to zero IF, * on which the band center shall be positioned */ uint8_t fil_cal_code; uint8_t input; uint8_t last_vco_curr; int has_lock; int tuner_pll_set; int tuner_harmonic; int init_done; int sideband; int disable_dither; /* Store current mode */ uint32_t delsys; enum r82xx_tuner_type type; uint32_t bw; /* in MHz */ void *rtl_dev; int last_if_mode; int last_manual_gain; int last_extended_mode; int last_LNA_value; int last_Mixer_value; int last_VGA_value; #if USE_R82XX_ENV_VARS /* store some environment variables */ int printI2C; unsigned int filterCenter; unsigned int haveR9, valR9; unsigned int haveR10L, valR10L; unsigned int haveR10H, valR10H; unsigned int haveR11L, valR11L; unsigned int haveR11H, valR11H; unsigned int haveR13L, valR13L; unsigned int haveR13H, valR13H; unsigned int haveR14L, valR14L; unsigned int haveR14H, valR14H; unsigned int haveR30H, valR30H; unsigned int haveR30L, valR30L; #endif }; struct r82xx_freq_range { uint32_t freq; uint8_t open_d; uint8_t rf_mux_ploy; uint8_t tf_c; uint8_t xtal_cap20p; uint8_t xtal_cap10p; uint8_t xtal_cap0p; }; enum r82xx_delivery_system { SYS_UNDEFINED, SYS_DVBT, SYS_DVBT2, SYS_ISDBT, }; int r82xx_standby(struct r82xx_priv *priv); int r82xx_init(struct r82xx_priv *priv); int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq); int r82xx_set_freq64(struct r82xx_priv *priv, uint64_t freq); int r82xx_is_tuner_locked(struct r82xx_priv *priv); int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain, int extended_mode, int lna_gain, int mixer_gain, int vga_gain, int *rtl_vga_control); int r82xx_get_rf_gain(struct r82xx_priv *priv); int r82xx_get_if_gain(struct r82xx_priv *priv); int r82xx_set_if_mode(struct r82xx_priv *priv, int if_mode, int *rtl_vga_control); int r82xx_set_i2c_register(struct r82xx_priv *priv, unsigned i2c_register, unsigned data, unsigned mask); int r82xx_get_i2c_register(struct r82xx_priv *priv, unsigned char* data, int len); int r82xx_set_i2c_override(struct r82xx_priv *priv, unsigned i2c_register, unsigned data, unsigned mask); int r82xx_set_bandwidth(struct r82xx_priv *priv, int bandwidth, uint32_t rate, uint32_t * applied_bw, int apply); int r82xx_set_bw_center(struct r82xx_priv *priv, int32_t if_band_center_freq); /* Mixer Sideband: 0: lower, 1: upper */ int r82xx_set_sideband(struct r82xx_priv *priv, int sideband); int r82xx_get_sideband(struct r82xx_priv *priv); /* should rtlsdr flip the spectrum? */ int r82xx_flip_rtl_sideband(struct r82xx_priv *priv); int r82xx_set_dither(struct r82xx_priv *priv, int dither); int r82xx_read_cache_reg(struct r82xx_priv *priv, int reg); int r82xx_write_reg_mask(struct r82xx_priv *priv, uint8_t reg, uint8_t val,uint8_t bit_mask); int r82xx_write_reg_mask_ext(struct r82xx_priv *priv, uint8_t reg, uint8_t val, uint8_t bit_mask, const char * func_name); int rtlsdr_check_dongle_model(void *dev, char *manufact_check, char *product_check); #endif ================================================ FILE: install-blacklist.sh ================================================ #!/bin/bash BLACKLIST_FN="" if [ -f /etc/modprobe.d/rtlsdr-blacklist.conf ]; then BLACKLIST_FN="rtlsdr-blacklist.conf" echo "found /etc/modprobe.d/${BLACKLIST_FN}" elif [ -f /etc/modprobe.d/blacklist-rtl8xxxu.conf ]; then BLACKLIST_FN="blacklist-rtl8xxxu.conf" echo "found /etc/modprobe.d/${BLACKLIST_FN}" elif [ -f /etc/modprobe.d/raspi-blacklist.conf ]; then BLACKLIST_FN="raspi-blacklist.conf" echo "found /etc/modprobe.d/${BLACKLIST_FN}" else BLACKLIST_FN="rtlsdr-blacklist.conf" echo "could not find existing blacklist. will use /etc/modprobe.d/${BLACKLIST_FN}" fi if [ -f /etc/modprobe.d/${BLACKLIST_FN} ]; then cat /etc/modprobe.d/${BLACKLIST_FN} rtlsdr-blacklist.conf | sort | uniq >/dev/shm/${BLACKLIST_FN} cp /dev/shm/${BLACKLIST_FN} /etc/modprobe.d/${BLACKLIST_FN} echo "updated /etc/modprobe.d/${BLACKLIST_FN} ; reboot to apply" else cp rtlsdr-blacklist.conf /etc/modprobe.d/${BLACKLIST_FN} echo "created /etc/modprobe.d/${BLACKLIST_FN} ; reboot to apply" fi ================================================ FILE: librtlsdr.pc.in ================================================ prefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: RTL-SDR Library Description: C Utility Library Version: @VERSION@ Cflags: -I${includedir}/ @RTLSDR_PC_CFLAGS@ Libs: -L${libdir} -lrtlsdr -lusb-1.0 Libs.private: @RTLSDR_PC_LIBS@ ================================================ FILE: m4/.gitignore ================================================ /libtool.m4 /lt*.m4 ================================================ FILE: mingw-w32-i686.cmake ================================================ # Sample toolchain file for building for Windows from an Ubuntu Linux system. # # Typical usage: # *) install cross compiler: `sudo apt-get install mingw-w64` # *) cd build # *) cmake -DCMAKE_TOOLCHAIN_FILE=~/mingw-w32-i686.cmake .. # # build for Windows' 32 bit architecture set(CMAKE_SYSTEM_NAME Windows) set(TOOLCHAIN_PREFIX i686-w64-mingw32) # cross compilers to use for C, C++ and Fortran set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) # target environment on the build host system set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) # modify default behavior of FIND_XXX() commands set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) ================================================ FILE: mingw-w64-x64_64.cmake ================================================ # Sample toolchain file for building for Windows from an Ubuntu Linux system. # # Typical usage: # *) install cross compiler: `sudo apt-get install mingw-w64` # *) cd build # *) cmake -DCMAKE_TOOLCHAIN_FILE=~/mingw-w64-x86_64.cmake .. # # build for Windows' 64 bit architecture set(CMAKE_SYSTEM_NAME Windows) set(TOOLCHAIN_PREFIX x86_64-w64-mingw32) # cross compilers to use for C, C++ and Fortran set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}-gcc) set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}-g++) set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) # target environment on the build host system set(CMAKE_FIND_ROOT_PATH /usr/${TOOLCHAIN_PREFIX}) # modify default behavior of FIND_XXX() commands set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) ================================================ FILE: protocol_rtl_tcp.txt ================================================ The command protocol of rtl_tcp is on port 1234 (by default): ============================================================= Commands are directed from a client application towards rtl_tcp. Each command consists of 2 fields: 1- command_id: value of enum RTL_TCP_COMMANDS, defined in rtl_tcp.h. This element is an unsigned character, starting at offset 0 with length: 1 byte. 2- parameter: depends on the command id, e.g. is a frequency for SET_FREQUENCY. This element is a (signed or unsigned) 32 bit integer, starting at offset 1 with length: 4 bytes. Both fields are in Network Byte Order (Big Endian). Size of one command is 3 bytes, without padding. Reverse direction on command connection: ======================================== With accepting a connection, rtl_tcp initially transmits some dongle_info. The dongle_info consists of 3 fields: 1- magic string: identifies the rtl_tcp protocol. The value is "RTL0". This element is a fixed size string, starting at offset 0 with length 4 bytes. 2- tuner type: identifies the tuner chip built in the controlled RTL-SDR model, e.g. E4000 or R820T or R820T2. This element is an unsigned 32 bit integer, starting at offset 4 with length: 4 bytes. 3- tuner gain count: reports the number of available gain values, necessary for the command SET_TUNER_GAIN_BY_INDEX. This element is an unsigned 32 bit integer, starting at offset 8 with length: 4 bytes. Both fields are in Network Byte Order (Big Endian). Size of one dongle_info is 12 bytes, without padding. After that initial dongle_info, from offset 12 byte, just raw I/Q data is transferred. For each I/Q frame, in the commanded samplerate, I and Q values are transferred, each as unsigned 8 bit sample. Thus one I/Q-frame is 2 bytes, each frame 1 byte. Response channel: ================= The command channel does not allow specific response on commands. This is why an additional response channel got necessary. rtl_tcp now offers an additional tcp server "response" channel, by default, on the next port as the command protocol. Thus, by default on port 1235. Each response from rtl_tcp to the client consists of 3 fields: 1- command_id: references the command, which generated this response. It's value is also the enum RTL_TCP_COMMANDS, defined in rtl_tcp.h. This element is an unsigned character, starting at offset 0 with length: 1 byte. 2- length_of_content: defines the number of bytes following this field at offset 3. The length is necessary to allow responses with different lengths. This element is an unsigned 16 bit integer, starting at offset 1 with length: 2 bytes. 0 is interpreted as 65536! It is strongly recommended to limit the response to 32768 bytes in total. 3- content: the content is command specific and might contain up to 65536 bytes. The content starts at offset 3. All fields are in Network Byte Order (Big Endian). Size of one response is (3 + length_of_content), without padding. ================================================ FILE: rtl-sdr.rules ================================================ # # Copyright 2012-2013 Osmocom rtl-sdr project # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # original RTL2832U vid/pid (hama nano, for example) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2832", MODE:="0666" # modified RTL2832U vid/pid .. not known to dvb modules SUBSYSTEMS=="usb", ATTRS{idVendor}=="1209", ATTRS{idProduct}=="2832", MODE:="0666" # RTL2832U OEM vid/pid, e.g. ezcap EzTV668 (E4000), Newsky TV28T (E4000/R820T) etc. SUBSYSTEMS=="usb", ATTRS{idVendor}=="0bda", ATTRS{idProduct}=="2838", MODE:="0666" # DigitalNow Quad DVB-T PCI-E card (4x FC0012?) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0413", ATTRS{idProduct}=="6680", MODE:="0666" # Leadtek WinFast DTV Dongle mini D (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0413", ATTRS{idProduct}=="6f0f", MODE:="0666" # Genius TVGo DVB-T03 USB dongle (Ver. B) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0458", ATTRS{idProduct}=="707f", MODE:="0666" # Terratec Cinergy T Stick Black (rev 1) (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00a9", MODE:="0666" # Terratec NOXON rev 1 (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b3", MODE:="0666" # Terratec Deutschlandradio DAB Stick (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b4", MODE:="0666" # Terratec NOXON DAB Stick - Radio Energy (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b5", MODE:="0666" # Terratec Media Broadcast DAB Stick (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b7", MODE:="0666" # Terratec BR DAB Stick (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b8", MODE:="0666" # Terratec WDR DAB Stick (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00b9", MODE:="0666" # Terratec MuellerVerlag DAB Stick (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00c0", MODE:="0666" # Terratec Fraunhofer DAB Stick (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00c6", MODE:="0666" # Terratec Cinergy T Stick RC (Rev.3) (E4000) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00d3", MODE:="0666" # Terratec T Stick PLUS (E4000) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00d7", MODE:="0666" # Terratec NOXON rev 2 (E4000) SUBSYSTEMS=="usb", ATTRS{idVendor}=="0ccd", ATTRS{idProduct}=="00e0", MODE:="0666" # PixelView PV-DT235U(RN) (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1554", ATTRS{idProduct}=="5020", MODE:="0666" # Astrometa DVB-T/DVB-T2 (R828D) SUBSYSTEMS=="usb", ATTRS{idVendor}=="15f4", ATTRS{idProduct}=="0131", MODE:="0666" # HanfTek DAB+FM+DVB-T SUBSYSTEMS=="usb", ATTRS{idVendor}=="15f4", ATTRS{idProduct}=="0133", MODE:="0666" # Compro Videomate U620F (E4000) SUBSYSTEMS=="usb", ATTRS{idVendor}=="185b", ATTRS{idProduct}=="0620", MODE:="0666" # Compro Videomate U650F (E4000) SUBSYSTEMS=="usb", ATTRS{idVendor}=="185b", ATTRS{idProduct}=="0650", MODE:="0666" # Compro Videomate U680F (E4000) SUBSYSTEMS=="usb", ATTRS{idVendor}=="185b", ATTRS{idProduct}=="0680", MODE:="0666" # GIGABYTE GT-U7300 (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d393", MODE:="0666" # DIKOM USB-DVBT HD SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d394", MODE:="0666" # Peak 102569AGPK (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d395", MODE:="0666" # KWorld KW-UB450-T USB DVB-T Pico TV (TUA9001) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d397", MODE:="0666" # Zaapa ZT-MINDVBZP (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d398", MODE:="0666" # SVEON STV20 DVB-T USB & FM (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d39d", MODE:="0666" # Twintech UT-40 (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d3a4", MODE:="0666" # ASUS U3100MINI_PLUS_V2 (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d3a8", MODE:="0666" # SVEON STV27 DVB-T USB & FM (FC0013) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d3af", MODE:="0666" # SVEON STV21 DVB-T USB & FM SUBSYSTEMS=="usb", ATTRS{idVendor}=="1b80", ATTRS{idProduct}=="d3b0", MODE:="0666" # Dexatek DK DVB-T Dongle (Logilink VG0002A) (FC2580) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d19", ATTRS{idProduct}=="1101", MODE:="0666" # Dexatek DK DVB-T Dongle (MSI DigiVox mini II V3.0) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d19", ATTRS{idProduct}=="1102", MODE:="0666" # Dexatek DK 5217 DVB-T Dongle (FC2580) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d19", ATTRS{idProduct}=="1103", MODE:="0666" # MSI DigiVox Micro HD (FC2580) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1d19", ATTRS{idProduct}=="1104", MODE:="0666" # Sweex DVB-T USB (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1f4d", ATTRS{idProduct}=="a803", MODE:="0666" # GTek T803 (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1f4d", ATTRS{idProduct}=="b803", MODE:="0666" # Lifeview LV5TDeluxe (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1f4d", ATTRS{idProduct}=="c803", MODE:="0666" # MyGica TD312 (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1f4d", ATTRS{idProduct}=="d286", MODE:="0666" # PROlectrix DV107669 (FC0012) SUBSYSTEMS=="usb", ATTRS{idVendor}=="1f4d", ATTRS{idProduct}=="d803", MODE:="0666" ================================================ FILE: rtlsdr-blacklist.conf ================================================ blacklist dvb_usb_rtl28xxu blacklist dvb_usb_v2 blacklist rtl_2830 blacklist rtl_2832 blacklist r820t blacklist rtl8xxxu ================================================ FILE: src/CMakeLists.txt ================================================ # Copyright 2012 OSMOCOM Project # # This file is part of rtl-sdr # # GNU Radio is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3, or (at your option) # any later version. # # GNU Radio is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GNU Radio; see the file COPYING. If not, write to # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. MACRO(RTLSDR_APPEND_SRCS) LIST(APPEND rtlsdr_srcs ${ARGV}) ENDMACRO(RTLSDR_APPEND_SRCS) RTLSDR_APPEND_SRCS( librtlsdr.c tuner_e4k.c tuner_fc0012.c tuner_fc0013.c tuner_fc2580.c tuner_r82xx.c ) if(WITH_RPC) RTLSDR_APPEND_SRCS( rtlsdr_rpc.c rtlsdr_rpc_msg.c ) endif() ######################################################################## # Set up Windows DLL resource files ######################################################################## IF(MSVC) include(${PROJECT_SOURCE_DIR}/cmake/Modules/Version.cmake) configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/rtlsdr.rc.in ${CMAKE_CURRENT_BINARY_DIR}/rtlsdr.rc @ONLY) RTLSDR_APPEND_SRCS(${CMAKE_CURRENT_BINARY_DIR}/rtlsdr.rc) ENDIF(MSVC) ######################################################################## # Setup shared library variant ######################################################################## add_library(rtlsdr_shared SHARED ${rtlsdr_srcs}) if(NOT WIN32) target_link_libraries(rtlsdr_shared ${LIBUSB_LIBRARIES}) else() target_link_libraries(rtlsdr_shared ws2_32 ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) endif() if (WITH_RPC) target_compile_definitions(rtlsdr_shared PUBLIC _ENABLE_RPC) endif() set_target_properties(rtlsdr_shared PROPERTIES DEFINE_SYMBOL "rtlsdr_EXPORTS") set_target_properties(rtlsdr_shared PROPERTIES OUTPUT_NAME rtlsdr) set_target_properties(rtlsdr_shared PROPERTIES SOVERSION ${MAJOR_VERSION}) set_target_properties(rtlsdr_shared PROPERTIES VERSION ${LIBVER}) ######################################################################## # Setup static library variant ######################################################################## add_library(rtlsdr_static STATIC ${rtlsdr_srcs}) if(NOT WIN32) target_link_libraries(rtlsdr_static ${LIBUSB_LIBRARIES}) else() target_link_libraries(rtlsdr_static ws2_32 ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) endif() if (WITH_RPC) target_compile_definitions(rtlsdr_static PUBLIC _ENABLE_RPC) endif() set_property(TARGET rtlsdr_static APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) if(NOT WIN32) # Force same library filename for static and shared variants of the library set_target_properties(rtlsdr_static PROPERTIES OUTPUT_NAME rtlsdr) endif() ######################################################################## # Set link library ######################################################################## if(LINK_RTLTOOLS_AGAINST_STATIC_LIB) set(RTLSDR_TOOL_LIB rtlsdr_static) else() set(RTLSDR_TOOL_LIB rtlsdr_shared) endif() ######################################################################## # Setup libraries used in executables ######################################################################## add_library(convenience_static STATIC convenience/rtl_convenience.c convenience/convenience.c convenience/wavewrite.c ) if(WIN32) add_library(libgetopt_static STATIC getopt/getopt.c ) target_link_libraries(convenience_static ${RTLSDR_TOOL_LIB} ) endif() ######################################################################## # Build utility ######################################################################## add_executable(rtl_sdr rtl_sdr.c) add_executable(rtl_tcp rtl_tcp.c controlThread.c) add_executable(rtl_udp rtl_udp.c) add_executable(rtl_test rtl_test.c) add_executable(rtl_fm rtl_fm.c) add_executable(rtl_ir rtl_ir.c) add_executable(rtl_eeprom rtl_eeprom.c) add_executable(rtl_adsb rtl_adsb.c) add_executable(rtl_power rtl_power.c) add_executable(rtl_biast rtl_biast.c) add_executable(rtl_raw2wav rtl_raw2wav.c convenience/convenience.c convenience/wavewrite.c) add_executable(rtl_wavestat rtl_wavestat.c convenience/convenience.c convenience/waveread.c) add_executable(rtl_wavestream rtl_wavestream.c convenience/convenience.c convenience/waveread.c) if (WITH_RPC) add_executable(rtl_rpcd rtl_rpcd.c rtlsdr_rpc_msg.c) set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_udp rtl_test rtl_fm rtl_ir rtl_eeprom rtl_adsb rtl_power rtl_biast rtl_raw2wav rtl_wavestat rtl_wavestream rtl_rpcd) else() set(INSTALL_TARGETS rtlsdr_shared rtlsdr_static rtl_sdr rtl_tcp rtl_udp rtl_test rtl_fm rtl_ir rtl_eeprom rtl_adsb rtl_power rtl_biast rtl_raw2wav rtl_wavestat rtl_wavestream) endif() target_link_libraries(rtl_sdr ${RTLSDR_TOOL_LIB} convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) target_link_libraries(rtl_tcp ${RTLSDR_TOOL_LIB} convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) target_link_libraries(rtl_test ${RTLSDR_TOOL_LIB} convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) target_link_libraries(rtl_fm ${RTLSDR_TOOL_LIB} convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) target_link_libraries(rtl_ir ${RTLSDR_TOOL_LIB} convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) target_link_libraries(rtl_eeprom ${RTLSDR_TOOL_LIB} convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) target_link_libraries(rtl_adsb ${RTLSDR_TOOL_LIB} convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) target_link_libraries(rtl_udp ${RTLSDR_TOOL_LIB} convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) target_link_libraries(rtl_power ${RTLSDR_TOOL_LIB} convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) target_link_libraries(rtl_biast ${RTLSDR_TOOL_LIB} convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) if(WITH_RPC) target_link_libraries(rtl_rpcd ${RTLSDR_TOOL_LIB} convenience_static ${LIBUSB_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ) endif() if(UNIX) target_link_libraries(rtl_fm m) target_link_libraries(rtl_ir m) target_link_libraries(rtl_adsb m) target_link_libraries(rtl_power m) target_link_libraries(rtl_raw2wav m) target_link_libraries(rtl_wavestat m) target_link_libraries(rtl_wavestream m) if(APPLE OR CMAKE_SYSTEM MATCHES "OpenBSD") target_link_libraries(rtl_test m) else() target_link_libraries(rtl_test m rt) endif() endif() if(WIN32) target_link_libraries(rtl_sdr libgetopt_static) target_link_libraries(rtl_tcp ws2_32 libgetopt_static) target_link_libraries(rtl_udp ws2_32 libgetopt_static) target_link_libraries(rtl_test libgetopt_static) target_link_libraries(rtl_fm libgetopt_static) target_link_libraries(rtl_ir libgetopt_static) target_link_libraries(rtl_eeprom libgetopt_static) target_link_libraries(rtl_adsb libgetopt_static) target_link_libraries(rtl_power libgetopt_static) target_link_libraries(rtl_biast libgetopt_static) target_link_libraries(rtl_raw2wav libgetopt_static) target_link_libraries(rtl_wavestat libgetopt_static) target_link_libraries(rtl_wavestream libgetopt_static) find_library(LIBM NAMES m) if (LIBM) target_link_libraries(rtl_raw2wav m) target_link_libraries(rtl_wavestat m) target_link_libraries(rtl_wavestream m) endif (LIBM) set_property(TARGET rtl_sdr APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_tcp APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_udp APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_test APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_fm APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_ir APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_eeprom APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_adsb APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_power APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_biast APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_raw2wav APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_wavestat APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) set_property(TARGET rtl_wavestream APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) if (WITH_RPC) target_link_libraries(rtl_rpcd ws2_32 libgetopt_static) set_property(TARGET rtl_rpcd APPEND PROPERTY COMPILE_DEFINITIONS "rtlsdr_STATIC" ) endif() endif() ######################################################################## # Install built library files & utilities ######################################################################## install(TARGETS ${INSTALL_TARGETS} LIBRARY DESTINATION ${LIB_INSTALL_DIR} # .so/.dylib file ARCHIVE DESTINATION ${LIB_INSTALL_DIR} # .lib file RUNTIME DESTINATION bin # .dll file ) ================================================ FILE: src/Makefile.am ================================================ # This is _NOT_ the library release version, it's an API version. # Please read Chapter 6 "Library interface versions" of the libtool documentation before making any modification LIBVERSION=0:5:0 AUTOMAKE_OPTIONS = subdir-objects INCLUDES = $(all_includes) -I$(top_srcdir)/include noinst_HEADERS = convenience/convenience.h AM_CFLAGS = ${CFLAGS} -fPIC ${SYMBOL_VISIBILITY} lib_LTLIBRARIES = librtlsdr.la librtlsdr_la_SOURCES = librtlsdr.c tuner_e4k.c tuner_fc0012.c tuner_fc0013.c tuner_fc2580.c tuner_r82xx.c rtlsdr_rpc.c rtlsdr_rpc_msg.c librtlsdr_la_LDFLAGS = -version-info $(LIBVERSION) bin_PROGRAMS = rtl_sdr rtl_tcp rtl_test rtl_fm rtl_ir rtl_eeprom rtl_adsb rtl_power rtl_rpcd rtl_sdr_SOURCES = rtl_sdr.c convenience/convenience.c rtl_sdr_LDADD = librtlsdr.la rtl_tcp_SOURCES = rtl_tcp.c convenience/convenience.c rtl_tcp_LDADD = librtlsdr.la rtl_test_SOURCES = rtl_test.c convenience/convenience.c rtl_test_LDADD = librtlsdr.la $(LIBM) rtl_fm_SOURCES = rtl_fm.c convenience/convenience.c rtl_fm_LDADD = librtlsdr.la $(LIBM) rtl_ir_SOURCES = rtl_ir.c convenience/convenience.c rtl_ir_LDADD = librtlsdr.la $(LIBM) rtl_eeprom_SOURCES = rtl_eeprom.c convenience/convenience.c rtl_eeprom_LDADD = librtlsdr.la $(LIBM) rtl_adsb_SOURCES = rtl_adsb.c convenience/convenience.c rtl_adsb_LDADD = librtlsdr.la $(LIBM) rtl_power_SOURCES = rtl_power.c convenience/convenience.c rtl_power_LDADD = librtlsdr.la $(LIBM) rtl_rpcd_SOURCES = rtl_rpcd.c rtlsdr_rpc_msg.c convenience/convenience.c rtl_rpcd_LDADD = librtlsdr.la ================================================ FILE: src/controlThread.c ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2012 by Steve Markgraf * Copyright (C) 2012-2013 by Hoernchen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #ifndef _WIN32 #include #include #include #include #include #include #include #include #else #include #include "getopt/getopt.h" #define usleep(x) Sleep(x/1000) #endif #ifdef NEED_PTHREADS_WORKARROUND #define HAVE_STRUCT_TIMESPEC #endif #include #include "rtl-sdr.h" #include "rtl_tcp.h" #include "controlThread.h" #include "convenience/convenience.h" #include "convenience/rtl_convenience.h" #include "tuner_r82xx.h" #ifdef _WIN32 #pragma comment(lib, "ws2_32.lib") typedef int socklen_t; #else #define closesocket close #define SOCKADDR struct sockaddr #define SOCKET int #define SOCKET_ERROR -1 #endif /* we need a message id in the protocol: 1st 2 byte (little endian) == message id */ #define USE_MSGID_IN_PROTOCOL 1 #define NUM_I2C_REGISTERS 32 #define TX_BUF_LEN (NUM_I2C_REGISTERS +4) //2 len, 1 head, 1 tail ctrl_thread_data_t ctrl_thread_data; void *ctrl_thread_fn(void *arg) { unsigned char reg_values [NUM_I2C_REGISTERS]; #if USE_MSGID_IN_PROTOCOL unsigned char txbuf [2+2 +1+NUM_I2C_REGISTERS+1]; //2 type, 2 length, 1 head, 1 tail #else unsigned char txbuf [NUM_I2C_REGISTERS+4]; //2 length, 1 head, 1 tail #endif int r = 1; struct timeval tv = { 1,0 }; struct linger ling = { 1,0 }; SOCKET listensocket; SOCKET controlSocket; int haveControlSocket = 0; struct sockaddr_in local, remote; socklen_t rlen; int error = 0; int ret = 0, len, result; fd_set connfds; fd_set writefds; int bytesleft, bytessent, index; ctrl_thread_data_t *data = (ctrl_thread_data_t *)arg; rtlsdr_dev_t *dev = data->dev; int port = data->port; int wait = data->wait; int report_i2c = data->report_i2c; char *addr = data->addr; int* do_exit = data->pDoExit; u_long blockmode = 1; int retval; memset(reg_values, 0, NUM_I2C_REGISTERS); memset(&local, 0, sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(port); local.sin_addr.s_addr = inet_addr(addr); listensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); setsockopt(listensocket, SOL_SOCKET, SO_REUSEADDR, (char *)&r, sizeof(int)); setsockopt(listensocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); retval = bind(listensocket, (struct sockaddr *)&local, sizeof(local)); if (retval == SOCKET_ERROR) error = 1; #ifdef _WIN32 ioctlsocket(listensocket, FIONBIO, &blockmode); #else r = fcntl(listensocket, F_GETFL, 0); r = fcntl(listensocket, F_SETFL, r | O_NONBLOCK); #endif while (1) { printf("listening on Control port %d...\n", port); retval = listen(listensocket, 1); if (retval == SOCKET_ERROR) error = 1; while (1) { FD_ZERO(&connfds); FD_SET(listensocket, &connfds); tv.tv_sec = 1; tv.tv_usec = 0; r = select(listensocket + 1, &connfds, NULL, NULL, &tv); if (*do_exit) { goto close; } else if (r) { rlen = sizeof(remote); controlSocket = accept(listensocket, (struct sockaddr *)&remote, &rlen); haveControlSocket = 1; break; } } setsockopt(controlSocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); printf("Control client accepted!\n"); usleep(5000000); while (1) { /* check if i2c reporting is to be (de)activated */ if ( report_i2c && !data->report_i2c ) report_i2c = 0; else if ( !report_i2c && data->report_i2c ) report_i2c = 1; /* @TODO: check if something else has to be transmitted */ if ( !report_i2c ) goto sleep; result = rtlsdr_get_tuner_i2c_register(dev, reg_values, NUM_I2C_REGISTERS); /* printf("rtlsdr_get_tuner_i2c_register\n"); */ memset(txbuf, 0, TX_BUF_LEN); if (result) goto sleep; /* Little Endian */ len = 0; /* we need some message id: use enum RTL_TCP_COMMANDS */ #if USE_MSGID_IN_PROTOCOL txbuf[len++] = REPORT_I2C_REGS & 0x0FF; txbuf[len++] = (REPORT_I2C_REGS >> 8) & 0x0FF; /* following message length in Little Endian */ txbuf[len++] = TX_BUF_LEN - 2 - 2; /* sub message id and length field */ #else txbuf[len++] = TX_BUF_LEN - 2; /* sub message id and length field */ #endif txbuf[len++] = 0; /* now the message contents */ txbuf[len++] = 0x55; /* @CS: do we need this? */ memcpy(&txbuf[len], reg_values, NUM_I2C_REGISTERS); txbuf[TX_BUF_LEN - 1] = 0xaa; /* @CS: do we need this? */ len = sizeof(txbuf); /* now start (possibly blocking) transmission */ bytessent = 0; bytesleft = len; index = 0; while (bytesleft > 0) { FD_ZERO(&writefds); FD_SET(controlSocket, &writefds); tv.tv_sec = 1; tv.tv_usec = 0; r = select(controlSocket + 1, NULL, &writefds, NULL, &tv); if (r) { bytessent = send(controlSocket, &txbuf[index], bytesleft, 0); bytesleft -= bytessent; index += bytessent; } if (bytessent == SOCKET_ERROR || *do_exit) { goto close; } } sleep: usleep(wait); } close: if (haveControlSocket) closesocket(controlSocket); if (*do_exit) { closesocket(listensocket); printf("Control Thread terminates\n"); break; } } return 0; } ================================================ FILE: src/convenience/convenience.c ================================================ /* * Copyright (C) 2014 by Kyle Keen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* a collection of user friendly tools * todo: use strtol for more flexible int parsing * */ #include #include #include #include #include #include #ifndef _WIN32 #include #include #else #include #include #include #include #define _USE_MATH_DEFINES #endif #include double atofs(char *s) /* standard suffixes */ { char last; int len; double suff = 1.0; len = strlen(s); /* allow formatting spaces from .csv command file */ while ( len > 1 && isspace(s[len-1]) ) --len; last = s[len-1]; s[len-1] = '\0'; switch (last) { case 'g': case 'G': suff *= 1e3; /* fall-through */ case 'm': case 'M': suff *= 1e3; /* fall-through */ case 'k': case 'K': suff *= 1e3; suff *= atof(s); s[len-1] = last; return suff; } s[len-1] = last; return atof(s); } double atoft(char *s) /* time suffixes, returns seconds */ { char last; int len; double suff = 1.0; len = strlen(s); last = s[len-1]; s[len-1] = '\0'; switch (last) { case 'h': case 'H': suff *= 60; /* fall-through */ case 'm': case 'M': suff *= 60; /* fall-through */ case 's': case 'S': suff *= atof(s); s[len-1] = last; return suff; } s[len-1] = last; return atof(s); } double atofp(char *s) /* percent suffixes */ { char last; int len; double suff = 1.0; len = strlen(s); last = s[len-1]; s[len-1] = '\0'; switch (last) { case '%': suff *= 0.01; suff *= atof(s); s[len-1] = last; return suff; } s[len-1] = last; return atof(s); } static struct tm * str_to_tm( const char * str, struct tm * t, double * fraction ) { char b[16]; int k, v; /* 0 1 2 */ /* 01234567890123456789012 */ /* 2019-09-15T01:53:20.234 - mostly ISO 8601 */ *fraction = 0.0; t->tm_sec = 0; t->tm_min = 0; t->tm_hour = 0; t->tm_mday = 1; t->tm_mon = 0; t->tm_year = 0; t->tm_wday = 0; t->tm_yday = 0; t->tm_isdst = -1; /* date */ if ( (str[4] == '-' || str[4] == '/') && str[4] == str[7] ) { /* year */ b[4] = 0; for ( k = 0; k < 4; ++k ) b[k] = str[k]; v = atoi(b); t->tm_year = v - 1900; /* month */ b[2] = 0; for ( k = 0; k < 2; ++k ) b[k] = str[5+k]; v = atoi(b); if (v < 1 || v > 12) return NULL; t->tm_mon = v - 1; /* day */ b[2] = 0; for ( k = 0; k < 2; ++k ) b[k] = str[8+k]; v = atoi(b); if (v < 1 || v > 31) return NULL; t->tm_mday = v; } else return NULL; if (str[10] == 0 ) return t; /* time */ if ( str[10] != 'T' && str[10] != ' ' && str[10] != '_' ) return NULL; if ( (str[13] == ':' || str[13] == '/') && str[13] == str[16] ) { /* hour */ b[2] = 0; for ( k = 0; k < 2; ++k ) b[k] = str[11+k]; v = atoi(b); if (v < 0 || v > 23) return NULL; t->tm_hour = v; /* minute */ b[2] = 0; for ( k = 0; k < 2; ++k ) b[k] = str[14+k]; v = atoi(b); if (v < 0 || v > 59) return NULL; t->tm_min = v; /* second */ b[2] = 0; for ( k = 0; k < 2; ++k ) b[k] = str[17+k]; v = atoi(b); if (v < 0 || v > 61) return NULL; t->tm_sec = v; } else return NULL; if (str[19] == 0 ) return t; /* fraction */ if ( str[19] == '.' || str[19] == ',' ) { for ( k = 0; k < 16; ++k ) b[k] = 0; strcpy(b, "0."); strncpy(&b[2], &str[20], 12); *fraction = atof(b); return t; } /* return t anyway .. without fraction */ return t; } time_t utctimestr_to_time(const char * str, double * fraction) { struct tm t; struct tm *p; #ifdef _WIN32 struct tm gtm; struct tm ltm; time_t nt; time_t gt; time_t lt; #endif p = str_to_tm( str, &t, fraction ); if (!p) return 0; p->tm_isdst = 0; #ifndef _WIN32 return timegm(p); #else #ifdef _MSC_VER return _mkgmtime(p); #else /* workaround missing mkgmtime on mingw */ nt = mktime(p); gtm = *gmtime(&nt); ltm = *localtime(&nt); gt = mktime(>m); lt = mktime(<m); assert( nt == gt ); nt += ( lt - gt ); return nt; #endif #endif } time_t localtimestr_to_time(const char * str, double * fraction) { struct tm t; struct tm *p; p = str_to_tm( str, &t, fraction ); if (!p) return 0; #ifndef _WIN32 return timelocal(p); #else return mktime(p); #endif } #ifndef _WIN32 void executeInBackground( char * file, char * args, char * searchStr[], char * replaceStr[] ) { pid_t pid; char * argv[256] = { NULL }; int k, argc = 0; argv[argc++] = file; if (args) { argv[argc] = strtok(args, " "); while (argc < 256 && argv[argc]) { argv[++argc] = strtok(NULL, " "); for (k=0; argv[argc] && searchStr && replaceStr && searchStr[k] && replaceStr[k]; k++) { if (!strcmp(argv[argc], searchStr[k])) { argv[argc] = replaceStr[k]; break; } } } } pid = fork(); switch (pid) { case -1: /* Fork() has failed */ fprintf(stderr, "error: fork for '%s' failed!\n", file); break; case 0: execvp(file, argv); fprintf(stderr, "error: execv of '%s' from within fork failed!\n", file); exit(10); break; default: /* This is processed by the parent */ break; } } #else void executeInBackground( char * file, char * args, char * searchStr[], char * replaceStr[] ) { char * argv[256] = { NULL }; int k, argc = 0; argv[argc++] = file; if (args) { argv[argc] = strtok(args, " \t"); while (argc < 256 && argv[argc]) { argv[++argc] = strtok(NULL, " \t"); for (k=0; argv[argc] && searchStr && replaceStr && searchStr[k] && replaceStr[k]; k++) { if (!strcmp(argv[argc], searchStr[k])) { argv[argc] = replaceStr[k]; break; } } } } spawnvp(P_NOWAIT, file, argv); } #endif // vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab ================================================ FILE: src/convenience/convenience.h ================================================ /* * Copyright (C) 2014 by Kyle Keen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __CONVENIENCE_H #define __CONVENIENCE_H #include #include #include #ifdef __cplusplus extern "C" { #endif /* a collection of user friendly tools */ /*! * Convert standard suffixes (k, M, G) to double * * \param s a string to be parsed * \return double */ double atofs(char *s); /*! * Convert time suffixes (s, m, h) to double * * \param s a string to be parsed * \return seconds as double */ double atoft(char *s); /*! * Convert percent suffixe (%) to double * * \param s a string to be parsed * \return double */ double atofp(char *s); time_t utctimestr_to_time(const char * str, double * fraction); time_t localtimestr_to_time(const char * str, double * fraction); void executeInBackground( char * file, char * args, char * searchStr[], char * replaceStr[] ); #ifdef __cplusplus } #endif #endif /*__CONVENIENCE_H*/ ================================================ FILE: src/convenience/rtl_convenience.c ================================================ /* * Copyright (C) 2014 by Kyle Keen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* a collection of user friendly tools * todo: use strtol for more flexible int parsing * */ #include #include #include #include #include #include #ifndef _WIN32 #include #include #else #include #include #include #define _USE_MATH_DEFINES #endif #include #include "rtl-sdr.h" int nearest_gain(rtlsdr_dev_t *dev, int target_gain) { int i, r, err1, err2, count, nearest; int* gains; r = rtlsdr_set_tuner_gain_mode(dev, 1); if (r < 0) { fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); return r; } count = rtlsdr_get_tuner_gains(dev, NULL); if (count <= 0) { return 0; } gains = malloc(sizeof(int) * count); count = rtlsdr_get_tuner_gains(dev, gains); nearest = gains[0]; for (i=0; i 0) { if (applied_bw) fprintf(stderr, "Bandwidth parameter %u Hz resulted in %u Hz.\n", bandwidth, applied_bw); else fprintf(stderr, "Set bandwidth parameter %u Hz.\n", bandwidth); } else { fprintf(stderr, "Bandwidth set to automatic resulted in %u Hz.\n", applied_bw); } return r; } int verbose_direct_sampling(rtlsdr_dev_t *dev, int on) { int r; r = rtlsdr_set_direct_sampling(dev, on); if (r != 0) { fprintf(stderr, "WARNING: Failed to set direct sampling mode.\n"); return r; } if (on == 0) { fprintf(stderr, "Direct sampling mode disabled.\n");} if (on == 1) { fprintf(stderr, "Enabled direct sampling mode, input 1/I.\n");} if (on == 2) { fprintf(stderr, "Enabled direct sampling mode, input 2/Q.\n");} return r; } int verbose_offset_tuning(rtlsdr_dev_t *dev) { int r; r = rtlsdr_set_offset_tuning(dev, 1); if (r != 0) { if ( r == -2 ) fprintf(stderr, "WARNING: Failed to set offset tuning: tuner doesn't support offset tuning!\n"); else if ( r == -3 ) fprintf(stderr, "WARNING: Failed to set offset tuning: direct sampling not combinable with offset tuning!\n"); else fprintf(stderr, "WARNING: Failed to set offset tuning.\n"); } else { fprintf(stderr, "Offset tuning mode enabled.\n"); } return r; } int verbose_auto_gain(rtlsdr_dev_t *dev) { int r; r = rtlsdr_set_tuner_gain_mode(dev, 0); if (r != 0) { fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); } else { fprintf(stderr, "Tuner gain set to automatic.\n"); } return r; } int verbose_gain_set(rtlsdr_dev_t *dev, int gain) { int r; r = rtlsdr_set_tuner_gain_mode(dev, 1); if (r < 0) { fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); return r; } r = rtlsdr_set_tuner_gain(dev, gain); if (r != 0) { fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); } else { fprintf(stderr, "Tuner gain set to %0.2f dB.\n", gain/10.0); } return r; } int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error) { int r; if (ppm_error == 0) { return 0;} r = rtlsdr_set_freq_correction(dev, ppm_error); if (r < 0) { fprintf(stderr, "WARNING: Failed to set ppm error.\n"); } else { fprintf(stderr, "Tuner error set to %i ppm.\n", ppm_error); } return r; } int verbose_reset_buffer(rtlsdr_dev_t *dev) { int r; r = rtlsdr_reset_buffer(dev); if (r < 0) { fprintf(stderr, "WARNING: Failed to reset buffers.\n");} return r; } int verbose_device_search(char *s) { int i, device_count, device, offset, rc; char *s2; char vendor[256] = {0}, product[256] = {0}, serial[256] = {0}; device_count = rtlsdr_get_device_count(); if (!device_count) { fprintf(stderr, "No supported devices found.\n"); return -1; } fprintf(stderr, "Found %d device(s):\n", device_count); for (i = 0; i < device_count; i++) { memset(&vendor[0], 0, 256 * sizeof(char)); memset(&product[0], 0, 256 * sizeof(char)); memset(&serial[0], 0, 256 * sizeof(char)); rc = rtlsdr_get_device_usb_strings(i, vendor, product, serial); if ( rc == 0 ) fprintf(stderr, " %d: %s, %s, SN: %s\n", i, vendor, product, serial); else fprintf(stderr, " %d: %s\n", i, "Failed to query data"); } fprintf(stderr, "\n"); /* does string look like raw id number */ device = (int)strtol(s, &s2, 0); if (s2[0] == '\0' && device >= 0 && device < device_count) { fprintf(stderr, "Using device %d: %s\n", device, rtlsdr_get_device_name((uint32_t)device)); return device; } /* does string exact match a serial */ for (i = 0; i < device_count; i++) { memset(&vendor[0], 0, 256 * sizeof(char)); memset(&product[0], 0, 256 * sizeof(char)); memset(&serial[0], 0, 256 * sizeof(char)); rc = rtlsdr_get_device_usb_strings(i, vendor, product, serial); if (rc) continue; if (strcmp(s, serial) != 0) { continue;} device = i; fprintf(stderr, "Using device %d: %s\n", device, rtlsdr_get_device_name((uint32_t)device)); return device; } /* does string prefix match a serial */ for (i = 0; i < device_count; i++) { memset(&vendor[0], 0, 256 * sizeof(char)); memset(&product[0], 0, 256 * sizeof(char)); memset(&serial[0], 0, 256 * sizeof(char)); rc = rtlsdr_get_device_usb_strings(i, vendor, product, serial); if (rc) continue; if (strncmp(s, serial, strlen(s)) != 0) { continue;} device = i; fprintf(stderr, "Using device %d: %s\n", device, rtlsdr_get_device_name((uint32_t)device)); return device; } /* does string suffix match a serial */ for (i = 0; i < device_count; i++) { memset(&vendor[0], 0, 256 * sizeof(char)); memset(&product[0], 0, 256 * sizeof(char)); memset(&serial[0], 0, 256 * sizeof(char)); rc = rtlsdr_get_device_usb_strings(i, vendor, product, serial); if (rc) continue; offset = strlen(serial) - strlen(s); if (offset < 0) { continue;} if (strncmp(s, serial+offset, strlen(s)) != 0) { continue;} device = i; fprintf(stderr, "Using device %d: %s\n", device, rtlsdr_get_device_name((uint32_t)device)); return device; } fprintf(stderr, "No matching devices found.\n"); return -1; } // vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab ================================================ FILE: src/convenience/rtl_convenience.h ================================================ /* * Copyright (C) 2014 by Kyle Keen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __RTL_CONVENIENCE_H #define __RTL_CONVENIENCE_H #include #include #ifdef __cplusplus extern "C" { #endif /* a collection of user friendly tools */ /*! * Find nearest supported gain * * \param dev the device handle given by rtlsdr_open() * \param target_gain in tenths of a dB * \return 0 on success */ int nearest_gain(rtlsdr_dev_t *dev, int target_gain); /*! * Set device frequency and report status on stderr * * \param dev the device handle given by rtlsdr_open() * \param frequency in Hz * \return 0 on success */ int verbose_set_frequency(rtlsdr_dev_t *dev, uint64_t frequency); /*! * Set device sample rate and report status on stderr * * \param dev the device handle given by rtlsdr_open() * \param samp_rate in samples/second * \return 0 on success */ int verbose_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate); /*! * Set device bandwidth and report status on stderr * * \param dev the device handle given by rtlsdr_open() * \param frequency in Hz * \return 0 on success */ int verbose_set_bandwidth(rtlsdr_dev_t *dev, uint32_t bandwidth); /*! * Enable or disable the direct sampling mode and report status on stderr * * \param dev the device handle given by rtlsdr_open() * \param on 0 means disabled, 1 I-ADC input enabled, 2 Q-ADC input enabled * \return 0 on success */ int verbose_direct_sampling(rtlsdr_dev_t *dev, int on); /*! * Enable offset tuning and report status on stderr * * \param dev the device handle given by rtlsdr_open() * \return 0 on success */ int verbose_offset_tuning(rtlsdr_dev_t *dev); /*! * Enable auto gain and report status on stderr * * \param dev the device handle given by rtlsdr_open() * \return 0 on success */ int verbose_auto_gain(rtlsdr_dev_t *dev); /*! * Set tuner gain and report status on stderr * * \param dev the device handle given by rtlsdr_open() * \param gain in tenths of a dB * \return 0 on success */ int verbose_gain_set(rtlsdr_dev_t *dev, int gain); /*! * Set the frequency correction value for the device and report status on stderr. * * \param dev the device handle given by rtlsdr_open() * \param ppm_error correction value in parts per million (ppm) * \return 0 on success */ int verbose_ppm_set(rtlsdr_dev_t *dev, int ppm_error); /*! * Reset buffer * * \param dev the device handle given by rtlsdr_open() * \return 0 on success */ int verbose_reset_buffer(rtlsdr_dev_t *dev); /*! * Find the closest matching device. * * \param s a string to be parsed * \return dev_index int, -1 on error */ int verbose_device_search(char *s); #ifdef __cplusplus } #endif #endif /*__RTL_CONVENIENCE_H*/ ================================================ FILE: src/convenience/wavehdr.h ================================================ /* * Copyright (C) 2019 by Hayati Ayguen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __WAVEHDR_H #define __WAVEHDR_H #include #include #include #pragma pack(push) #pragma pack(1) typedef struct { char ID[4]; uint32_t size; } chunk_hdr; typedef struct { uint16_t wYear; /* 1601 through 30827 */ uint16_t wMonth; /* 1..12 */ uint16_t wDayOfWeek; /* 0 .. 6: 0 == Sunday, .., 6 == Saturday */ uint16_t wDay; /* 1 .. 31 */ uint16_t wHour; /* 0 .. 23 */ uint16_t wMinute; /* 0 .. 59 */ uint16_t wSecond; /* 0 .. 59 */ uint16_t wMilliseconds; /* 0 .. 999 */ } Wind_SystemTime; typedef struct { /* RIFF header */ chunk_hdr hdr; /* ID == "RIFF" string, size == full filesize - 8 bytes (maybe with some byte missing...) */ char waveID[4]; /* "WAVE" string */ } riff_chunk; typedef struct { /* FMT header */ chunk_hdr hdr; /* ID == "fmt " */ int16_t wFormatTag; int16_t nChannels; int32_t nSamplesPerSec; int32_t nAvgBytesPerSec; int16_t nBlockAlign; int16_t nBitsPerSample; } fmt_chunk; typedef struct { /* auxi header - used by SpectraVue / rfspace / HDSDR / ELAD FDM .. */ chunk_hdr hdr; /* ="auxi" (chunk rfspace) */ Wind_SystemTime StartTime; Wind_SystemTime StopTime; uint32_t centerFreq; /* receiver center frequency */ uint32_t ADsamplerate; /* A/D sample frequency before downsampling */ uint32_t IFFrequency; /* IF freq if an external down converter is used */ uint32_t Bandwidth; /* displayable BW if you want to limit the display to less than Nyquist band */ int32_t IQOffset; /* DC offset of the I and Q channels in 1/1000's of a count */ int32_t Unused2; int32_t Unused3; int32_t Unused4; int32_t Unused5; } auxi_chunk; typedef struct { /* DATA header */ chunk_hdr hdr; /* ="data" */ } data_chunk; typedef struct { riff_chunk r; fmt_chunk f; auxi_chunk a; data_chunk d; } waveFileHeader; #pragma pack(pop) #endif /* __WAVEHDR_H */ // vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab ================================================ FILE: src/convenience/waveread.c ================================================ /* * Copyright (C) 2019 by Hayati Ayguen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "waveread.h" #include #include #include #include #include #ifndef _WIN32 #include #include #else #include #include #include #include #define _USE_MATH_DEFINES #endif #include #include "wavehdr.h" #define PRINT_DEBUG_OUTPUTS 0 static waveFileHeader waveHdr; static uint32_t waveDataSize = 0; static void waveGetTimeInt(const Wind_SystemTime *p, time_t *tim, double *fraction) { struct tm t; #ifdef _WIN32 struct tm gtm, ltm; time_t nt, gt, lt; #endif /* fprintf(stderr, "waveGetTimeInt( Wind_SystemTime = %04d-%02d-%02d T %02d:%02d:%02d %f )\n" , (int)(p->wYear), (int)(p->wMonth), (int)(p->wDay) , (int)(p->wHour), (int)(p->wMinute), (int)(p->wSecond) , (float)(p->wMilliseconds / 1000.0) ); */ t.tm_year = p->wYear - 1900; /* 1601 through 30827 */ t.tm_mon = p->wMonth -1; /* 1..12 */ t.tm_wday = p->wDayOfWeek; /* 0 .. 6: 0 == Sunday, .., 6 == Saturday */ t.tm_mday = p->wDay; /* 1 .. 31 */ t.tm_hour = p->wHour; /* 0 .. 23 */ t.tm_min = p->wMinute; /* 0 .. 59 */ t.tm_sec = p->wSecond; /* 0 .. 59 */ t.tm_wday = 0; t.tm_yday = 0; t.tm_isdst = 0; *fraction = p->wMilliseconds / 1000.0; #ifndef _WIN32 *tim = timegm( &t ); /* fprintf(stderr, " using timegm()\n"); */ #else #ifdef _MSC_VER *tim = _mkgmtime(&t); /* fprintf(stderr, " using _mkgmtime()\n"); */ #else /* workaround missing mkgmtime on mingw */ nt = mktime(&t); gtm = *gmtime(&nt); ltm = *localtime(&nt); gt = mktime(>m); lt = mktime(<m); assert( nt == gt ); nt += ( lt - gt ); *tim = nt; /* fprintf(stderr, " using mktime(), gmtime() and localtime() difference\n"); */ #endif #endif } void waveGetStartTime(time_t *tim, double *fraction) { waveGetTimeInt(&waveHdr.a.StartTime, tim, fraction); } void waveGetStopTime(time_t *tim, double *fraction) { waveGetTimeInt(&waveHdr.a.StopTime, tim, fraction); } int waveReadHeader(FILE * f, uint32_t *srate, uint32_t *freq, int *bitsPerSample, int *numChannels , uint32_t *nFrames, int16_t *formatTag, int verbosity) { uint8_t buf[32768]; size_t rd, smpSize; int readAuxiHdr = 0; int readDataHdr = 0; int rv = 0; #if PRINT_DEBUG_OUTPUTS fprintf(stderr, "waveReadHeader(.., verbosity = %d)\n", verbosity); #endif rd = fread( &waveHdr.r, sizeof(riff_chunk), 1, f ); if ( rd != 1 ) return 10; if ( memcmp(waveHdr.r.hdr.ID, "RIFF", 4) ) return 11; if ( memcmp(waveHdr.r.waveID, "WAVE", 4 ) ) return 12; rd = fread( &waveHdr.f, sizeof(fmt_chunk), 1, f ); #if PRINT_DEBUG_OUTPUTS fprintf(stderr, "read %d chunk '%c%c%c%c' size %u = 0x%X\n", (int)rd , waveHdr.f.hdr.ID[0], waveHdr.f.hdr.ID[1], waveHdr.f.hdr.ID[2], waveHdr.f.hdr.ID[3] , (unsigned)waveHdr.f.hdr.size, (unsigned)waveHdr.f.hdr.size ); #endif if ( rd != 1 ) return 20; if ( memcmp(waveHdr.f.hdr.ID, "fmt ", 4 ) ) return 21; if ( waveHdr.f.hdr.size != 16 ) return 22; *bitsPerSample = waveHdr.f.nBitsPerSample; *numChannels = waveHdr.f.nChannels; *srate = waveHdr.f.nSamplesPerSec; *formatTag = waveHdr.f.wFormatTag; rd = fread( &waveHdr.a.hdr, sizeof(chunk_hdr), 1, f ); while ( !readDataHdr ) { if ( rd != 1 ) return 30; #if PRINT_DEBUG_OUTPUTS fprintf(stderr, "read %d chunk '%c%c%c%c' size %u = 0x%X\n", (int)rd , waveHdr.a.hdr.ID[0], waveHdr.a.hdr.ID[1], waveHdr.a.hdr.ID[2], waveHdr.a.hdr.ID[3] , (unsigned)waveHdr.a.hdr.size, (unsigned)waveHdr.a.hdr.size ); #endif if ( !memcmp(waveHdr.a.hdr.ID, "auxi", 4 ) ) { if ( verbosity ) fprintf(stderr, "read chunk 'auxi' .. continue searching for 'data'-chunk ..\n"); if ( waveHdr.a.hdr.size > sizeof(buf) ) return 32; if ( waveHdr.a.hdr.size < (sizeof(auxi_chunk) - sizeof(chunk_hdr)) ) return 33; rd = fread( buf, waveHdr.a.hdr.size, 1, f ); if ( rd != 1 ) return 34; memcpy( &waveHdr.a.StartTime, buf, sizeof(auxi_chunk) - sizeof(chunk_hdr)); *freq = waveHdr.a.centerFreq; readAuxiHdr = 1; } else if ( !memcmp(waveHdr.a.hdr.ID, "data", 4 ) ) { if ( verbosity ) fprintf(stderr, "read chunk 'data' .. going to process - %s 'auxi'-chunk ..\n", readAuxiHdr ? "with" : "without" ); waveHdr.d.hdr = waveHdr.a.hdr; if (!readAuxiHdr) { memset(&waveHdr.a, 0, sizeof(auxi_chunk)); waveHdr.a.StartTime.wYear = 1970; waveHdr.a.StartTime.wMonth = 1; waveHdr.a.StartTime.wDay = 1; *freq = 100UL * 1000 * 1000; /* set frequency to 100 MHz */ } readDataHdr = 1; break; } else { if ( verbosity ) fprintf(stderr, "read chunk '%c%c%c%c' while expecting 'auxi' or 'data'. going to next chunk ..\n" , waveHdr.a.hdr.ID[0], waveHdr.a.hdr.ID[1], waveHdr.a.hdr.ID[2], waveHdr.a.hdr.ID[3] ); if ( waveHdr.a.hdr.size > sizeof(buf) ) return 32; rd = fread( buf, waveHdr.a.hdr.size, 1, f ); if ( rd != 1 ) return 34; } /* read next chunk's header */ rd = fread( &waveHdr.a.hdr, sizeof(chunk_hdr), 1, f ); } if ( !readDataHdr ) { rd = fread( &waveHdr.d.hdr, sizeof(chunk_hdr), 1, f ); #if PRINT_DEBUG_OUTPUTS fprintf(stderr, "read %d chunk '%c%c%c%c' size %u = 0x%X\n", (int)rd , waveHdr.d.hdr.ID[0], waveHdr.d.hdr.ID[1], waveHdr.d.hdr.ID[2], waveHdr.d.hdr.ID[3] , (unsigned)waveHdr.d.hdr.size, (unsigned)waveHdr.d.hdr.size ); #endif } else rd = 1; if ( rd != 1 ) return 40; if ( memcmp(waveHdr.d.hdr.ID, "data", 4 ) ) return 41; smpSize = (*bitsPerSample + 7) / 8; /* round up to next byte */ smpSize *= *numChannels; *nFrames = waveHdr.d.hdr.size / smpSize; if ( verbosity >= 2 ) { fprintf(stderr, "riffSize = %lu\n", (unsigned long)waveHdr.r.hdr.size ); fprintf(stderr, "dataSize = %lu\n", (unsigned long)waveHdr.d.hdr.size); fprintf(stderr, "nBlockAlign = %d\n", (int)waveHdr.f.nBlockAlign); fprintf(stderr, "smpSize = %d\n", (int)smpSize); fprintf(stderr, "*nFrames = %lu\n", (unsigned long)(*nFrames) ); } return rv; } int waveReadSamples(FILE* f, void * vpData, size_t numSamples, int needCleanData, size_t *numRead) { size_t nw; switch (waveHdr.f.nBitsPerSample) { case 0: default: return 1; case 8: /* no endian conversion needed for single bytes */ nw = fread(vpData, sizeof(uint8_t), numSamples, f); *numRead = nw; return (nw == numSamples) ? 0 : 1; case 16: /* TODO: endian conversion needed */ nw = fread(vpData, sizeof(int16_t), numSamples, f); if ( needCleanData ) { /* TODO: convert back endianness */ } *numRead = nw; return (nw == numSamples) ? 0 : 1; } } int waveReadFrames(FILE* f, void * vpData, size_t numFrames, int needCleanData, size_t *numRead) { size_t nw; switch (waveHdr.f.nBitsPerSample) { case 0: default: return 1; case 8: /* no endian conversion needed for single bytes */ nw = fread(vpData, waveHdr.f.nChannels * sizeof(uint8_t), numFrames, f); *numRead = nw; return (nw == numFrames) ? 0 : 1; case 16: /* TODO: endian conversion needed */ nw = fread(vpData, waveHdr.f.nChannels * sizeof(int16_t), numFrames, f); if ( needCleanData ) { /* TODO: convert back endianness */ } *numRead = nw; return (nw == numFrames) ? 0 : 1; } } // vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab ================================================ FILE: src/convenience/waveread.h ================================================ /* * Copyright (C) 2019 by Hayati Ayguen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __WAVEREAD_H #define __WAVEREAD_H #include #include #include #ifdef __cplusplus extern "C" { #endif int waveReadHeader(FILE * f, uint32_t *samplerate, uint32_t *freq, int *bitsPerSample, int *numChannels , uint32_t *nFrames, int16_t *formatTag, int verbosity); int waveReadFrames(FILE* f, void * vpData, size_t numFrames, int needCleanData, size_t *numRead); int waveReadSamples(FILE* f, void * vpData, size_t numSamples, int needCleanData, size_t *numRead); /* returns 0, when no errors occured */ void waveGetStartTime(time_t *tim, double *fraction); void waveGetStopTime(time_t *tim, double *fraction); #ifdef __cplusplus } #endif #endif /*__WAVEREAD_H*/ ================================================ FILE: src/convenience/wavewrite.c ================================================ /* * Copyright (C) 2019 by Hayati Ayguen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "wavewrite.h" #include #include #include #include #include #ifndef _WIN32 #include #include #else #include #include #include #include #define _USE_MATH_DEFINES #endif #include #include "wavehdr.h" static waveFileHeader waveHdr; static uint32_t waveDataSize = 0; int waveHdrStarted = 0; #ifdef _WIN32 int gettimeofday(struct timeval *tv, void* ignored) { FILETIME ft; unsigned __int64 tmp = 0; if (NULL != tv) { GetSystemTimeAsFileTime(&ft); tmp |= ft.dwHighDateTime; tmp <<= 32; tmp |= ft.dwLowDateTime; tmp /= 10; #ifdef _MSC_VER tmp -= 11644473600000000Ui64; #else tmp -= 11644473600000000ULL; #endif tv->tv_sec = (long)(tmp / 1000000UL); tv->tv_usec = (long)(tmp % 1000000UL); } return 0; } #endif static void waveSetCurrTime(Wind_SystemTime *p) { struct timeval tv; struct tm t; gettimeofday(&tv, NULL); p->wMilliseconds = tv.tv_usec / 1000; #ifdef _WIN32 t = *gmtime(&tv.tv_sec); #else gmtime_r(&tv.tv_sec, &t); #endif p->wYear = t.tm_year + 1900; /* 1601 through 30827 */ p->wMonth = t.tm_mon + 1; /* 1..12 */ p->wDayOfWeek = t.tm_wday; /* 0 .. 6: 0 == Sunday, .., 6 == Saturday */ p->wDay = t.tm_mday; /* 1 .. 31 */ p->wHour = t.tm_hour; /* 0 .. 23 */ p->wMinute = t.tm_min; /* 0 .. 59 */ p->wSecond = t.tm_sec; /* 0 .. 59 */ } static void waveSetStartTimeInt(time_t tim, double fraction, Wind_SystemTime *p) { struct tm t = *gmtime( &tim ); p->wYear = t.tm_year + 1900; /* 1601 through 30827 */ p->wMonth = t.tm_mon + 1; /* 1..12 */ p->wDayOfWeek = t.tm_wday; /* 0 .. 6: 0 == Sunday, .., 6 == Saturday */ p->wDay = t.tm_mday; /* 1 .. 31 */ p->wHour = t.tm_hour; /* 0 .. 23 */ p->wMinute = t.tm_min; /* 0 .. 59 */ p->wSecond = t.tm_sec; /* 0 .. 59 */ p->wMilliseconds = (int)( fraction * 1000.0 ); if (p->wMilliseconds >= 1000) p->wMilliseconds = 999; } void waveSetStartTime(time_t tim, double fraction) { waveSetStartTimeInt(tim, fraction, &waveHdr.a.StartTime ); waveHdr.a.StopTime = waveHdr.a.StartTime; /* to fix */ } void wavePrepareHeader(unsigned samplerate, unsigned freq, int bitsPerSample, int numChannels) { int bytesPerSample = bitsPerSample / 8; int bytesPerFrame = bytesPerSample * numChannels; memcpy( waveHdr.r.hdr.ID, "RIFF", 4 ); waveHdr.r.hdr.size = sizeof(waveFileHeader) - 8; /* to fix */ memcpy( waveHdr.r.waveID, "WAVE", 4 ); memcpy( waveHdr.f.hdr.ID, "fmt ", 4 ); waveHdr.f.hdr.size = 16; waveHdr.f.wFormatTag = 1; /* PCM */ waveHdr.f.nChannels = numChannels; /* I and Q channels */ waveHdr.f.nSamplesPerSec = samplerate; waveHdr.f.nAvgBytesPerSec = samplerate * bytesPerFrame; waveHdr.f.nBlockAlign = waveHdr.f.nChannels; waveHdr.f.nBitsPerSample = bitsPerSample; memcpy( waveHdr.a.hdr.ID, "auxi", 4 ); waveHdr.a.hdr.size = 2 * sizeof(Wind_SystemTime) + 9 * sizeof(int32_t); /* = 2 * 16 + 9 * 4 = 68 */ waveSetCurrTime( &waveHdr.a.StartTime ); waveHdr.a.StopTime = waveHdr.a.StartTime; /* to fix */ waveHdr.a.centerFreq = freq; waveHdr.a.ADsamplerate = samplerate; waveHdr.a.IFFrequency = 0; waveHdr.a.Bandwidth = 0; waveHdr.a.IQOffset = 0; waveHdr.a.Unused2 = 0; waveHdr.a.Unused3 = 0; waveHdr.a.Unused4 = 0; waveHdr.a.Unused5 = 0; memcpy( waveHdr.d.hdr.ID, "data", 4 ); waveHdr.d.hdr.size = 0; /* to fix later */ waveDataSize = 0; } void waveWriteHeader(unsigned samplerate, unsigned freq, int bitsPerSample, int numChannels, FILE * f) { if (f != stdout) { assert( !waveHdrStarted ); wavePrepareHeader(samplerate, freq, bitsPerSample, numChannels); fwrite(&waveHdr, sizeof(waveFileHeader), 1, f); waveHdrStarted = 1; } } int waveWriteSamples(FILE* f, void * vpData, size_t numSamples, int needCleanData) { size_t nw; switch (waveHdr.f.nBitsPerSample) { case 0: default: return 1; case 8: /* no endian conversion needed for single bytes */ nw = fwrite(vpData, sizeof(uint8_t), numSamples, f); waveDataSize += sizeof(uint8_t) * numSamples; return (nw == numSamples) ? 0 : 1; case 16: /* TODO: endian conversion needed */ nw = fwrite(vpData, sizeof(int16_t), numSamples, f); waveDataSize += sizeof(int16_t) * numSamples; if ( needCleanData ) { /* TODO: convert back endianness */ } return (nw == numSamples) ? 0 : 1; } } int waveWriteFrames(FILE* f, void * vpData, size_t numFrames, int needCleanData) { size_t nw; switch (waveHdr.f.nBitsPerSample) { case 0: default: return 1; case 8: /* no endian conversion needed for single bytes */ nw = fwrite(vpData, waveHdr.f.nChannels * sizeof(uint8_t), numFrames, f); waveDataSize += waveHdr.f.nChannels * sizeof(uint8_t) * numFrames; return (nw == numFrames) ? 0 : 1; case 16: /* TODO: endian conversion needed */ nw = fwrite(vpData, waveHdr.f.nChannels * sizeof(int16_t), numFrames, f); waveDataSize += waveHdr.f.nChannels * sizeof(int16_t) * numFrames; if ( needCleanData ) { /* TODO: convert back endianness */ } return (nw == numFrames) ? 0 : 1; } } int waveFinalizeHeader(FILE * f) { if (f != stdout) { assert( waveHdrStarted ); waveSetCurrTime( &waveHdr.a.StopTime ); waveHdr.d.hdr.size = waveDataSize; waveHdr.r.hdr.size += waveDataSize; /* fprintf(stderr, "waveFinalizeHeader(): datasize = %d\n", waveHdr.dataSize); */ waveHdrStarted = 0; if ( fseek(f, 0, SEEK_SET) ) return 1; if ( 1 != fwrite(&waveHdr, sizeof(waveFileHeader), 1, f) ) return 1; /* fprintf(stderr, "waveFinalizeHeader(): success writing header\n"); */ return 0; } return 1; } // vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab ================================================ FILE: src/convenience/wavewrite.h ================================================ /* * Copyright (C) 2019 by Hayati Ayguen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __WAVEWRITE_H #define __WAVEWRITE_H #include #include #include #ifdef __cplusplus extern "C" { #endif extern int waveHdrStarted; /*! * helper functions to write and finalize wave headers * with compatibility to some SDR programs - showing frequency: * raw sample data still have to be written by caller to FILE*. * call waveWriteHeader() before writing anything to to file * and call waveFinalizeHeader() afterwards, * stdout/stderr can't be used, because seek to begin isn't possible. * */ void waveWriteHeader(unsigned samplerate, unsigned freq, int bitsPerSample, int numChannels, FILE * f); /* waveWriteFrames() writes (numFrames * numChannels) samples * waveWriteSamples() * both return 0, when no errors occured */ int waveWriteFrames(FILE* f, void * vpData, size_t numFrames, int needCleanData); int waveWriteSamples(FILE* f, void * vpData, size_t numSamples, int needCleanData); /* returns 0, when no errors occured */ void waveSetStartTime(time_t t, double fraction); int waveFinalizeHeader(FILE * f); /* returns 0, when no errors occured */ #ifdef __cplusplus } #endif #endif /*__WAVEWRITE_H*/ ================================================ FILE: src/getopt/getopt.c ================================================ /* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to drepper@gnu.org before changing it! Copyright (C) 1987,88,89,90,91,92,93,94,95,96,98,99,2000,2001 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO # define _NO_PROTO #endif #ifdef HAVE_CONFIG_H # include #endif #if !defined __STDC__ || !__STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ # ifndef const # define const # endif #endif #include /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #define GETOPT_INTERFACE_VERSION 2 #if !defined _LIBC && defined __GLIBC__ && __GLIBC__ >= 2 # include # if _GNU_GETOPT_INTERFACE_VERSION == GETOPT_INTERFACE_VERSION # define ELIDE_CODE # endif #endif #ifndef ELIDE_CODE /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ # include # include #endif /* GNU C library. */ #ifdef VMS # include # if HAVE_STRING_H - 0 # include # endif #endif #ifndef _ /* This is for other GNU distributions with internationalized messages. */ # if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC # include # ifndef _ # define _(msgid) gettext (msgid) # endif # else # define _(msgid) (msgid) # endif #endif /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include "getopt.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* 1003.2 says this must be 1 before any call. */ int optind = 1; /* Formerly, initialization of getopt depended on optind==0, which causes problems with re-calling getopt as programs generally don't know that. */ int __getopt_initialized; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int optopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return -1 with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ # include # define my_index strchr #else # if 1 //HAVE_STRING_H # include # else # include # endif /* Avoid depending on library functions or files whose names are inconsistent. */ #ifndef getenv #ifdef _MSC_VER // DDK will complain if you don't use the stdlib defined getenv #include #else extern char *getenv (); #endif #endif static char * my_index (str, chr) const char *str; int chr; { while (*str) { if (*str == chr) return (char *) str; str++; } return 0; } /* If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare it. */ #ifdef __GNUC__ /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that was here before. */ # if (!defined __STDC__ || !__STDC__) && !defined strlen /* gcc with -traditional declares the built-in strlen to return int, and has done so at least since version 2.4.5. -- rms. */ extern int strlen (const char *); # endif /* not __STDC__ */ #endif /* __GNUC__ */ #endif /* not __GNU_LIBRARY__ */ /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; #ifdef _LIBC /* Stored original parameters. XXX This is no good solution. We should rather copy the args so that we can compare them later. But we must not use malloc(3). */ extern int __libc_argc; extern char **__libc_argv; /* Bash 2.0 gives us an environment variable containing flags indicating ARGV elements that should not be considered arguments. */ # ifdef USE_NONOPTION_FLAGS /* Defined in getopt_init.c */ extern char *__getopt_nonoption_flags; static int nonoption_flags_max_len; static int nonoption_flags_len; # endif # ifdef USE_NONOPTION_FLAGS # define SWAP_FLAGS(ch1, ch2) \ if (nonoption_flags_len > 0) \ { \ char __tmp = __getopt_nonoption_flags[ch1]; \ __getopt_nonoption_flags[ch1] = __getopt_nonoption_flags[ch2]; \ __getopt_nonoption_flags[ch2] = __tmp; \ } # else # define SWAP_FLAGS(ch1, ch2) # endif #else /* !_LIBC */ # define SWAP_FLAGS(ch1, ch2) #endif /* _LIBC */ /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ #if defined __STDC__ && __STDC__ static void exchange (char **); #endif static void exchange (argv) char **argv; { int bottom = first_nonopt; int middle = last_nonopt; int top = optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ #if defined _LIBC && defined USE_NONOPTION_FLAGS /* First make sure the handling of the `__getopt_nonoption_flags' string can work normally. Our top argument must be in the range of the string. */ if (nonoption_flags_len > 0 && top >= nonoption_flags_max_len) { /* We must extend the array. The user plays games with us and presents new arguments. */ char *new_str = malloc (top + 1); if (new_str == NULL) nonoption_flags_len = nonoption_flags_max_len = 0; else { memset (__mempcpy (new_str, __getopt_nonoption_flags, nonoption_flags_max_len), '\0', top + 1 - nonoption_flags_max_len); nonoption_flags_max_len = top + 1; __getopt_nonoption_flags = new_str; } } #endif while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; SWAP_FLAGS (bottom + i, top - (middle - bottom) + i); } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; SWAP_FLAGS (bottom + i, middle + i); } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* Initialize the internal data when the first call is made. */ #if defined __STDC__ && __STDC__ static const char *_getopt_initialize (int, char *const *, const char *); #endif static const char * _getopt_initialize (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = optind; nextchar = NULL; posixly_correct = getenv ("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; #if defined _LIBC && defined USE_NONOPTION_FLAGS if (posixly_correct == NULL && argc == __libc_argc && argv == __libc_argv) { if (nonoption_flags_max_len == 0) { if (__getopt_nonoption_flags == NULL || __getopt_nonoption_flags[0] == '\0') nonoption_flags_max_len = -1; else { const char *orig_str = __getopt_nonoption_flags; int len = nonoption_flags_max_len = strlen (orig_str); if (nonoption_flags_max_len < argc) nonoption_flags_max_len = argc; __getopt_nonoption_flags = (char *) malloc (nonoption_flags_max_len); if (__getopt_nonoption_flags == NULL) nonoption_flags_max_len = -1; else memset (__mempcpy (__getopt_nonoption_flags, orig_str, len), '\0', nonoption_flags_max_len - len); } } nonoption_flags_len = nonoption_flags_max_len; } else nonoption_flags_len = 0; #endif return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns -1. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal (argc, argv, optstring, longopts, longind, long_only) int argc; char *const *argv; const char *optstring; const struct option *longopts; int *longind; int long_only; { int print_errors = opterr; if (optstring[0] == ':') print_errors = 0; if (argc < 1) return -1; optarg = NULL; if (optind == 0 || !__getopt_initialized) { if (optind == 0) optind = 1; /* Don't scan ARGV[0], the program name. */ optstring = _getopt_initialize (argc, argv, optstring); __getopt_initialized = 1; } /* Test whether ARGV[optind] points to a non-option argument. Either it does not have option syntax, or there is an environment flag from the shell indicating it is not an option. The later information is only used when the used in the GNU libc. */ #if defined _LIBC && defined USE_NONOPTION_FLAGS # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0' \ || (optind < nonoption_flags_len \ && __getopt_nonoption_flags[optind] == '1')) #else # define NONOPTION_P (argv[optind][0] != '-' || argv[optind][1] == '\0') #endif if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ /* Give FIRST_NONOPT & LAST_NONOPT rational values if OPTIND has been moved back by the user (who may also have changed the arguments). */ if (last_nonopt > optind) last_nonopt = optind; if (first_nonopt > optind) first_nonopt = optind; if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (last_nonopt != optind) first_nonopt = optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && NONOPTION_P) optind++; last_nonopt = optind; } /* The special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp (argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return -1; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if (NONOPTION_P) { if (ordering == REQUIRE_ORDER) return -1; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = -1; int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == (unsigned int) strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else if (long_only || pfound->has_arg != p->has_arg || pfound->flag != p->flag || pfound->val != p->val) /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (print_errors) fprintf (stderr, _("%s: option `%s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; optopt = 0; return '?'; } if (pfound != NULL) { option_index = indfound; optind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (print_errors) { if (argv[optind - 1][1] == '-') /* --option */ fprintf (stderr, _("%s: option `--%s' doesn't allow an argument\n"), argv[0], pfound->name); else /* +option or -option */ fprintf (stderr, _("%s: option `%c%s' doesn't allow an argument\n"), argv[0], argv[optind - 1][0], pfound->name); } nextchar += strlen (nextchar); optopt = pfound->val; return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (print_errors) fprintf (stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen (nextchar); optopt = pfound->val; return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' || my_index (optstring, *nextchar) == NULL) { if (print_errors) { if (argv[optind][1] == '-') /* --option */ fprintf (stderr, _("%s: unrecognized option `--%s'\n"), argv[0], nextchar); else /* +option or -option */ fprintf (stderr, _("%s: unrecognized option `%c%s'\n"), argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; optopt = 0; return '?'; } } /* Look at and handle the next short option-character. */ { char c = *nextchar++; char *temp = my_index (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (print_errors) { if (posixly_correct) /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: illegal option -- %c\n"), argv[0], c); else fprintf (stderr, _("%s: invalid option -- %c\n"), argv[0], c); } optopt = c; return '?'; } /* Convenience. Treat POSIX -W foo same as long option --foo */ if (temp[0] == 'W' && temp[1] == ';') { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (print_errors) { /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; return c; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; /* optarg is now the argument, see if it's in the table of longopts. */ for (nextchar = nameend = optarg; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if ((unsigned int) (nameend - nextchar) == strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (print_errors) fprintf (stderr, _("%s: option `-W %s' is ambiguous\n"), argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; return '?'; } if (pfound != NULL) { option_index = indfound; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (print_errors) fprintf (stderr, _("\ %s: option `-W %s' doesn't allow an argument\n"), argv[0], pfound->name); nextchar += strlen (nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (print_errors) fprintf (stderr, _("%s: option `%s' requires an argument\n"), argv[0], argv[optind - 1]); nextchar += strlen (nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } nextchar = NULL; return 'W'; /* Let the application handle it. */ } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (print_errors) { /* 1003.2 specifies the format of this message. */ fprintf (stderr, _("%s: option requires an argument -- %c\n"), argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt (argc, argv, optstring) int argc; char *const *argv; const char *optstring; { return _getopt_internal (argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); } #endif /* Not ELIDE_CODE. */ #ifdef TEST /* Compile with -DTEST to make an executable for use in testing the above definition of `getopt'. */ int main (argc, argv) int argc; char **argv; { int c; int digit_optind = 0; while (1) { int this_option_optind = optind ? optind : 1; c = getopt (argc, argv, "abc:d:0123456789"); if (c == -1) break; switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (digit_optind != 0 && digit_optind != this_option_optind) printf ("digits occur in two different argv-elements.\n"); digit_optind = this_option_optind; printf ("option %c\n", c); break; case 'a': printf ("option a\n"); break; case 'b': printf ("option b\n"); break; case 'c': printf ("option c with value `%s'\n", optarg); break; case '?': break; default: printf ("?? getopt returned character code 0%o ??\n", c); } } if (optind < argc) { printf ("non-option ARGV-elements: "); while (optind < argc) printf ("%s ", argv[optind++]); printf ("\n"); } exit (0); } #endif /* TEST */ ================================================ FILE: src/getopt/getopt.h ================================================ /* Declarations for getopt. Copyright (C) 1989-1994, 1996-1999, 2001 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifndef _GETOPT_H #ifndef __need_getopt # define _GETOPT_H 1 #endif /* If __GNU_LIBRARY__ is not already defined, either we are being used standalone, or this is the first header included in the source file. If we are being used with glibc, we need to include , but that does not exist if we are standalone. So: if __GNU_LIBRARY__ is not defined, include , which will pull in for us if it's from glibc. (Why ctype.h? It's guaranteed to exist and it doesn't flood the namespace with stuff the way some other headers do.) */ #if !defined __GNU_LIBRARY__ # include #endif #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns -1, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; #ifndef __need_getopt /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { # if (defined __STDC__ && __STDC__) || defined __cplusplus const char *name; # else char *name; # endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ # define no_argument 0 # define required_argument 1 # define optional_argument 2 #endif /* need getopt */ /* Get definitions and prototypes for functions to process the arguments in ARGV (ARGC of them, minus the program name) for options given in OPTS. Return the option character from OPTS just read. Return -1 when there are no more options. For unrecognized options, or options missing arguments, `optopt' is set to the option letter, and '?' is returned. The OPTS string is a list of characters which are recognized option letters, optionally followed by colons, specifying that that letter takes an argument, to be placed in `optarg'. If a letter in OPTS is followed by two colons, its argument is optional. This behavior is specific to the GNU `getopt'. The argument `--' causes premature termination of argument scanning, explicitly telling `getopt' that there are no more options. If OPTS begins with `--', then non-option arguments are treated as arguments to the option '\0'. This behavior is specific to the GNU `getopt'. */ #if (defined __STDC__ && __STDC__) || defined __cplusplus # ifdef __GNU_LIBRARY__ /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int getopt (int __argc, char *const *__argv, const char *__shortopts); # else /* not __GNU_LIBRARY__ */ extern int getopt (); # endif /* __GNU_LIBRARY__ */ # ifndef __need_getopt extern int getopt_long (int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind); extern int getopt_long_only (int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal (int __argc, char *const *__argv, const char *__shortopts, const struct option *__longopts, int *__longind, int __long_only); # endif #else /* not __STDC__ */ extern int getopt (); # ifndef __need_getopt extern int getopt_long (); extern int getopt_long_only (); extern int _getopt_internal (); # endif #endif /* __STDC__ */ #ifdef __cplusplus } #endif /* Make sure we later can get all the definitions and declarations. */ #undef __need_getopt #endif /* getopt.h */ ================================================ FILE: src/librtlsdr.c ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2012-2014 by Steve Markgraf * Copyright (C) 2012 by Dimitri Stolnikov * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef _WIN32 #include #include #include #include #include #define LAST_SOCK_ERROR() errno #define closesocket close #define SOCKADDR struct sockaddr #define SOCKET int #define SOCKET_ERROR -1 #define INVALID_SOCKET -1 #else #include #define LAST_SOCK_ERROR() WSAGetLastError() #define usleep(x) Sleep(x/1000) typedef int socklen_t; #pragma comment(lib, "ws2_32.lib") #endif #include #include #include #include #include #include #ifndef _WIN32 #define min(a, b) (((a) < (b)) ? (a) : (b)) #endif #include #ifndef WIN32 #include "rtlsdr_rpc.h" #endif #include /* cond dumbness */ #define safe_cond_signal(n, m) pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m) #define safe_cond_wait(n, m) pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m) /* * All libusb callback functions should be marked with the LIBUSB_CALL macro * to ensure that they are compiled with the same calling convention as libusb. * * If the macro isn't available in older libusb versions, we simply define it. */ #ifndef LIBUSB_CALL #define LIBUSB_CALL #endif /* libusb < 1.0.9 doesn't have libusb_handle_events_timeout_completed */ #ifndef HAVE_LIBUSB_HANDLE_EVENTS_TIMEOUT_COMPLETED #define libusb_handle_events_timeout_completed(ctx, tv, c) \ libusb_handle_events_timeout(ctx, tv) #endif /* two raised to the power of n */ #define TWO_POW(n) ((double)(1ULL<<(n))) #include #include #include "tuner_e4k.h" #include "tuner_fc0012.h" #include "tuner_fc0013.h" #include "tuner_fc2580.h" #include "tuner_r82xx.h" #include #include #include #ifndef _WIN32 #include #include #endif #include #define LOG_API_CALLS 0 #define LOG_API_SET_FREQ 0 #define PRINT_UDP_SRV_MSGS 0 #define INIT_R820T_TUNER_GAIN 0 #define ENBALE_R820T_HARM_OPT 1 #define ENABLE_VCO_OPTIONS 1 /* activate/use RTL's IF AGC control .. from https://github.com/old-dab/rtlsdr * purpose: make AGC more smooth .. and NOT freeze * most of it is in switch case on tuner_type in rtlsdr_open() */ #define USE_OLD_DAB_IF_GAIN 1 typedef struct rtlsdr_tuner_iface { /* tuner interface */ int (*init)(void *); int (*exit)(void *); int (*set_freq)(void *, uint32_t freq /* Hz */); int (*set_freq64)(void *, uint64_t freq /* Hz */); int (*set_bw)(void *, int bw /* Hz */, uint32_t *applied_bw /* configured bw in Hz */, int apply /* 1 == configure it!, 0 == deliver applied_bw */); int (*set_bw_center)(void *, int32_t if_band_center_freq); int (*set_gain)(void *, int gain /* tenth dB */); int (*set_if_gain)(void *, int stage, int gain /* tenth dB */); int (*set_gain_mode)(void *, int manual); int (*set_i2c_register)(void *, unsigned i2c_register, unsigned data /* byte */, unsigned mask /* byte */ ); int (*set_i2c_override)(void *, unsigned i2c_register, unsigned data /* byte */, unsigned mask /* byte */ ); unsigned (*get_i2c_register)(void *, int i2c_register); /* read single register */ int (*get_i2c_reg_array)(void *, unsigned char* data, int len); /* -cs- */ int (*set_sideband)(void *, int sideband); } rtlsdr_tuner_iface_t; enum rtlsdr_async_status { RTLSDR_INACTIVE = 0, RTLSDR_CANCELING, RTLSDR_RUNNING }; #define FIR_LEN 16 /* * FIR coefficients. * * The filter is running at XTal frequency. It is symmetric filter with 32 * coefficients. Only first 16 coefficients are specified, the other 16 * use the same values but in reversed order. The first coefficient in * the array is the outer one, the last, the last is the inner one. * First 8 coefficients are 8 bit signed integers, the next 8 coefficients * are 12 bit signed integers. All coefficients have the same weight. * * Default FIR coefficients used for DAB/FM by the Windows driver, * the DVB driver uses different ones */ static const int fir_default[FIR_LEN] = { -54, -36, -41, -40, -32, -14, 14, 53, /* 8 bit signed */ 101, 156, 215, 273, 327, 372, 404, 421 /* 12 bit signed */ }; enum softagc_mode { SOFTAGC_OFF = 0, /* off */ SOFTAGC_ON_CHANGE, /* activate on initial start and on relevant changes .. and deactivate afterwards */ SOFTAGC_AUTO_ATTEN, /* operate full time - but do only attenuate after initial control (ON_CHANGE) */ SOFTAGC_AUTO /* operate full time - attenuate and gain */ }; enum softagc_stateT { SOFTSTATE_OFF = 0, SOFTSTATE_ON, SOFTSTATE_RESET_CONT, SOFTSTATE_RESET, SOFTSTATE_INIT }; struct softagc_state { pthread_t command_thread; pthread_mutex_t mutex; pthread_cond_t cond; volatile int exit_command_thread; volatile int command_newGain; volatile int command_changeGain; enum softagc_stateT agcState; /* active: don't forward samples while active for initial measurement */ enum softagc_mode softAgcMode; int verbose; float scanTimeMs; /* scan duration per gain level - to look for maximum */ float deadTimeMs; /* dead time in ms - after changing tuner gain */ int scanTimeSps; /* scan duration in samples */ int deadTimeSps; /* dead time in samples */ volatile int remainingDeadSps; /* dead time in samples */ int remainingScanSps; /* scan duration in samples */ int numInHisto; /* number of values in histogram */ int histo[16]; /* count histogram over high 4 bits */ int gainIdx; /* currently tested gain idx */ int softAgcBiasT; int rpcNumGains; /* local copy for RPC speedup */ int * rpcGainValues; }; struct rtlsdr_dev { libusb_context *ctx; struct libusb_device_handle *devh; uint32_t xfer_buf_num; uint32_t xfer_buf_len; struct libusb_transfer **xfer; unsigned char **xfer_buf; rtlsdr_read_async_cb_t cb; void *cb_ctx; volatile enum rtlsdr_async_status async_status; int async_cancel; int use_zerocopy; /* rtl demod context */ uint32_t rate; /* Hz */ uint32_t rtl_xtal; /* Hz */ int fir[FIR_LEN]; int direct_sampling; int rtl_vga_control; /* tuner context */ enum rtlsdr_tuner tuner_type; rtlsdr_tuner_iface_t *tuner; uint32_t tun_xtal; /* Hz */ uint64_t freq; /* Hz */ uint32_t bw; uint32_t offs_freq; /* Hz */ int32_t if_band_center_freq; /* Hz - rtlsdr_set_tuner_band_center() */ int tuner_if_freq; int tuner_sideband; int rtl_spectrum_sideband; /* buffered last sideband. 1: LSB; 2: USB */ int corr; /* ppm */ /* int gain; * tenth dB */ enum rtlsdr_ds_mode direct_sampling_mode; uint32_t direct_sampling_threshold; /* Hz */ struct e4k_state e4k_s; struct r82xx_config r82xx_c; struct r82xx_priv r82xx_p; /* soft tuner agc */ struct softagc_state softagc; /* -cs- Concurrent lock for the periodic reading of I2C registers */ pthread_mutex_t cs_mutex; pthread_mutexattr_t cs_mutex_attr; /* UDP controller server */ #ifdef WITH_UDP_SERVER #define UDP_TX_BUFLEN 1024 unsigned udpPortNo; /* default: 32323 */ int override_if_freq; int override_if_flag; int last_if_freq; pthread_t srv_thread; SOCKET udpS; struct sockaddr_in server; struct sockaddr_in si_other; int srv_started; char buf[UDP_TX_BUFLEN]; #ifdef _WIN32 WSADATA wsa; int recv_len; int slen; #else ssize_t recv_len; socklen_t slen; #endif #endif int biast_gpio_pin_no; uint32_t gpio_state_known; /* bitmask over pins 0 .. 7 */ uint32_t gpio_state; /* bitmask over pins 0 .. 7: = 0 == write, 1 == read */ int called_set_opt; /* status */ int dev_lost; int driver_active; unsigned int xfer_errors; int i2c_repeater_on; int rc_active; int verbose; int dev_num; char manufact[256]; char product[256]; }; static int rtlsdr_demod_write_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint16_t val, uint8_t len); static int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq); static int rtlsdr_update_ds(rtlsdr_dev_t *dev, uint64_t freq); static int rtlsdr_set_spectrum_inversion(rtlsdr_dev_t *dev, int sideband); static void softagc_init(rtlsdr_dev_t *dev); static void softagc_uninit(rtlsdr_dev_t *dev); static int reactivate_softagc(rtlsdr_dev_t *dev, enum softagc_stateT newState); /* generic tuner interface functions, shall be moved to the tuner implementations */ int e4000_init(void *dev) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; devt->e4k_s.i2c_addr = E4K_I2C_ADDR; rtlsdr_get_xtal_freq(devt, NULL, &devt->e4k_s.vco.fosc); devt->e4k_s.rtl_dev = dev; return e4k_init(&devt->e4k_s); } int e4000_exit(void *dev) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return e4k_standby(&devt->e4k_s, 1); } int e4000_set_freq(void *dev, uint32_t freq) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return e4k_tune_freq(&devt->e4k_s, freq); } int e4000_set_bw(void *dev, int bw, uint32_t *applied_bw, int apply) { int r = 0; rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; if(!apply) return 0; r |= e4k_if_filter_bw_set(&devt->e4k_s, E4K_IF_FILTER_MIX, bw); r |= e4k_if_filter_bw_set(&devt->e4k_s, E4K_IF_FILTER_RC, bw); r |= e4k_if_filter_bw_set(&devt->e4k_s, E4K_IF_FILTER_CHAN, bw); return r; } int e4000_set_gain(void *dev, int gain) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; int mixgain = (gain > 340) ? 12 : 4; #if 0 int enhgain = (gain - 420); #endif if(e4k_set_lna_gain(&devt->e4k_s, min(300, gain - mixgain * 10)) == -EINVAL) return -1; if(e4k_mixer_gain_set(&devt->e4k_s, mixgain) == -EINVAL) return -1; #if 0 /* enhanced mixer gain seems to have no effect */ if(enhgain >= 0) if(e4k_set_enh_gain(&devt->e4k_s, enhgain) == -EINVAL) return -1; #endif return 0; } int e4000_set_if_gain(void *dev, int stage, int gain) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return e4k_if_gain_set(&devt->e4k_s, (uint8_t)stage, (int8_t)(gain / 10)); } int e4000_set_gain_mode(void *dev, int manual) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return e4k_enable_manual_gain(&devt->e4k_s, manual); } int fc0012_exit(void *dev) { return 0; } int fc0012_set_freq(void *dev, uint32_t freq) { /* select V-band/U-band filter */ rtlsdr_set_gpio_bit(dev, 6, (freq > 300000000) ? 1 : 0); return fc0012_set_params(dev, freq, 6000000); } int fc0012_set_bw(void *dev, int bw, uint32_t *applied_bw, int apply) { return 0; } int fc0012_set_gain_mode(void *dev, int manual) { return 0; } int _fc0012_set_i2c_register(void *dev, unsigned i2c_register, unsigned data, unsigned mask ) { return fc0012_set_i2c_register(dev, i2c_register, data); } int _fc0013_init(void *dev) { return fc0013_init(dev); } int fc0013_exit(void *dev) { return 0; } int fc0013_set_freq(void *dev, uint32_t freq) { return fc0013_set_params(dev, freq, 6000000); } int fc0013_set_bw(void *dev, int bw, uint32_t *applied_bw, int apply) { return 0; } int _fc0013_set_gain(void *dev, int gain) { return fc0013_set_lna_gain(dev, gain); } int fc2580_init(void *dev) { return fc2580_Initialize(dev); } int fc2580_exit(void *dev) { return 0; } int _fc2580_set_freq(void *dev, uint32_t freq) { return fc2580_SetRfFreqHz(dev, freq); } int fc2580_set_bw(void *dev, int bw, uint32_t *applied_bw, int apply) { if(!apply) return 0; return fc2580_SetBandwidthMode(dev, 1); } int fc2580_set_gain(void *dev, int gain) { return 0; } int fc2580_set_gain_mode(void *dev, int manual) { return 0; } int r820t_init(void *dev) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; devt->r82xx_p.rtl_dev = dev; if (devt->tuner_type == RTLSDR_TUNER_R828D) { devt->r82xx_c.i2c_addr = R828D_I2C_ADDR; devt->r82xx_c.rafael_chip = CHIP_R828D; } else { devt->r82xx_c.i2c_addr = R820T_I2C_ADDR; devt->r82xx_c.rafael_chip = CHIP_R820T; } rtlsdr_get_xtal_freq(devt, NULL, &devt->r82xx_c.xtal); devt->r82xx_c.max_i2c_msg_len = 8; devt->r82xx_c.use_predetect = 0; devt->r82xx_p.cfg = &devt->r82xx_c; return r82xx_init(&devt->r82xx_p); } int r820t_exit(void *dev) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return r82xx_standby(&devt->r82xx_p); } int r820t_set_freq64(void *dev, uint64_t freq) { int r, ri, flip, sideband; rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; r = r82xx_set_freq64(&devt->r82xx_p, freq); sideband = r82xx_get_sideband(&devt->r82xx_p); flip = r82xx_flip_rtl_sideband(&devt->r82xx_p); ri = rtlsdr_set_spectrum_inversion(devt, sideband ^ flip); if (ri) { if ( devt->verbose ) fprintf(stderr, "r820t_set_freq(%f MHz): rtlsdr_set_spectrum_inversion() returned %d\n", freq * 1E-6, r); return ri; } return r; } int r820t_set_freq(void *dev, uint32_t freq) { return r820t_set_freq64(dev, (uint64_t)freq); } int r820t_set_bw(void *dev, int bw, uint32_t *applied_bw, int apply) { int r, iffreq; rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; iffreq = r82xx_set_bandwidth(&devt->r82xx_p, bw, devt->rate, applied_bw, apply); if(!apply) return 0; if(iffreq < 0) { r = iffreq; if ( devt->verbose ) fprintf(stderr, "r820t_set_bw(%d): r82xx_set_bandwidth() returned error %d\n", bw, r); return r; } devt->tuner_if_freq = iffreq; iffreq = (devt->tuner_sideband) /* -1 for USB; +1 for LSB */ ? ( devt->tuner_if_freq - devt->if_band_center_freq ) : ( devt->tuner_if_freq + devt->if_band_center_freq ); r = rtlsdr_set_if_freq(devt, iffreq ); if (r) { if ( devt->verbose ) fprintf(stderr, "r820t_set_bw(%d): rtlsdr_set_if_freq(%d) returned error %d\n", bw, iffreq, r); return r; } r = rtlsdr_set_center_freq64(devt, devt->freq); if ( r && devt->verbose ) fprintf(stderr, "r820t_set_bw(%d): rtlsdr_set_center_freq(%f MHz) returned error %d\n", bw, devt->freq * 1E-6, r); return r; } int r820t_set_bw_center(void *dev, int32_t if_band_center_freq) { int r, iffreq; rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; iffreq = r82xx_set_bw_center(&devt->r82xx_p, if_band_center_freq); if(iffreq < 0) { r = iffreq; if ( devt->verbose ) fprintf(stderr, "r820t_set_bw_center(%d): r82xx_set_bw_center() returned error %d\n", if_band_center_freq, r); return r; } devt->tuner_if_freq = iffreq; devt->if_band_center_freq = if_band_center_freq; iffreq = (devt->tuner_sideband) /* -1 for USB; +1 for LSB */ ? ( devt->tuner_if_freq - devt->if_band_center_freq ) : ( devt->tuner_if_freq + devt->if_band_center_freq ); r = rtlsdr_set_if_freq(devt, iffreq ); if (r) { if ( devt->verbose ) fprintf(stderr, "r820t_set_bw_center(%d): rtlsdr_set_if_freq(%d) returned error %d\n", if_band_center_freq, iffreq, r); return r; } r = rtlsdr_set_center_freq64(devt, devt->freq); if ( r && devt->verbose ) fprintf(stderr, "r820t_set_bw_center(%d): rtlsdr_set_center_freq(%f MHz) returned error %d\n", if_band_center_freq, devt->freq * 1E-6, r); return r; } int rtlsdr_vga_control( rtlsdr_dev_t* devt, int rc, int rtl_vga_control ) { if (rc < 0) return rc; if ( rtl_vga_control != devt->rtl_vga_control ) { /* enable/disable RF AGC loop */ #if USE_OLD_DAB_IF_GAIN == 0 rc = rtlsdr_demod_write_reg(devt, 1, 0x04, rtl_vga_control ? 0x80 : 0x00, 1); if ( devt->verbose ) fprintf(stderr, "rtlsdr_vga_control(%s) returned %d\n" , rtl_vga_control ? "activate" : "deactivate", rc ); #endif devt->rtl_vga_control = rtl_vga_control; } return rc; } int r820t_set_gain(void *dev, int gain) { int rc, rtl_vga_control = 0; rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; rc = r82xx_set_gain(&devt->r82xx_p, 1, gain, 0, 0, 0, 0, &rtl_vga_control); rc = rtlsdr_vga_control(devt, rc, rtl_vga_control); return rc; } int r820t_set_gain_ext(void *dev, int lna_gain, int mixer_gain, int vga_gain) { int rc, rtl_vga_control = 0; rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; rc = r82xx_set_gain(&devt->r82xx_p, 0, 0, 1, lna_gain, mixer_gain, vga_gain, &rtl_vga_control); rc = rtlsdr_vga_control(devt, rc, rtl_vga_control); return rc; } int r820t_set_if_mode(void *dev, int if_mode) { int rc, rtl_vga_control = 0; rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; rc = r82xx_set_if_mode(&devt->r82xx_p, if_mode, &rtl_vga_control); rc = rtlsdr_vga_control(devt, rc, rtl_vga_control); return rc; } int r820t_set_gain_mode(void *dev, int manual) { int rc, rtl_vga_control = 0; rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; rc = r82xx_set_gain(&devt->r82xx_p, manual, 0, 0, 0, 0, 0, &rtl_vga_control); rc = rtlsdr_vga_control(devt, rc, rtl_vga_control); return rc; } unsigned r820t_get_i2c_register(void *dev, int reg) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return r82xx_read_cache_reg(&devt->r82xx_p,reg); } int r820t_set_i2c_register(void *dev, unsigned i2c_register, unsigned data, unsigned mask ) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return r82xx_set_i2c_register(&devt->r82xx_p, i2c_register, data, mask); } /* -cs- */ int r820t_get_i2c_reg_array(void *dev, unsigned char* data, int len) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return r82xx_get_i2c_register(&devt->r82xx_p, data, len); } int r820t_set_sideband(void *dev, int sideband) { int r, flip; rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; if ( devt->verbose ) fprintf(stderr, "r820t_set_sideband(%d): r82xx_set_sideband() ..\n", sideband); r = r82xx_set_sideband(&devt->r82xx_p, sideband); if(r < 0) { if ( devt->verbose ) fprintf(stderr, "r820t_set_sideband(%d): r82xx_set_sideband() returned %d\n", sideband, r); return r; } flip = r82xx_flip_rtl_sideband(&devt->r82xx_p); if ( devt->verbose ) fprintf(stderr, "r820t_set_sideband(%d): rtlsdr_set_spectrum_inversion() ^ %d from tuner ..\n", sideband, flip); r = rtlsdr_set_spectrum_inversion(devt, sideband ^ flip); if (r) { if ( devt->verbose ) fprintf(stderr, "r820t_set_sideband(%d): rtlsdr_set_spectrum_inversion() returned %d\n", sideband, r); return r; } if (!devt->freq) return r; if ( devt->verbose ) fprintf(stderr, "r820t_set_sideband(%d): rtlsdr_set_center_freq(%f MHz) ..\n", sideband, devt->freq * 1E-6); r = rtlsdr_set_center_freq64(devt, devt->freq); if (r) { if ( devt->verbose ) fprintf(stderr, "r820t_set_sideband(%d): rtlsdr_set_center_freq(%f MHz) returned %d\n", sideband, devt->freq * 1E-6, r); } return r; } int r820t_set_i2c_override(void *dev, unsigned i2c_register, unsigned data, unsigned mask ) { rtlsdr_dev_t* devt = (rtlsdr_dev_t*)dev; return r82xx_set_i2c_override(&devt->r82xx_p, i2c_register, data, mask); } /* definition order must match enum rtlsdr_tuner */ static rtlsdr_tuner_iface_t tuners[] = { { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL /* dummy for unknown tuners */ }, { e4000_init, e4000_exit, e4000_set_freq, NULL, e4000_set_bw, NULL, e4000_set_gain, e4000_set_if_gain, e4000_set_gain_mode, NULL, NULL, NULL, NULL, NULL }, { fc0012_init, fc0012_exit, fc0012_set_freq, NULL, fc0012_set_bw, NULL, fc0012_set_gain, NULL, fc0012_set_gain_mode, _fc0012_set_i2c_register, NULL, NULL, fc0012_get_i2c_register, NULL }, { _fc0013_init, fc0013_exit, fc0013_set_freq, NULL, fc0013_set_bw, NULL, _fc0013_set_gain, NULL, fc0013_set_gain_mode, NULL, NULL, NULL, NULL, NULL }, { fc2580_init, fc2580_exit, _fc2580_set_freq, NULL, fc2580_set_bw, NULL, fc2580_set_gain, NULL, fc2580_set_gain_mode, NULL, NULL, NULL, NULL, NULL }, { r820t_init, r820t_exit, r820t_set_freq, r820t_set_freq64, r820t_set_bw, r820t_set_bw_center, r820t_set_gain, NULL, r820t_set_gain_mode, r820t_set_i2c_register, r820t_set_i2c_override, r820t_get_i2c_register, r820t_get_i2c_reg_array, r820t_set_sideband }, { r820t_init, r820t_exit, r820t_set_freq, r820t_set_freq64, r820t_set_bw, r820t_set_bw_center, r820t_set_gain, NULL, r820t_set_gain_mode, r820t_set_i2c_register, r820t_set_i2c_override, r820t_get_i2c_register, r820t_get_i2c_reg_array, r820t_set_sideband }, }; typedef struct rtlsdr_dongle { uint16_t vid; uint16_t pid; const char *name; } rtlsdr_dongle_t; /* * Please add your device here and send a patch to osmocom-sdr@lists.osmocom.org */ static rtlsdr_dongle_t known_devices[] = { { 0x0bda, 0x2832, "Generic RTL2832U" }, { 0x0bda, 0x2838, "Generic RTL2832U OEM" }, { 0x0413, 0x6680, "DigitalNow Quad DVB-T PCI-E card" }, { 0x0413, 0x6f0f, "Leadtek WinFast DTV Dongle mini D" }, { 0x0458, 0x707f, "Genius TVGo DVB-T03 USB dongle (Ver. B)" }, { 0x0ccd, 0x00a9, "Terratec Cinergy T Stick Black (rev 1)" }, { 0x0ccd, 0x00b3, "Terratec NOXON DAB/DAB+ USB dongle (rev 1)" }, { 0x0ccd, 0x00b4, "Terratec Deutschlandradio DAB Stick" }, { 0x0ccd, 0x00b5, "Terratec NOXON DAB Stick - Radio Energy" }, { 0x0ccd, 0x00b7, "Terratec Media Broadcast DAB Stick" }, { 0x0ccd, 0x00b8, "Terratec BR DAB Stick" }, { 0x0ccd, 0x00b9, "Terratec WDR DAB Stick" }, { 0x0ccd, 0x00c0, "Terratec MuellerVerlag DAB Stick" }, { 0x0ccd, 0x00c6, "Terratec Fraunhofer DAB Stick" }, { 0x0ccd, 0x00d3, "Terratec Cinergy T Stick RC (Rev.3)" }, { 0x0ccd, 0x00d7, "Terratec T Stick PLUS" }, { 0x0ccd, 0x00e0, "Terratec NOXON DAB/DAB+ USB dongle (rev 2)" }, { 0x1209, 0x2832, "Generic RTL2832U" }, { 0x1554, 0x5020, "PixelView PV-DT235U(RN)" }, { 0x15f4, 0x0131, "Astrometa DVB-T/DVB-T2" }, { 0x15f4, 0x0133, "HanfTek DAB+FM+DVB-T" }, { 0x185b, 0x0620, "Compro Videomate U620F"}, { 0x185b, 0x0650, "Compro Videomate U650F"}, { 0x185b, 0x0680, "Compro Videomate U680F"}, { 0x1b80, 0xd393, "GIGABYTE GT-U7300" }, { 0x1b80, 0xd394, "DIKOM USB-DVBT HD" }, { 0x1b80, 0xd395, "Peak 102569AGPK" }, { 0x1b80, 0xd397, "KWorld KW-UB450-T USB DVB-T Pico TV" }, { 0x1b80, 0xd398, "Zaapa ZT-MINDVBZP" }, { 0x1b80, 0xd39d, "SVEON STV20 DVB-T USB & FM" }, { 0x1b80, 0xd3a4, "Twintech UT-40" }, { 0x1b80, 0xd3a8, "ASUS U3100MINI_PLUS_V2" }, { 0x1b80, 0xd3af, "SVEON STV27 DVB-T USB & FM" }, { 0x1b80, 0xd3b0, "SVEON STV21 DVB-T USB & FM" }, { 0x1d19, 0x1101, "Dexatek DK DVB-T Dongle (Logilink VG0002A)" }, { 0x1d19, 0x1102, "Dexatek DK DVB-T Dongle (MSI DigiVox mini II V3.0)" }, { 0x1d19, 0x1103, "Dexatek Technology Ltd. DK 5217 DVB-T Dongle" }, { 0x1d19, 0x1104, "MSI DigiVox Micro HD" }, { 0x1f4d, 0xa803, "Sweex DVB-T USB" }, { 0x1f4d, 0xb803, "GTek T803" }, { 0x1f4d, 0xc803, "Lifeview LV5TDeluxe" }, { 0x1f4d, 0xd286, "MyGica TD312" }, { 0x1f4d, 0xd803, "PROlectrix DV107669" }, }; #define DEFAULT_BUF_NUMBER 15 #define DEFAULT_BUF_LENGTH (16 * 32 * 512) /* buf_len: * must be multiple of 512 - else it will be overwritten * in rtlsdr_read_async() in librtlsdr.c with DEFAULT_BUF_LENGTH (= 16*32 *512 = 512 *512) * * -> 512*512 -> 1048 ms @ 250 kS or 81.92 ms @ 3.2 MS (internal default) * -> 32*512 -> 65 ms @ 250 kS or 5.12 ms @ 3.2 MS (new default) */ #define DEF_RTL_XTAL_FREQ 28800000 #define MIN_RTL_XTAL_FREQ (DEF_RTL_XTAL_FREQ - 1000) #define MAX_RTL_XTAL_FREQ (DEF_RTL_XTAL_FREQ + 1000) #define CTRL_IN (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_IN) #define CTRL_OUT (LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_ENDPOINT_OUT) #define CTRL_TIMEOUT 300 #define BULK_TIMEOUT 10000 #define EEPROM_ADDR 0xa0 enum usb_reg { USB_SYSCTL = 0x2000, USB_CTRL = 0x2010, USB_STAT = 0x2014, USB_EPA_CFG = 0x2144, USB_EPA_CTL = 0x2148, USB_EPA_MAXPKT = 0x2158, USB_EPA_MAXPKT_2 = 0x215a, USB_EPA_FIFO_CFG = 0x2160, }; enum sys_reg { DEMOD_CTL = 0x3000, GPO = 0x3001, GPI = 0x3002, GPOE = 0x3003, GPD = 0x3004, SYSINTE = 0x3005, SYSINTS = 0x3006, GP_CFG0 = 0x3007, GP_CFG1 = 0x3008, SYSINTE_1 = 0x3009, SYSINTS_1 = 0x300a, DEMOD_CTL_1 = 0x300b, IR_SUSPEND = 0x300c, /* IrDA registers */ SYS_IRRC_PSR = 0x3020, /* IR protocol selection */ SYS_IRRC_PER = 0x3024, /* IR protocol extension */ SYS_IRRC_SF = 0x3028, /* IR sampling frequency */ SYS_IRRC_DPIR = 0x302C, /* IR data package interval */ SYS_IRRC_CR = 0x3030, /* IR control */ SYS_IRRC_RP = 0x3034, /* IR read port */ SYS_IRRC_SR = 0x3038, /* IR status */ /* I2C master registers */ SYS_I2CCR = 0x3040, /* I2C clock */ SYS_I2CMCR = 0x3044, /* I2C master control */ SYS_I2CMSTR = 0x3048, /* I2C master SCL timing */ SYS_I2CMSR = 0x304C, /* I2C master status */ SYS_I2CMFR = 0x3050, /* I2C master FIFO */ /* * IR registers */ IR_RX_BUF = 0xFC00, IR_RX_IE = 0xFD00, IR_RX_IF = 0xFD01, IR_RX_CTRL = 0xFD02, IR_RX_CFG = 0xFD03, IR_MAX_DURATION0 = 0xFD04, IR_MAX_DURATION1 = 0xFD05, IR_IDLE_LEN0 = 0xFD06, IR_IDLE_LEN1 = 0xFD07, IR_GLITCH_LEN = 0xFD08, IR_RX_BUF_CTRL = 0xFD09, IR_RX_BUF_DATA = 0xFD0A, IR_RX_BC = 0xFD0B, IR_RX_CLK = 0xFD0C, IR_RX_C_COUNT_L = 0xFD0D, IR_RX_C_COUNT_H = 0xFD0E, IR_SUSPEND_CTRL = 0xFD10, IR_ERR_TOL_CTRL = 0xFD11, IR_UNIT_LEN = 0xFD12, IR_ERR_TOL_LEN = 0xFD13, IR_MAX_H_TOL_LEN = 0xFD14, IR_MAX_L_TOL_LEN = 0xFD15, IR_MASK_CTRL = 0xFD16, IR_MASK_DATA = 0xFD17, IR_RES_MASK_ADDR = 0xFD18, IR_RES_MASK_T_LEN = 0xFD19, }; enum blocks { DEMODB = 0, USBB = 1, SYSB = 2, TUNB = 3, ROMB = 4, IRB = 5, IICB = 6, }; static const char * dsmode_str[] = { "0: use I & Q", "1: use I", "2: use Q", "3: use I below threshold frequency", "4: use Q below threshold frequency" }; int rtlsdr_read_array(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t *array, uint8_t len) { int r; uint16_t index = (block << 8); if (block == IRB) index = (SYSB << 8) | 0x01; r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, array, len, CTRL_TIMEOUT); #if 0 if (r < 0) fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); #endif return r; } int rtlsdr_write_array(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t *array, uint8_t len) { int r; uint16_t index = (block << 8) | 0x10; if (block == IRB) index = (SYSB << 8) | 0x11; r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, array, len, CTRL_TIMEOUT); #if 0 if (r < 0) fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); #endif return r; } int rtlsdr_i2c_write_reg(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t reg, uint8_t val) { uint16_t addr = i2c_addr; uint8_t data[2]; data[0] = reg; data[1] = val; return rtlsdr_write_array(dev, IICB, addr, (uint8_t *)&data, 2); } uint8_t rtlsdr_i2c_read_reg(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t reg) { uint16_t addr = i2c_addr; uint8_t data = 0; rtlsdr_write_array(dev, IICB, addr, ®, 1); rtlsdr_read_array(dev, IICB, addr, &data, 1); return data; } int rtlsdr_i2c_write(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t *buffer, int len) { uint16_t addr = i2c_addr; if (!dev) return -1; return rtlsdr_write_array(dev, IICB, addr, buffer, len); } int rtlsdr_i2c_read(rtlsdr_dev_t *dev, uint8_t i2c_addr, uint8_t *buffer, int len) { uint16_t addr = i2c_addr; if (!dev) return -1; return rtlsdr_read_array(dev, IICB, addr, buffer, len); } uint16_t rtlsdr_read_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t len) { int r; unsigned char data[2]; uint16_t reg; uint16_t index = (block << 8); if (block == IRB) index = (SYSB << 8) | 0x01; r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); if (r < 0) fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); reg = (data[1] << 8) | data[0]; return reg; } int rtlsdr_write_reg(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint16_t val, uint8_t len) { int r; unsigned char data[2]; uint16_t index = (block << 8) | 0x10; if (block == IRB) index = (SYSB << 8) | 0x11; if (len == 1) data[0] = val & 0xff; else data[0] = val >> 8; data[1] = val & 0xff; r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, data, len, CTRL_TIMEOUT); if (r < 0) fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); return r; } uint16_t rtlsdr_demod_read_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint8_t len) { int r; unsigned char data[2]; uint16_t index = page; uint16_t reg; addr = (addr << 8) | 0x20; r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); if (r < 0) fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); reg = (data[1] << 8) | data[0]; return reg; } int rtlsdr_demod_write_reg(rtlsdr_dev_t *dev, uint8_t page, uint16_t addr, uint16_t val, uint8_t len) { int r; unsigned char data[2]; uint16_t index = 0x10 | page; addr = (addr << 8) | 0x20; if (len == 1) data[0] = val & 0xff; else data[0] = val >> 8; data[1] = val & 0xff; r = libusb_control_transfer(dev->devh, CTRL_OUT, 0, addr, index, data, len, CTRL_TIMEOUT); if (r < 0) fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); rtlsdr_demod_read_reg(dev, 0x0a, 0x01, 1); return (r == len) ? 0 : -1; } int rtlsdr_set_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int val) { uint16_t r, retval; gpio = 1 << gpio; r = rtlsdr_read_reg(dev, SYSB, GPO, 1); r = val ? (r | gpio) : (r & ~gpio); retval = rtlsdr_write_reg(dev, SYSB, GPO, r, 1); return retval; } int rtlsdr_set_gpio_output(rtlsdr_dev_t *dev, uint8_t gpio) { int r, retval = 0; gpio = 1 << gpio; /* state: bitmask over pins 0 .. 7: = 0 == write, 1 == read */ if ( !(dev->gpio_state_known & gpio) || (dev->gpio_state & gpio) ) { r = rtlsdr_read_reg(dev, SYSB, GPD, 1); retval = rtlsdr_write_reg(dev, SYSB, GPD, r & ~gpio, 1); if (retval < 0) return retval; r = rtlsdr_read_reg(dev, SYSB, GPOE, 1); retval = rtlsdr_write_reg(dev, SYSB, GPOE, r | gpio, 1); if (retval < 0) return retval; dev->gpio_state_known |= gpio; dev->gpio_state &= ~( (uint32_t)gpio ); } return retval; } int rtlsdr_get_gpio_bit(rtlsdr_dev_t *dev, uint8_t gpio, int *val) { uint16_t r; gpio = 1 << gpio; r = rtlsdr_read_reg(dev, SYSB, GPI, 1); *val = (r & gpio) ? 1 : 0; return 0; /* no way to determine error with rtlsdr_read_reg() for now! */ } int rtlsdr_set_gpio_input(rtlsdr_dev_t *dev, uint8_t gpio) { int r, retval = 0; gpio = 1 << gpio; /* state: bitmask over pins 0 .. 7: = 0 == write, 1 == read */ if ( !(dev->gpio_state_known & gpio) || !(dev->gpio_state & gpio) ) { r = rtlsdr_read_reg(dev, SYSB, GPD, 1); retval = rtlsdr_write_reg(dev, SYSB, GPD, r | gpio, 1); if (retval < 0) return retval; r = rtlsdr_read_reg(dev, SYSB, GPOE, 1); retval = rtlsdr_write_reg(dev, SYSB, GPOE, r & ~gpio, 1); if (retval < 0) return retval; dev->gpio_state_known |= gpio; dev->gpio_state |= ( (uint32_t)gpio ); } return retval; } int rtlsdr_set_gpio_status(rtlsdr_dev_t *dev, int *status ) { int r; r = rtlsdr_read_reg(dev, SYSB, GPD, 1); *status = r; return 0; /* no way to determine error with rtlsdr_read_reg() for now! */ } int rtlsdr_get_gpio_byte(rtlsdr_dev_t *dev, int *val) { *val = rtlsdr_read_reg(dev, SYSB, GPI, 1); return 0; /* no way to determine error with rtlsdr_read_reg() for now! */ } int rtlsdr_set_gpio_byte(rtlsdr_dev_t *dev, int val) { int retval = rtlsdr_write_reg(dev, SYSB, GPO, val, 1); return retval; } void rtlsdr_set_i2c_repeater(rtlsdr_dev_t *dev, int on) { if (on) pthread_mutex_lock(&dev->cs_mutex); /* hayguen: don't do early exit for mutex! * just skip rtlsdr_demod_write_reg() call * if (on == dev->i2c_repeater_on) * return; */ if (on != dev->i2c_repeater_on) { dev->i2c_repeater_on = on; rtlsdr_demod_write_reg(dev, 1, 0x01, on ? 0x18 : 0x10, 1); } if (!on) pthread_mutex_unlock(&dev->cs_mutex); } int rtlsdr_set_fir(rtlsdr_dev_t *dev) { uint8_t fir[20]; int i; /* format: int8_t[8] */ for (i = 0; i < 8; ++i) { const int val = dev->fir[i]; if (val < -128 || val > 127) { return -1; } fir[i] = val; } /* format: int12_t[8] */ for (i = 0; i < 8; i += 2) { const int val0 = dev->fir[8+i]; const int val1 = dev->fir[8+i+1]; if (val0 < -2048 || val0 > 2047 || val1 < -2048 || val1 > 2047) { return -1; } fir[8+i*3/2] = val0 >> 4; fir[8+i*3/2+1] = (val0 << 4) | ((val1 >> 8) & 0x0f); fir[8+i*3/2+2] = val1; } for (i = 0; i < (int)sizeof(fir); i++) { if (rtlsdr_demod_write_reg(dev, 1, 0x1c + i, fir[i], 1)) return -1; } return 0; } void rtlsdr_init_baseband(rtlsdr_dev_t *dev) { unsigned int i; /* initialize USB */ rtlsdr_write_reg(dev, USBB, USB_SYSCTL, 0x09, 1); rtlsdr_write_reg(dev, USBB, USB_EPA_MAXPKT, 0x0002, 2); rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x1002, 2); /* poweron demod */ rtlsdr_write_reg(dev, SYSB, DEMOD_CTL_1, 0x22, 1); rtlsdr_write_reg(dev, SYSB, DEMOD_CTL, 0xe8, 1); /* reset demod (bit 3, soft_rst) */ rtlsdr_demod_write_reg(dev, 1, 0x01, 0x14, 1); rtlsdr_demod_write_reg(dev, 1, 0x01, 0x10, 1); /* disable spectrum inversion and adjacent channel rejection */ rtlsdr_demod_write_reg(dev, 1, 0x15, 0x00, 1); rtlsdr_demod_write_reg(dev, 1, 0x16, 0x0000, 2); /* clear both DDC shift and IF frequency registers */ for (i = 0; i < 6; i++) rtlsdr_demod_write_reg(dev, 1, 0x16 + i, 0x00, 1); rtlsdr_set_fir(dev); /* enable SDR mode, disable DAGC (bit 5) */ rtlsdr_demod_write_reg(dev, 0, 0x19, 0x05, 1); /* init FSM state-holding register */ rtlsdr_demod_write_reg(dev, 1, 0x93, 0xf0, 1); rtlsdr_demod_write_reg(dev, 1, 0x94, 0x0f, 1); /* disable AGC (en_dagc, bit 0) (this seems to have no effect) */ rtlsdr_demod_write_reg(dev, 1, 0x11, 0x00, 1); /* disable RF and IF AGC loop */ #if USE_OLD_DAB_IF_GAIN == 0 rtlsdr_demod_write_reg(dev, 1, 0x04, 0x00, 1); #endif dev->rtl_vga_control = 0; /* disable PID filter (enable_PID = 0) */ rtlsdr_demod_write_reg(dev, 0, 0x61, 0x60, 1); /* opt_adc_iq = 0, default ADC_I/ADC_Q datapath */ rtlsdr_demod_write_reg(dev, 0, 0x06, 0x80, 1); /* Enable Zero-IF mode (en_bbin bit), DC cancellation (en_dc_est), * IQ estimation/compensation (en_iq_comp, en_iq_est) */ rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1b, 1); /* disable 4.096 MHz clock output on pin TP_CK0 */ rtlsdr_demod_write_reg(dev, 0, 0x0d, 0x83, 1); } int rtlsdr_deinit_baseband(rtlsdr_dev_t *dev) { int r = 0; if (!dev) return -1; if (dev->tuner && dev->tuner->exit) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->exit(dev); /* deinitialize tuner */ rtlsdr_set_i2c_repeater(dev, 0); } /* poweroff demodulator and ADCs */ rtlsdr_write_reg(dev, SYSB, DEMOD_CTL, 0x20, 1); return r; } static int rtlsdr_set_if_freq(rtlsdr_dev_t *dev, uint32_t freq) { uint32_t rtl_xtal; int32_t if_freq; uint8_t tmp; int r; if (!dev) return -1; /* read corrected clock value */ if (rtlsdr_get_xtal_freq(dev, &rtl_xtal, NULL)) return -2; #ifdef WITH_UDP_SERVER dev->last_if_freq = freq; if ( dev->override_if_flag ) { if ( dev->verbose ) fprintf(stderr, "overriding rtlsdr_set_if_freq(): modifying %u to %d Hz\n" , freq, dev->override_if_freq ); freq = dev->override_if_freq; if ( dev->override_if_flag == 1 ) dev->override_if_flag = 0; } #endif if_freq = ((freq * TWO_POW(22)) / rtl_xtal) * (-1); tmp = (if_freq >> 16) & 0x3f; r = rtlsdr_demod_write_reg(dev, 1, 0x19, tmp, 1); tmp = (if_freq >> 8) & 0xff; r |= rtlsdr_demod_write_reg(dev, 1, 0x1a, tmp, 1); tmp = if_freq & 0xff; r |= rtlsdr_demod_write_reg(dev, 1, 0x1b, tmp, 1); return r; } static int rtlsdr_set_spectrum_inversion(rtlsdr_dev_t *dev, int sideband) { int r = 0; if ( dev->rtl_spectrum_sideband != sideband + 1 ) { if(sideband) /* disable spectrum inversion */ r = rtlsdr_demod_write_reg(dev, 1, 0x15, 0x00, 1); else /* enable spectrum inversion */ r = rtlsdr_demod_write_reg(dev, 1, 0x15, 0x01, 1); dev->rtl_spectrum_sideband = (r) ? 0 : (sideband + 1); } return r; } int rtlsdr_set_sample_freq_correction(rtlsdr_dev_t *dev, int ppm) { int r = 0; uint8_t tmp; int16_t offs = ppm * (-1) * TWO_POW(24) / 1000000; tmp = offs & 0xff; r |= rtlsdr_demod_write_reg(dev, 1, 0x3f, tmp, 1); tmp = (offs >> 8) & 0x3f; r |= rtlsdr_demod_write_reg(dev, 1, 0x3e, tmp, 1); return r; } int rtlsdr_set_xtal_freq(rtlsdr_dev_t *dev, uint32_t rtl_freq, uint32_t tuner_freq) { int r = 0; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_xtal_freq(rtl_freq %u, tuner_freq %u)\n", (unsigned)rtl_freq, (unsigned)tuner_freq); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_xtal_freq(dev, rtl_freq, tuner_freq); } #endif if (!dev) return -1; if (rtl_freq > 0 && (rtl_freq < MIN_RTL_XTAL_FREQ || rtl_freq > MAX_RTL_XTAL_FREQ)) return -2; if (rtl_freq > 0 && dev->rtl_xtal != rtl_freq) { dev->rtl_xtal = rtl_freq; /* update xtal-dependent settings */ if (dev->rate) r = rtlsdr_set_sample_rate(dev, dev->rate); } if (dev->tun_xtal != tuner_freq) { if (0 == tuner_freq) dev->tun_xtal = dev->rtl_xtal; else dev->tun_xtal = tuner_freq; /* read corrected clock value into e4k and r82xx structure */ if (rtlsdr_get_xtal_freq(dev, NULL, &dev->e4k_s.vco.fosc) || rtlsdr_get_xtal_freq(dev, NULL, &dev->r82xx_c.xtal)) return -3; /* update xtal-dependent settings */ if (dev->freq) r = rtlsdr_set_center_freq64(dev, dev->freq); } return r; } int rtlsdr_get_xtal_freq(rtlsdr_dev_t *dev, uint32_t *rtl_freq, uint32_t *tuner_freq) { #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_get_xtal_freq(rtl_freq, tuner_freq)\n"); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_xtal_freq(dev, rtl_freq, tuner_freq); } #endif if (!dev) return -1; #define APPLY_PPM_CORR(val,ppm) (((val) * (1.0 + (ppm) / 1e6))) if (rtl_freq) *rtl_freq = (uint32_t) APPLY_PPM_CORR(dev->rtl_xtal, dev->corr); if (tuner_freq) *tuner_freq = (uint32_t) APPLY_PPM_CORR(dev->tun_xtal, dev->corr); return 0; } int rtlsdr_get_usb_strings(rtlsdr_dev_t *dev, char *manufact, char *product, char *serial) { struct libusb_device_descriptor dd; libusb_device *device = NULL; const int buf_max = 256; int r = 0; #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_usb_strings(dev, manufact, product, serial); } #endif if (!dev || !dev->devh) return -1; device = libusb_get_device(dev->devh); r = libusb_get_device_descriptor(device, &dd); if (r < 0) return -1; if (manufact) { memset(manufact, 0, buf_max); libusb_get_string_descriptor_ascii(dev->devh, dd.iManufacturer, (unsigned char *)manufact, buf_max); } if (product) { memset(product, 0, buf_max); libusb_get_string_descriptor_ascii(dev->devh, dd.iProduct, (unsigned char *)product, buf_max); } if (serial) { memset(serial, 0, buf_max); libusb_get_string_descriptor_ascii(dev->devh, dd.iSerialNumber, (unsigned char *)serial, buf_max); } return 0; } int rtlsdr_write_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_t len) { int r = 0; int i; uint8_t cmd[2]; #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_write_eeprom(dev, data, offset, len); } #endif if (!dev) return -1; if ((len + offset) > 256) return -2; for (i = 0; i < len; i++) { cmd[0] = i + offset; r = rtlsdr_write_array(dev, IICB, EEPROM_ADDR, cmd, 1); r = rtlsdr_read_array(dev, IICB, EEPROM_ADDR, &cmd[1], 1); /* only write the byte if it differs */ if (cmd[1] == data[i]) continue; cmd[1] = data[i]; r = rtlsdr_write_array(dev, IICB, EEPROM_ADDR, cmd, 2); if (r != sizeof(cmd)) return -3; /* for some EEPROMs (e.g. ATC 240LC02) we need a delay * between write operations, otherwise they will fail */ #ifdef _WIN32 Sleep(5); #else usleep(5000); #endif } return 0; } int rtlsdr_read_eeprom(rtlsdr_dev_t *dev, uint8_t *data, uint8_t offset, uint16_t len) { int r = 0; int i; #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_read_eeprom(dev, data, offset, len); } #endif if (!dev) return -1; if ((len + offset) > 256) return -2; r = rtlsdr_write_array(dev, IICB, EEPROM_ADDR, &offset, 1); if (r < 0) return -3; for (i = 0; i < len; i++) { r = rtlsdr_read_array(dev, IICB, EEPROM_ADDR, data + i, 1); if (r < 0) return -3; } return r; } int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq) { int r = -1; #if LOG_API_CALLS && LOG_API_SET_FREQ fprintf(stderr, "LOG: rtlsdr_set_center_freq(freq %f MHz)\n", freq * 1E-6); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_center_freq(dev, freq); } #endif if (!dev || !dev->tuner) return -1; if (dev->direct_sampling_mode > RTLSDR_DS_Q) rtlsdr_update_ds(dev, freq); if (dev->direct_sampling) { r = rtlsdr_set_if_freq(dev, freq); } else if (dev->tuner && dev->tuner->set_freq) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_freq(dev, freq - dev->offs_freq); rtlsdr_set_i2c_repeater(dev, 0); } if (!r) dev->freq = freq; else dev->freq = 0; return r; } int rtlsdr_set_center_freq64(rtlsdr_dev_t *dev, uint64_t freq) { int r = -1; #if LOG_API_CALLS && LOG_API_SET_FREQ fprintf(stderr, "LOG: rtlsdr_set_center_freq64(freq %f MHz)\n", freq * 1E-6); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_center_freq(dev, freq); } #endif if (!dev || !dev->tuner) return -1; if (dev->direct_sampling_mode > RTLSDR_DS_Q) rtlsdr_update_ds(dev, freq); if (dev->direct_sampling) { r = rtlsdr_set_if_freq(dev, freq); } else if (dev->tuner && dev->tuner->set_freq64) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_freq64(dev, freq - dev->offs_freq); rtlsdr_set_i2c_repeater(dev, 0); } else if (dev->tuner && dev->tuner->set_freq) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_freq(dev, (uint32_t)freq - dev->offs_freq); rtlsdr_set_i2c_repeater(dev, 0); } if (!r) dev->freq = freq; else dev->freq = 0; return r; } int rtlsdr_is_tuner_PLL_locked(rtlsdr_dev_t *dev) { int r = -1; #if LOG_API_CALLS && LOG_API_SET_FREQ fprintf(stderr, "LOG: rtlsdr_is_tuner_PLL_locked()\n"); #endif if (!dev || !dev->tuner) return -1; if (dev->tuner_type != RTLSDR_TUNER_R820T && dev->tuner_type != RTLSDR_TUNER_R828D ) return -2; rtlsdr_set_i2c_repeater(dev, 1); r = r82xx_is_tuner_locked(&dev->r82xx_p); rtlsdr_set_i2c_repeater(dev, 0); return r; } uint32_t rtlsdr_get_center_freq(rtlsdr_dev_t *dev) { #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_center_freq(dev); } #endif if (!dev) return 0; return dev->freq; } uint64_t rtlsdr_get_center_freq64(rtlsdr_dev_t *dev) { #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_center_freq(dev); } #endif if (!dev) return 0; return dev->freq; } int rtlsdr_set_freq_correction(rtlsdr_dev_t *dev, int ppm) { int r = 0; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_freq_correction(ppm %d)\n", ppm); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_freq_correction(dev, ppm); } #endif if (!dev) return -1; if (dev->corr == ppm) return -2; dev->corr = ppm; r |= rtlsdr_set_sample_freq_correction(dev, ppm); /* read corrected clock value into e4k and r82xx structure */ if (rtlsdr_get_xtal_freq(dev, NULL, &dev->e4k_s.vco.fosc) || rtlsdr_get_xtal_freq(dev, NULL, &dev->r82xx_c.xtal)) return -3; if (dev->freq) /* retune to apply new correction value */ r |= rtlsdr_set_center_freq64(dev, dev->freq); return r; } int rtlsdr_get_freq_correction(rtlsdr_dev_t *dev) { #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_freq_correction(dev); } #endif if (!dev) return 0; return dev->corr; } enum rtlsdr_tuner rtlsdr_get_tuner_type(rtlsdr_dev_t *dev) { #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return (enum rtlsdr_tuner)rtlsdr_rpc_get_tuner_type(dev); } #endif if (!dev) return RTLSDR_TUNER_UNKNOWN; return dev->tuner_type; } static const int * get_tuner_gains(rtlsdr_dev_t *dev, int *pNum ) { /* all gain values are expressed in tenths of a dB */ static const int e4k_gains[] = { -10, 15, 40, 65, 90, 115, 140, 165, 190, 215, 240, 290, 340, 420 }; static const int fc0012_gains[] = { -99, -40, 71, 179, 192 }; static const int fc0013_gains[] = { -99, -73, -65, -63, -60, -58, -54, 58, 61, 63, 65, 67, 68, 70, 71, 179, 181, 182, 184, 186, 188, 191, 197 }; static const int fc2580_gains[] = { 0 /* no gain values */ }; static const int r82xx_gains[] = { 0, 9, 14, 27, 37, 77, 87, 125, 144, 157, 166, 197, 207, 229, 254, 280, 297, 328, 338, 364, 372, 386, 402, 421, 434, 439, 445, 480, 496 }; static const int unknown_gains[] = { 0 /* no gain values */ }; const int *ptr = NULL; int len = 0; #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { if ( !dev->softagc.rpcGainValues ) { dev->softagc.rpcNumGains = rtlsdr_rpc_get_tuner_gains(dev, NULL); if ( dev->softagc.rpcNumGains > 0 ) { dev->softagc.rpcGainValues = malloc( dev->softagc.rpcNumGains * sizeof(int) ); if ( dev->softagc.rpcGainValues ) rtlsdr_get_tuner_gains(dev, dev->softagc.rpcGainValues); } } if ( dev->softagc.rpcGainValues ) { *pNum = dev->softagc.rpcNumGains; return dev->softagc.rpcGainValues; } *pNum = 0; return NULL; } #endif switch (dev->tuner_type) { case RTLSDR_TUNER_E4000: ptr = e4k_gains; len = sizeof(e4k_gains); break; case RTLSDR_TUNER_FC0012: ptr = fc0012_gains; len = sizeof(fc0012_gains); break; case RTLSDR_TUNER_FC0013: ptr = fc0013_gains; len = sizeof(fc0013_gains); break; case RTLSDR_TUNER_FC2580: ptr = fc2580_gains; len = sizeof(fc2580_gains); break; case RTLSDR_TUNER_R820T: case RTLSDR_TUNER_R828D: ptr = r82xx_gains; len = sizeof(r82xx_gains); break; default: ptr = unknown_gains; len = sizeof(unknown_gains); break; } *pNum = len / sizeof(int); return ptr; } int rtlsdr_get_tuner_gains(rtlsdr_dev_t *dev, int *gains) { const int *ptr = NULL; int len = 0; if (!dev) return -1; ptr = get_tuner_gains(dev, &len ); len = len * sizeof(int); if (!gains) { /* no buffer provided, just return the count */ return len / sizeof(int); } else { if (len) memcpy(gains, ptr, len); return len / sizeof(int); } } int rtlsdr_set_and_get_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw, uint32_t *applied_bw, int apply_bw ) { int r = 0; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_and_get_tuner_bandwidth(bw %u Hz, apply_bw %d)\n", (unsigned)bw, apply_bw); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_and_get_tuner_bandwidth(dev, bw, applied_bw, apply_bw); } #endif *applied_bw = 0; /* unknown */ if (!dev || !dev->tuner) return -1; if(!apply_bw) { if (dev->tuner->set_bw) { r = dev->tuner->set_bw(dev, bw > 0 ? bw : dev->rate, applied_bw, apply_bw); } return r; } if (dev->tuner->set_bw) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_bw(dev, bw > 0 ? bw : dev->rate, applied_bw, apply_bw); rtlsdr_set_i2c_repeater(dev, 0); reactivate_softagc(dev, SOFTSTATE_RESET); if (r) return r; dev->bw = bw; } return r; } int rtlsdr_set_tuner_bandwidth(rtlsdr_dev_t *dev, uint32_t bw ) { uint32_t applied_bw = 0; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_tuner_bandwidth(bw %u Hz)\n", (unsigned)bw); #endif return rtlsdr_set_and_get_tuner_bandwidth(dev, bw, &applied_bw, 1 /* =apply_bw */ ); } int rtlsdr_set_tuner_band_center(rtlsdr_dev_t *dev, int32_t if_band_center_freq ) { int r = -1; if (!dev || !dev->tuner || !dev->tuner->set_bw_center) return -1; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_tuner_band_center(if_band_center_freq %d Hz)\n", (int)if_band_center_freq); #endif return dev->tuner->set_bw_center(dev, if_band_center_freq); } int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain) { int r = 0; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_tuner_gain(%d /10 dB)\n", gain); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_tuner_gain(dev, gain); } #endif if (!dev || !dev->tuner) return -1; if (dev->tuner->set_gain) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_gain((void *)dev, gain); rtlsdr_set_i2c_repeater(dev, 0); } return r; } int rtlsdr_set_tuner_gain_ext(rtlsdr_dev_t *dev, int lna_gain, int mixer_gain, int vga_gain) { int r = 0; if (!dev || ( dev->tuner_type != RTLSDR_TUNER_R820T && dev->tuner_type != RTLSDR_TUNER_R828D ) ) return -1; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_tuner_gain_ext(indexes 0 .. 15: lna %d, mixer %d, vga %d)\n", lna_gain, mixer_gain, vga_gain ); #endif if (dev->tuner->set_gain) { rtlsdr_set_i2c_repeater(dev, 1); r = r820t_set_gain_ext((void *)dev, lna_gain, mixer_gain, vga_gain); rtlsdr_set_i2c_repeater(dev, 0); } return r; } int rtlsdr_set_tuner_if_mode(rtlsdr_dev_t *dev, int if_mode) { int r = 0; if (!dev || ( dev->tuner_type != RTLSDR_TUNER_R820T && dev->tuner_type != RTLSDR_TUNER_R828D ) ) return -1; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_tuner_if_mode(if_mode %d)\n", if_mode); #endif if (dev->tuner->set_gain) { rtlsdr_set_i2c_repeater(dev, 1); r = r820t_set_if_mode((void *)dev, if_mode); rtlsdr_set_i2c_repeater(dev, 0); } return r; } int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev) { int rf_gain = 0; #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_tuner_gain(dev); } #endif if (!dev) return 0; if (dev->tuner_type == RTLSDR_TUNER_R820T) rf_gain = r82xx_get_rf_gain(&dev->r82xx_p); return rf_gain; } int rtlsdr_set_tuner_if_gain(rtlsdr_dev_t *dev, int stage, int gain) { int r = 0; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_tuner_if_gain(stage %d, gain %d /10 dB)\n", stage, gain ); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_tuner_if_gain(dev, stage, gain); } #endif if (!dev || !dev->tuner) return -1; if (dev->tuner->set_if_gain) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_if_gain(dev, stage, gain); rtlsdr_set_i2c_repeater(dev, 0); reactivate_softagc(dev, SOFTSTATE_RESET); } return r; } int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int mode) { int r = 0; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_tuner_gain_mode(mgc mode %d => agc %d)\n", mode, (mode ? 0 : 1) ); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_tuner_gain_mode(dev, mode); } #endif if (!dev || !dev->tuner) return -1; if (dev->tuner->set_gain_mode) { if ( dev->softagc.softAgcMode != SOFTAGC_OFF ) { mode = 1; /* use manual gain mode - for softagc */ if ( dev->softagc.softAgcMode && dev->softagc.verbose ) fprintf(stderr, "rtlsdr_set_tuner_gain_mode() - overridden for softagc!\n"); } rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_gain_mode((void *)dev, mode); rtlsdr_set_i2c_repeater(dev, 0); } return r; } int rtlsdr_set_tuner_sideband(rtlsdr_dev_t *dev, int sideband) { int r = 0, iffreq; rtlsdr_dev_t *devt = dev; if (!dev || !dev->tuner) return -1; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_tuner_sideband(sideband %d '%s')\n", sideband, (sideband ? "Upper" : "Lower") ); #endif if (dev->tuner->set_sideband) { if ( devt->verbose ) fprintf(stderr, "rtlsdr_set_tuner_sideband(%d): tuner.set_sideband() ..\n", sideband); rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_sideband((void *)dev, sideband); rtlsdr_set_i2c_repeater(dev, 0); if (r) { if ( devt->verbose ) fprintf(stderr, "rtlsdr_set_tuner_sideband(%d): tuner.set_sideband() returned error %d\n", sideband, r); return r; } devt->tuner_sideband = sideband; iffreq = (devt->tuner_sideband) /* -1 for USB; +1 for LSB */ ? ( devt->tuner_if_freq - devt->if_band_center_freq ) : ( devt->tuner_if_freq + devt->if_band_center_freq ); if ( devt->verbose ) { fprintf(stderr, "rtlsdr_set_tuner_sideband(%d): rtlsdr_set_if_freq(%d) ..\n", sideband, iffreq); fprintf(stderr, "rtlsdr_set_tuner_sideband(%d): iffreq = %d %c %d = %d\n", sideband, devt->tuner_if_freq, (devt->tuner_sideband ? '-' : '+'), devt->if_band_center_freq, iffreq); } r = rtlsdr_set_if_freq(devt, iffreq ); if (r) { if ( devt->verbose ) fprintf(stderr, "rtlsdr_set_tuner_sideband(%d): rtlsdr_set_if_freq(%d) returned error %d\n", sideband, iffreq, r); return r; } if (!devt->freq) return r; if (devt->verbose ) fprintf(stderr, "rtlsdr_set_tuner_sideband(%d): rtlsdr_set_center_freq64(%f MHz) ..\n", sideband, devt->freq * 1E-6); r = rtlsdr_set_center_freq64(devt, devt->freq); if (r && devt->verbose ) fprintf(stderr, "rtlsdr_set_tuner_sideband(%d): rtlsdr_set_center_freq(%f MHz) returned error %d\n", sideband, devt->freq * 1E-6, r); return r; } return r; } int rtlsdr_set_tuner_i2c_register(rtlsdr_dev_t *dev, unsigned i2c_register, unsigned mask /* byte */, unsigned data /* byte */ ) { int r = 0; #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { /* TODO */ return -1; } #endif if (!dev || !dev->tuner) return -1; if (dev->tuner->set_i2c_register) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_i2c_register((void *)dev, i2c_register, data, mask); rtlsdr_set_i2c_repeater(dev, 0); } return r; } /* -cs- */ int rtlsdr_get_tuner_i2c_register(rtlsdr_dev_t *dev, unsigned char* data, int len) { int r = 0; if (!dev || !dev->tuner) return -1; if (dev->tuner->get_i2c_register) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->get_i2c_reg_array((void *)dev, data, len); rtlsdr_set_i2c_repeater(dev, 0); } return r; } int rtlsdr_set_tuner_i2c_override(rtlsdr_dev_t *dev, unsigned i2c_register, unsigned mask /* byte */, unsigned data /* byte */ ) { int r = 0; #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { /* TODO */ return -1; } #endif if (!dev || !dev->tuner) return -1; if (dev->tuner->set_i2c_override) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->set_i2c_override((void *)dev, i2c_register, data, mask); rtlsdr_set_i2c_repeater(dev, 0); } return r; } int rtlsdr_set_sample_rate(rtlsdr_dev_t *dev, uint32_t samp_rate) { int r = 0; uint16_t tmp; uint32_t rsamp_ratio, real_rsamp_ratio; double real_rate; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_sample_rate(samp_rate %u)\n", (unsigned)samp_rate); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_sample_rate(dev, samp_rate); } #endif if (!dev) return -1; /* check if the rate is supported by the resampler */ if ((samp_rate <= 225000) || (samp_rate > 3200000) || ((samp_rate > 300000) && (samp_rate <= 900000))) { fprintf(stderr, "Invalid sample rate: %u Hz\n", samp_rate); return -EINVAL; } rsamp_ratio = (dev->rtl_xtal * TWO_POW(22)) / samp_rate; rsamp_ratio &= 0x0ffffffc; real_rsamp_ratio = rsamp_ratio | ((rsamp_ratio & 0x08000000) << 1); real_rate = (dev->rtl_xtal * TWO_POW(22)) / real_rsamp_ratio; if ( ((double)samp_rate) != real_rate ) fprintf(stderr, "Exact sample rate is: %f Hz\n", real_rate); dev->rate = (uint32_t)real_rate; if (dev->tuner && dev->tuner->set_bw) { uint32_t applied_bw = 0; rtlsdr_set_i2c_repeater(dev, 1); dev->tuner->set_bw(dev, dev->bw > 0 ? dev->bw : dev->rate, &applied_bw, 1); rtlsdr_set_i2c_repeater(dev, 0); } tmp = (rsamp_ratio >> 16); r |= rtlsdr_demod_write_reg(dev, 1, 0x9f, tmp, 2); tmp = rsamp_ratio & 0xffff; r |= rtlsdr_demod_write_reg(dev, 1, 0xa1, tmp, 2); r |= rtlsdr_set_sample_freq_correction(dev, dev->corr); /* reset demod (bit 3, soft_rst) */ r |= rtlsdr_demod_write_reg(dev, 1, 0x01, 0x14, 1); r |= rtlsdr_demod_write_reg(dev, 1, 0x01, 0x10, 1); /* recalculate offset frequency if offset tuning is enabled */ if (dev->offs_freq) rtlsdr_set_offset_tuning(dev, 1); if ( reactivate_softagc(dev, SOFTSTATE_RESET) ) { dev->softagc.deadTimeSps = 0; dev->softagc.scanTimeSps = 0; } return r; } uint32_t rtlsdr_get_sample_rate(rtlsdr_dev_t *dev) { #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_sample_rate(dev); } #endif if (!dev) return 0; return dev->rate; } int rtlsdr_set_testmode(rtlsdr_dev_t *dev, int on) { #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_testmode(on %d)\n", on); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_testmode(dev, on); } #endif if (!dev) return -1; return rtlsdr_demod_write_reg(dev, 0, 0x19, on ? 0x03 : 0x05, 1); } int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on) { #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_agc_mode(on %d for digital AGC in RTL2832)\n", on); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_agc_mode(dev, on); } #endif if (!dev) return -1; return rtlsdr_demod_write_reg(dev, 0, 0x19, on ? 0x25 : 0x05, 1); } int rtlsdr_set_direct_sampling(rtlsdr_dev_t *dev, int on) { int r = 0; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_direct_sampling(on %d - 1 = I-ADC, 2 = Q-ADC)\n", on); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_direct_sampling(dev, on); } #endif if (!dev) return -1; if (on) { if (dev->tuner && dev->tuner->exit) { rtlsdr_set_i2c_repeater(dev, 1); r = dev->tuner->exit(dev); rtlsdr_set_i2c_repeater(dev, 0); } /* disable Zero-IF mode */ r |= rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1a, 1); /* disable spectrum inversion */ r |= rtlsdr_demod_write_reg(dev, 1, 0x15, 0x00, 1); /* only enable In-phase ADC input */ r |= rtlsdr_demod_write_reg(dev, 0, 0x08, 0x4d, 1); /* swap I and Q ADC, this allows to select between two inputs */ r |= rtlsdr_demod_write_reg(dev, 0, 0x06, (on > 1) ? 0x90 : 0x80, 1); fprintf(stderr, "Enabled direct sampling mode, input %i\n", on); dev->direct_sampling = on; } else { if (dev->tuner && dev->tuner->init) { rtlsdr_set_i2c_repeater(dev, 1); r |= dev->tuner->init(dev); rtlsdr_set_i2c_repeater(dev, 0); } if ((dev->tuner_type == RTLSDR_TUNER_R820T) || (dev->tuner_type == RTLSDR_TUNER_R828D)) { r |= rtlsdr_set_if_freq(dev, R82XX_IF_FREQ); /* enable spectrum inversion */ r |= rtlsdr_demod_write_reg(dev, 1, 0x15, 0x01, 1); } else { r |= rtlsdr_set_if_freq(dev, 0); /* enable In-phase + Quadrature ADC input */ r |= rtlsdr_demod_write_reg(dev, 0, 0x08, 0xcd, 1); /* Enable Zero-IF mode */ r |= rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1b, 1); } /* opt_adc_iq = 0, default ADC_I/ADC_Q datapath */ r |= rtlsdr_demod_write_reg(dev, 0, 0x06, 0x80, 1); fprintf(stderr, "Disabled direct sampling mode\n"); dev->direct_sampling = 0; } r |= rtlsdr_set_center_freq64(dev, dev->freq); return r; } int rtlsdr_get_direct_sampling(rtlsdr_dev_t *dev) { #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_direct_sampling(dev); } #endif if (!dev) return -1; return dev->direct_sampling; } int rtlsdr_set_ds_mode(rtlsdr_dev_t *dev, enum rtlsdr_ds_mode mode, uint32_t freq_threshold) { uint64_t center_freq; if (!dev) return -1; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_ds_mode(mode %d, freq threshold %u Hz)\n", (int)mode, (unsigned)freq_threshold); #endif center_freq = rtlsdr_get_center_freq64(dev); if ( !center_freq ) return -2; if (!freq_threshold) { switch(dev->tuner_type) { default: case RTLSDR_TUNER_UNKNOWN: freq_threshold = 28800000; break; /* no idea!!! */ case RTLSDR_TUNER_E4000: freq_threshold = 50*1000000; break; /* E4K_FLO_MIN_MHZ */ case RTLSDR_TUNER_FC0012: freq_threshold = 28800000; break; /* no idea!!! */ case RTLSDR_TUNER_FC0013: freq_threshold = 28800000; break; /* no idea!!! */ case RTLSDR_TUNER_FC2580: freq_threshold = 28800000; break; /* no idea!!! */ case RTLSDR_TUNER_R820T: freq_threshold = 24000000; break; /* ~ */ case RTLSDR_TUNER_R828D: freq_threshold = 28800000; break; /* no idea!!! */ } } dev->direct_sampling_mode = mode; dev->direct_sampling_threshold = freq_threshold; if (mode <= RTLSDR_DS_Q) rtlsdr_set_direct_sampling(dev, mode); return rtlsdr_set_center_freq64(dev, center_freq); } static int rtlsdr_update_ds(rtlsdr_dev_t *dev, uint64_t freq) { int new_ds = 0; int curr_ds = rtlsdr_get_direct_sampling(dev); if ( curr_ds < 0 ) return -1; switch (dev->direct_sampling_mode) { default: case RTLSDR_DS_IQ: break; case RTLSDR_DS_I: new_ds = 1; break; case RTLSDR_DS_Q: new_ds = 2; break; case RTLSDR_DS_I_BELOW: new_ds = (freq < dev->direct_sampling_threshold) ? 1 : 0; break; case RTLSDR_DS_Q_BELOW: new_ds = (freq < dev->direct_sampling_threshold) ? 2 : 0; break; } //if ( dev->verbose ) // fprintf(stderr, "rtlsdr_update_ds(%u Hz) --> ds = %d for mode %s\n", // freq, new_ds, dsmode_str[dev->direct_sampling_mode] ); if ( curr_ds != new_ds ) return rtlsdr_set_direct_sampling(dev, new_ds); return 0; } int rtlsdr_set_offset_tuning(rtlsdr_dev_t *dev, int on) { int r = 0; int bw; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_offset_tuning(on %d)\n", on); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_set_offset_tuning(dev, on); } #endif if (!dev) return -1; if ((dev->tuner_type == RTLSDR_TUNER_R820T) || (dev->tuner_type == RTLSDR_TUNER_R828D)) return -2; if (dev->direct_sampling) return -3; /* based on keenerds 1/f noise measurements */ dev->offs_freq = on ? ((dev->rate / 2) * 170 / 100) : 0; r |= rtlsdr_set_if_freq(dev, dev->offs_freq); if (dev->tuner && dev->tuner->set_bw) { uint32_t applied_bw = 0; rtlsdr_set_i2c_repeater(dev, 1); if (on) { bw = 2 * dev->offs_freq; } else if (dev->bw > 0) { bw = dev->bw; } else { bw = dev->rate; } dev->tuner->set_bw(dev, bw, &applied_bw, 1); rtlsdr_set_i2c_repeater(dev, 0); } if (dev->freq > dev->offs_freq) r |= rtlsdr_set_center_freq64(dev, dev->freq); return r; } int rtlsdr_get_offset_tuning(rtlsdr_dev_t *dev) { #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_offset_tuning(dev); } #endif if (!dev) return -1; return (dev->offs_freq) ? 1 : 0; } int rtlsdr_set_dithering(rtlsdr_dev_t *dev, int dither) { if (dev->tuner_type == RTLSDR_TUNER_R820T) { return r82xx_set_dither(&dev->r82xx_p, dither); } return 1; } static rtlsdr_dongle_t *find_known_device(uint16_t vid, uint16_t pid) { unsigned int i; rtlsdr_dongle_t *device = NULL; for (i = 0; i < sizeof(known_devices)/sizeof(rtlsdr_dongle_t); i++ ) { if (known_devices[i].vid == vid && known_devices[i].pid == pid) { device = &known_devices[i]; break; } } return device; } uint32_t rtlsdr_get_device_count(void) { int i,r; libusb_context *ctx; libusb_device **list; uint32_t device_count = 0; struct libusb_device_descriptor dd; ssize_t cnt; #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_device_count(); } #endif r = libusb_init(&ctx); if(r < 0) return 0; cnt = libusb_get_device_list(ctx, &list); for (i = 0; i < cnt; i++) { libusb_get_device_descriptor(list[i], &dd); if (find_known_device(dd.idVendor, dd.idProduct)) device_count++; } libusb_free_device_list(list, 1); libusb_exit(ctx); return device_count; } const char *rtlsdr_get_device_name(uint32_t index) { int i,r; libusb_context *ctx; libusb_device **list; struct libusb_device_descriptor dd; rtlsdr_dongle_t *device = NULL; uint32_t device_count = 0; ssize_t cnt; #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_device_name(index); } #endif r = libusb_init(&ctx); if(r < 0) return ""; cnt = libusb_get_device_list(ctx, &list); for (i = 0; i < cnt; i++) { libusb_get_device_descriptor(list[i], &dd); device = find_known_device(dd.idVendor, dd.idProduct); if (device) { device_count++; if (index == device_count - 1) break; } } libusb_free_device_list(list, 1); libusb_exit(ctx); if (device) return device->name; else return ""; } int rtlsdr_get_device_usb_strings(uint32_t index, char *manufact, char *product, char *serial) { int r = -2; int i; libusb_context *ctx; libusb_device **list; struct libusb_device_descriptor dd; rtlsdr_dongle_t *device = NULL; rtlsdr_dev_t devt; uint32_t device_count = 0; ssize_t cnt; #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_device_usb_strings (index, manufact, product, serial); } #endif r = libusb_init(&ctx); if(r < 0) return r; cnt = libusb_get_device_list(ctx, &list); for (i = 0; i < cnt; i++) { libusb_get_device_descriptor(list[i], &dd); device = find_known_device(dd.idVendor, dd.idProduct); if (device) { device_count++; if (index == device_count - 1) { r = libusb_open(list[i], &devt.devh); if (!r) { r = rtlsdr_get_usb_strings(&devt, manufact, product, serial); libusb_close(devt.devh); } break; } } } libusb_free_device_list(list, 1); libusb_exit(ctx); return r; } int rtlsdr_get_index_by_serial(const char *serial) { int i, cnt, r; char str[256]; #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_get_index_by_serial(serial); } #endif if (!serial) return -1; cnt = rtlsdr_get_device_count(); if (!cnt) return -2; for (i = 0; i < cnt; i++) { r = rtlsdr_get_device_usb_strings(i, NULL, NULL, str); if (!r && !strcmp(serial, str)) return i; } return -3; } /* UDP controller server */ #ifdef WITH_UDP_SERVER static int parseNum(const char * pacNum) { int numBase = 10; /* assume decimal */ int sgn = 1; /* sign: +/- 1 */ int val = 0; const char * pac = pacNum + 1; if ( !pacNum || !pacNum[0] ) return 0; if ( pacNum[0] == 'd' ) /* decimal system */ numBase = 10; else if ( pacNum[0] == 'x' ) /* hexadecimal system */ numBase = 16; else if ( pacNum[0] == 'b' ) /* binary system */ numBase = 2; else pac = pacNum; if ( *pac == '-' ) { sgn = -1; ++pac; } while ( *pac ) { int digitValue = ( '0' <= *pac && *pac <= '9' ) ? (*pac - '0') : ( 'A' <= *pac && *pac <= 'F' ) ? (*pac + 10 - 'A') : (*pac + 10 - 'a'); if ( digitValue >= 0 && digitValue < numBase ) { val = val * numBase + digitValue; ++pac; continue; } else if ( *pac == '\'' || *pac == '.' || *pac == '_' ) { /* ignore some delimiter chars */ ++pac; continue; } else break; } return val * sgn; } #endif static double parseFreq(char *s) /* standard suffixes */ { char last; int len; double suff = 1.0; len = strlen(s); /* allow formatting spaces from .csv command file */ while ( len > 1 && isspace(s[len-1]) ) --len; last = s[len-1]; s[len-1] = '\0'; switch (last) { case 'g': case 'G': suff *= 1e3; /* fall-through */ case 'm': case 'M': suff *= 1e3; /* fall-through */ case 'k': case 'K': suff *= 1e3; suff *= atof(s); s[len-1] = last; return suff; } s[len-1] = last; return atof(s); } /* UDP controller server */ #ifdef WITH_UDP_SERVER static const char * formatInHex(char * buf, int v, int num_digits) { static const char tab[] = "0123456789ABCDEF"; int nibbleVal, nibbleNo, off = 0; buf[off++] = 'x'; for ( nibbleNo = num_digits -1; nibbleNo >= 0; --nibbleNo ) { if ( (nibbleNo % 4) == 3 && nibbleNo != num_digits -1 ) buf[off++] = '\''; nibbleVal = ( ((uint32_t)v) >> (nibbleNo * 4) ) & 0x0f; buf[off++] = tab[nibbleVal]; } buf[off++] = 0; return buf; } static const char * formatInBin(char * buf, int v, int num_digits) { static const char tab[] = "01"; int bitVal, bitNo, off = 0; buf[off++] = 'b'; for ( bitNo = num_digits -1; bitNo >= 0; --bitNo ) { if ( (bitNo % 4) == 3 && bitNo != num_digits -1 ) buf[off++] = '\''; bitVal = ( ((uint32_t)v) >> bitNo ) & 1; buf[off++] = tab[bitVal]; } buf[off++] = 0; return buf; } static int parse(char *message, rtlsdr_dev_t *dev) { char binBufA[64], binBufB[64]; char hexBufA[16], hexBufB[16]; char *str1, *token, *saveptr; char response[UDP_TX_BUFLEN]; double freqVal = -1; int comm = 0, parsedVal = 0, iVal = 0; int val = 0; uint32_t applied_bw = 0; uint8_t mask = 0xff, reg=0; uint32_t freq; uint64_t freq64; int32_t bandcenter; int sideband; int retCode; str1 = message; memset(response,'\0', UDP_TX_BUFLEN); str1[100] = 0; str1[strlen(str1)-1] = 0; /* first token == command */ token = strtok_r(str1, " \t", &saveptr); if ( !token ) { sprintf(response,"?\n"); sendto(dev->udpS, response, strlen(response), 0, (struct sockaddr*) &dev->si_other, dev->slen); return 0; } /* commands: * g # get tuner i2c register * s [] # set tuner i2c register once * S [] # set tuner i2c register permanent * * i # set tuner IF frequency once. value in [ 0 .. 28 800 000 ] or < 0 for reset * I # set tuner IF frequency permanent * * f # set rtl center frequency * b # set tuner bandwidth * c # set tuner bandwidth center in output. value in [ -1 600 000 .. 1 600 000 ] * v # set tuner sideband inversion * * a # 0: VGA = auto * # g in -5000 .. +5000: VGA = g / 10 dB * # 10000+x: VGA idx = x * m # set tuner gain * * M # 0 : tuner agc off; digital rtl agc off * # 1 : tuner agc on ; digital rtl agc off * # 2 : tuner agc off; digital rtl agc on * # 3 : tuner agc on ; digital rtl agc on * * * ********** not implemented yet * * e # set extended tuner gain - for R820 tuner * RTLSDR_API int rtlsdr_set_tuner_gain_mode(rtlsdr_dev_t *dev, int manual); * RTLSDR_API int rtlsdr_set_agc_mode(rtlsdr_dev_t *dev, int on); * * RTLSDR_API int rtlsdr_set_tuner_gain_ext(rtlsdr_dev_t *dev, int lna_gain, int mixer_gain, int vga_gain); * RTLSDR_API int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev); * RTLSDR_API int rtlsdr_set_tuner_gain(rtlsdr_dev_t *dev, int gain); * */ /* commands with register args: 64 | x */ if (!strcmp(token, "g")) comm = 64 + 1; if (!strcmp(token, "s")) comm = 64 + 2; if (!strcmp(token, "S")) comm = 64 + 3; if (!strcmp(token, "i")) comm = 128 + 1; if (!strcmp(token, "I")) comm = 128 + 2; if (!strcmp(token, "f")) comm = 256 + 1; if (!strcmp(token, "b")) comm = 256 + 2; if (!strcmp(token, "c")) comm = 256 + 3; if (!strcmp(token, "v")) comm = 256 + 4; if (!strcmp(token, "a")) comm = 512 + 1; if (!strcmp(token, "m")) comm = 512 + 2; if (!strcmp(token, "M")) comm = 512 + 3; if (!strcmp(token, "h")) comm = 1024; if ( comm & 64 ) { token = strtok_r(NULL, " \t", &saveptr); parsedVal = parseNum(token); if ( (!token) || (comm >= (64+2) && parsedVal < 5) || (parsedVal > 32) ) { sprintf(response,"?\n"); if (sendto(dev->udpS, response, strlen(response), 0, (struct sockaddr*) &dev->si_other, dev->slen) == SOCKET_ERROR) { /* perror("send"); */ return -1; } return 0; } reg = (uint8_t)parsedVal; /* 1st arg: register address */ if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, "parsed register %d from token '%s'\n", reg, token); if (token) token = strtok_r(NULL, " \t", &saveptr); if ( (!token) && (comm >= 64+2)) { /* set requires additional parameter: the value */ sprintf(response,"?\n"); if (sendto(dev->udpS, response, strlen(response), 0, (struct sockaddr*) &dev->si_other, dev->slen) == SOCKET_ERROR) { /* perror("send"); */ return -1; } return 0; } else if (comm >= 64+2) { parsedVal = parseNum(token); /* set: 2nd arg: value */ iVal = parsedVal; if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, "parsed value %d = %03X from token '%s'\n", iVal, iVal, token); } if (token) token = strtok_r(NULL, " \t", &saveptr); if (!token) { mask = 0xff; /* default mask */ } else { parsedVal = parseNum(token); /* set: 3rd optional arg: mask */ mask = (uint8_t)( parsedVal & 0xff ); if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, "parsed mask %d = %02X from token '%s'\n", parsedVal, parsedVal, token); } if (comm == 64 + 1) { val = dev->tuner->get_i2c_register(dev, reg); sprintf(response,"! %d = %s = %s\n", val , formatInHex(hexBufA, val, 2) , formatInBin(binBufA, val, 8) ); if ( dev->verbose && PRINT_UDP_SRV_MSGS ) { fprintf(stderr, "parsed 'get i2c register %d = x%02X'\n", reg, reg); fprintf(stderr, "\tresponse: %s\n", response); } val = sendto(dev->udpS, response, strlen(response), 0, (struct sockaddr*) &dev->si_other, dev->slen); if (val<0) { /* printf("error sending\n"); */ return -1; } } else if (comm == 64 +2 || comm == 64 +3 ) { if ( dev->verbose && PRINT_UDP_SRV_MSGS ) { fprintf(stderr, "parsed 'set i2c register %s %d = x%02X value %d = %s = %s with mask %s = %s'\n" , ( comm == (64 +3) ? (iVal > 255 ? "override clear " : "override ") : "" ) , reg, reg , val, formatInHex(hexBufA, iVal, 3), formatInBin(binBufA, iVal, 12) , formatInHex(hexBufB, (int)mask, 2), formatInBin(binBufB, (int)mask, 8) ); fprintf(stderr, "\tresponse: %s\n", response); } if ( dev->tuner->set_i2c_register && dev->tuner->set_i2c_override ) { rtlsdr_set_i2c_repeater(dev, 1); if (comm == 64 +2) { if (PRINT_UDP_SRV_MSGS) fprintf(stderr, "calling tuner->set_i2c_register( reg %d, value %02X, mask %02X)\n", reg, iVal, mask); val = dev->tuner->set_i2c_register(dev, reg, iVal, mask); } else { if (PRINT_UDP_SRV_MSGS) fprintf(stderr, "calling tuner->set_i2c_override( reg %d, value %02X, mask %02X)\n", reg, iVal, mask); val = dev->tuner->set_i2c_override(dev, reg, iVal, mask); } rtlsdr_set_i2c_repeater(dev, 0); } sprintf(response,"! %d\n", (int)val); /* printf("%d %d %d\n", reg, val, mask); */ val = sendto(dev->udpS, response, strlen(response), 0, (struct sockaddr*) &dev->si_other, dev->slen); if ( val < 0 ) { /* printf("error sending\n"); */ return -1; } } else { sprintf(response,"?\n"); if ( dev->verbose && PRINT_UDP_SRV_MSGS ) { fprintf(stderr, "parsed unknown command!\n"); fprintf(stderr, "\tresponse: %s\n", response); } sendto(dev->udpS, response, strlen(response), 0, (struct sockaddr*) &dev->si_other, dev->slen); } } else if ( comm & 128 ) { token = strtok_r(NULL, " \t", &saveptr); freqVal = parseFreq(token); if ( freqVal < 0 ) { dev->override_if_freq = 0; dev->override_if_flag = 0; } else { dev->override_if_freq = (int)freqVal; dev->override_if_flag = ( comm == (128 + 1) ) ? 1 : 2; } /* set last bandwidth .. which also has to set the IF frequency */ rtlsdr_set_and_get_tuner_bandwidth(dev, dev->bw, &applied_bw, 1 ); rtlsdr_set_center_freq64(dev, dev->freq); } else if ( comm & 256 ) { token = strtok_r(NULL, " \t", &saveptr); freqVal = parseFreq(token); retCode = -1; switch (comm & 63) { case 1: /* frequency */ freq64 = (uint64_t)(freqVal + 0.5); if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, "parsed RF frequency = %f MHz from token '%s'\n", freq64 * 1E-6, token); retCode = rtlsdr_set_center_freq64(dev, freq64); if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, " rtlsdr_set_center_freq() returned %d\n", retCode); break; case 2: /* bandwidth */ freq = (uint32_t)freqVal; if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, "parsed bandwidth = %u Hz from token '%s'\n", (unsigned)freq, token); retCode = rtlsdr_set_and_get_tuner_bandwidth(dev, freq, &applied_bw, 1); if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, " rtlsdr_set_and_get_tuner_bandwidth() returned %d and bw %u\n", retCode, applied_bw); break; case 3: /* band center */ bandcenter = (int32_t)freqVal; if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, "parsed bandcenter = %d Hz from token '%s'\n", (int)bandcenter, token); retCode = rtlsdr_set_tuner_band_center(dev, bandcenter); if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, " rtlsdr_set_tuner_band_center() returned %d\n", retCode); break; case 4: /* sideband */ sideband = (int32_t)freqVal; if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, "parsed sideband = %d = %s from token '%s'\n", sideband, (sideband ? "USB" : "LSB"), token); retCode = rtlsdr_set_tuner_sideband(dev, sideband); if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, " rtlsdr_set_tuner_sideband() returned %d\n", retCode); break; default: break; } } else if ( comm & 512 ) { token = strtok_r(NULL, " \t", &saveptr); parsedVal = parseNum(token); if ( !token ) { sprintf(response,"?\n"); if (sendto(dev->udpS, response, strlen(response), 0, (struct sockaddr*) &dev->si_other, dev->slen) == SOCKET_ERROR) { /* perror("send"); */ return -1; } return 0; } switch (comm & 63) { case 1: /* agc variant */ if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, "parsed if mode %d from token '%s'\n", parsedVal, token); retCode = rtlsdr_set_tuner_if_mode(dev, parsedVal); if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, " rtlsdr_set_tuner_if_mode() returned %d\n", retCode); break; case 2: /* manual gain */ if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, "parsed tuner gain %d tenth dB from token '%s'\n", parsedVal, token); retCode = rtlsdr_set_tuner_gain(dev, parsedVal); if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, " rtlsdr_set_tuner_gain() returned %d\n", retCode); break; case 3: /* gainMode */ if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, "parsed gainMode %d with tuner AGC '%s' and RTL AGC '%s' from token '%s'\n" , parsedVal , ( (parsedVal & 1) == 1 ) ? "on" : "off" , ( (parsedVal & 2) == 2 ) ? "on" : "off" , token ); retCode = rtlsdr_set_tuner_gain_mode(dev, ( (parsedVal & 1) == 0 ) ? 1 : 0 ); if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, " rtlsdr_set_tuner_gain_mode() returned %d\n", retCode); retCode = rtlsdr_set_agc_mode(dev, ( (parsedVal & 2) == 2 ) ? 1 : 0 ); if ( dev->verbose && PRINT_UDP_SRV_MSGS ) fprintf(stderr, " rtlsdr_set_agc_mode() returned %d\n", retCode); break; } } else if ( comm & 1024 ) { sprintf(response, "g # get content of I2C ..\n" "s [] # set conten\n" "S [] # set content - keeping value in future\n" "i # set IF frequency [0 .. 28'800'000]\n" "f # set center frequency\n" "b # set tuner bandwidth\n" "c # set tuner bw center in output [-1'600'000 .. 1'600'000]\n" "v # set tuner sideband: 0 for LSB, 1 for USB\n" "a # set VGA: 0 for auto; in tenth dB or 10000+idx\n" "m # set tuner gain\n" "M # 0 .. 3: digital rtl agc (0..1) * 2 + tuner agc (0..1)\n" ); sendto(dev->udpS, response, strlen(response), 0, (struct sockaddr*) &dev->si_other, dev->slen); if (PRINT_UDP_SRV_MSGS) fprintf(stderr, "udp server command help:\n%s\n", response); } { sprintf(response,"?\n"); sendto(dev->udpS, response, strlen(response), 0, (struct sockaddr*) &dev->si_other, dev->slen); } return 0; } void * srv_server(void *vdev) { int ret; rtlsdr_dev_t * dev = (rtlsdr_dev_t *)vdev; dev->slen = sizeof(dev->si_other); #ifdef _WIN32 /* Initialise winsock */ if (WSAStartup(MAKEWORD(2,2),&dev->wsa) != 0) { fprintf(stderr, "Failed to initialize WinSock; continue without UDP server. Error Code : %d\n",LAST_SOCK_ERROR()); return NULL; } #endif /* Create a socket */ dev->udpS = socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP ); if(dev->udpS == INVALID_SOCKET) { fprintf(stderr, "Could not create socket for UDP server : %d\n" , LAST_SOCK_ERROR()); return NULL; } memset(&dev->server,0,sizeof(dev->server)); dev->server.sin_family = AF_INET; dev->server.sin_addr.s_addr = INADDR_ANY; dev->server.sin_port = htons( dev->udpPortNo ); ret = bind(dev->udpS, (struct sockaddr *)&dev->server , sizeof(dev->server)); if(ret == SOCKET_ERROR) { fprintf(stderr, "Bind failed for UDP server with error code : %d\n" , LAST_SOCK_ERROR()); closesocket(dev->udpS); return NULL; } /* keep listening for data */ while(1) { /* clear the buffer by filling null, it might have previously received data */ memset(dev->buf,'\0', UDP_TX_BUFLEN); /* try to receive some data, this is a blocking call */ dev->recv_len = recvfrom(dev->udpS, dev->buf, UDP_TX_BUFLEN-1, 0, (struct sockaddr *) &dev->si_other, &dev->slen); if (dev->recv_len == SOCKET_ERROR) { fprintf(stderr, "recvfrom() for UDP server failed with error code %d. Shutting down UDP server.\n" , LAST_SOCK_ERROR()); break; } if ( dev->verbose ) fprintf(stderr, "received udp: %s\n", dev->buf); parse( dev->buf, dev ); /* print details of the client/peer and the data received */ /* printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port)); */ /* printf("Data: %s\n" , buf); */ } closesocket(dev->udpS); #ifdef _WIN32 /* application might still use WinSock! */ /* WSACleanup(); */ #endif return NULL; } #endif /* Returns true if the manufact_check and product_check strings match what is in the dongles EEPROM */ int rtlsdr_check_dongle_model(void *dev, char *manufact_check, char *product_check) { if ((strcmp(((rtlsdr_dev_t *)dev)->manufact, manufact_check) == 0 && strcmp(((rtlsdr_dev_t *)dev)->product, product_check) == 0)) { return 1; } return 0; } int rtlsdr_open(rtlsdr_dev_t **out_dev, uint32_t index) { int r; int i; libusb_device **list; rtlsdr_dev_t *dev = NULL; libusb_device *device = NULL; uint32_t device_count = 0; struct libusb_device_descriptor dd; uint8_t reg; ssize_t cnt; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_open(%u)\n", (unsigned)index); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_open((void**)out_dev, index); } #endif dev = malloc(sizeof(rtlsdr_dev_t)); if (NULL == dev) return -ENOMEM; memset(dev, 0, sizeof(rtlsdr_dev_t)); memcpy(dev->fir, fir_default, sizeof(fir_default)); r = libusb_init(&dev->ctx); if(r < 0){ free(dev); return -1; } pthread_mutexattr_init(&dev->cs_mutex_attr); pthread_mutexattr_settype(&dev->cs_mutex_attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&dev->cs_mutex, &dev->cs_mutex_attr); dev->rtl_vga_control = 0; dev->biast_gpio_pin_no = 0; dev->gpio_state_known = 0; dev->gpio_state = 0; dev->called_set_opt = 0; dev->r82xx_c.harmonic = 0; /* fprintf(stderr, "\n*********************************\ninit/overwrite tuner VCO settings\n"); */ dev->r82xx_c.vco_curr_min = 0xff; /* VCO min/max current for R18/0x12 bits [7:5] in 0 .. 7. use 0xff for default */ dev->r82xx_c.vco_curr_max = 0xff; /* value is inverted: programmed is 7-value, that 0 is lowest current */ dev->r82xx_c.vco_algo = 0x00; dev->r82xx_c.verbose = 0; /* dev->softagc.command_thread; */ dev->softagc.agcState = SOFTSTATE_OFF; dev->softagc.softAgcMode = SOFTAGC_OFF; /* SOFTAGC_FREQ_CHANGE SOFTAGC_ATTEN SOFTAGC_ALL */ dev->softagc.verbose = 0; dev->softagc.scanTimeMs = 100; /* parameter: default: 100 ms */ dev->softagc.deadTimeMs = 1; /* parameter: default: 1 ms */ dev->softagc.scanTimeSps = 0; dev->softagc.deadTimeSps = 0; dev->softagc.rpcNumGains = 0; dev->softagc.rpcGainValues = NULL; /* UDP controller server */ #ifdef WITH_UDP_SERVER dev->udpPortNo = 0; /* default port 32323 .. but deactivated - by default */ dev->override_if_freq = 0; dev->override_if_flag = 0; #endif dev->dev_num = index; dev->dev_lost = 1; cnt = libusb_get_device_list(dev->ctx, &list); for (i = 0; i < cnt; i++) { device = list[i]; libusb_get_device_descriptor(list[i], &dd); if (find_known_device(dd.idVendor, dd.idProduct)) { device_count++; } if (index == device_count - 1) break; device = NULL; } if (!device) { r = -1; goto err; } r = libusb_open(device, &dev->devh); if (r < 0) { libusb_free_device_list(list, 1); fprintf(stderr, "usb_open error %d\n", r); if(r == LIBUSB_ERROR_ACCESS) fprintf(stderr, "Please fix the device permissions, e.g. " "by installing the udev rules file rtl-sdr.rules\n"); goto err; } libusb_free_device_list(list, 1); if (libusb_kernel_driver_active(dev->devh, 0) == 1) { dev->driver_active = 1; #ifdef DETACH_KERNEL_DRIVER if (!libusb_detach_kernel_driver(dev->devh, 0)) { fprintf(stderr, "Detached kernel driver\n"); } else { fprintf(stderr, "Detaching kernel driver failed!"); goto err; } #else fprintf(stderr, "\nKernel driver is active, or device is " "claimed by second instance of librtlsdr." "\nIn the first case, please either detach" " or blacklist the kernel module\n" "(dvb_usb_rtl28xxu), or enable automatic" " detaching at compile time.\n\n"); #endif } r = libusb_claim_interface(dev->devh, 0); if (r < 0) { fprintf(stderr, "usb_claim_interface error %d\n", r); goto err; } dev->rtl_xtal = DEF_RTL_XTAL_FREQ; /* perform a dummy write, if it fails, reset the device */ if (rtlsdr_write_reg(dev, USBB, USB_SYSCTL, 0x09, 1) < 0) { fprintf(stderr, "Resetting device...\n"); libusb_reset_device(dev->devh); } rtlsdr_init_baseband(dev); dev->dev_lost = 0; /* Get device manufacturer and product id */ r = rtlsdr_get_usb_strings(dev, dev->manufact, dev->product, NULL); /* Probe tuners */ rtlsdr_set_i2c_repeater(dev, 1); /* C++ style RAII would be fine! */ reg = rtlsdr_i2c_read_reg(dev, E4K_I2C_ADDR, E4K_CHECK_ADDR); if (reg == E4K_CHECK_VAL) { fprintf(stderr, "Found Elonics E4000 tuner\n"); dev->tuner_type = RTLSDR_TUNER_E4000; goto found; } reg = rtlsdr_i2c_read_reg(dev, FC0013_I2C_ADDR, FC0013_CHECK_ADDR); if (reg == FC0013_CHECK_VAL) { fprintf(stderr, "Found Fitipower FC0013 tuner\n"); dev->tuner_type = RTLSDR_TUNER_FC0013; goto found; } reg = rtlsdr_i2c_read_reg(dev, R820T_I2C_ADDR, R82XX_CHECK_ADDR); if (reg == R82XX_CHECK_VAL) { fprintf(stderr, "Found Rafael Micro R820T/2 tuner\n"); dev->tuner_type = RTLSDR_TUNER_R820T; goto found; } reg = rtlsdr_i2c_read_reg(dev, R828D_I2C_ADDR, R82XX_CHECK_ADDR); if (reg == R82XX_CHECK_VAL) { fprintf(stderr, "Found Rafael Micro R828D tuner\n"); if (rtlsdr_check_dongle_model(dev, "RTLSDRBlog", "Blog V4")) { fprintf(stderr, "RTL-SDR Blog V4 Detected\n"); } dev->tuner_type = RTLSDR_TUNER_R828D; goto found; } /* initialise GPIOs */ rtlsdr_set_gpio_output(dev, 4); /* reset tuner before probing */ rtlsdr_set_gpio_bit(dev, 4, 1); rtlsdr_set_gpio_bit(dev, 4, 0); reg = rtlsdr_i2c_read_reg(dev, FC2580_I2C_ADDR, FC2580_CHECK_ADDR); if ((reg & 0x7f) == FC2580_CHECK_VAL) { fprintf(stderr, "Found FCI 2580 tuner\n"); dev->tuner_type = RTLSDR_TUNER_FC2580; goto found; } reg = rtlsdr_i2c_read_reg(dev, FC0012_I2C_ADDR, FC0012_CHECK_ADDR); if (reg == FC0012_CHECK_VAL) { fprintf(stderr, "Found Fitipower FC0012 tuner\n"); rtlsdr_set_gpio_output(dev, 6); dev->tuner_type = RTLSDR_TUNER_FC0012; /* rtlsdr_set_gpio_output(dev, 5); */ /* rtlsdr_set_gpio_bit(dev, 5, 1); */ goto found; } found: /* use the rtl clock value by default */ dev->tun_xtal = dev->rtl_xtal; dev->tuner = &tuners[dev->tuner_type]; switch (dev->tuner_type) { case RTLSDR_TUNER_FC2580: #if USE_OLD_DAB_IF_GAIN dev->tun_xtal = FC2580_XTAL_FREQ; #endif break; case RTLSDR_TUNER_E4000: #if USE_OLD_DAB_IF_GAIN rtlsdr_demod_write_reg(dev, 1, 0x12, 0x5a, 1);//DVBT_DAGC_TRG_VAL rtlsdr_demod_write_reg(dev, 1, 0x02, 0x40, 1);//DVBT_AGC_TARG_VAL_0 rtlsdr_demod_write_reg(dev, 1, 0x03, 0x5a, 1);//DVBT_AGC_TARG_VAL_8_1 rtlsdr_demod_write_reg(dev, 1, 0xc7, 0x30, 1);//DVBT_AAGC_LOOP_GAIN rtlsdr_demod_write_reg(dev, 1, 0x04, 0xd0, 1);//DVBT_LOOP_GAIN2_3_0 rtlsdr_demod_write_reg(dev, 1, 0x05, 0xbe, 1);//DVBT_LOOP_GAIN2_4 rtlsdr_demod_write_reg(dev, 1, 0xc8, 0x18, 1);//DVBT_LOOP_GAIN3 rtlsdr_demod_write_reg(dev, 1, 0x06, 0x35, 1);//DVBT_VTOP1 rtlsdr_demod_write_reg(dev, 1, 0xc9, 0x21, 1);//DVBT_VTOP2 rtlsdr_demod_write_reg(dev, 1, 0xca, 0x21, 1);//DVBT_VTOP3 rtlsdr_demod_write_reg(dev, 1, 0xcb, 0x00, 1);//DVBT_KRF1 rtlsdr_demod_write_reg(dev, 1, 0x07, 0x40, 1);//DVBT_KRF2 rtlsdr_demod_write_reg(dev, 1, 0xcd, 0x10, 1);//DVBT_KRF3 rtlsdr_demod_write_reg(dev, 1, 0xce, 0x10, 1);//DVBT_KRF4 rtlsdr_demod_write_reg(dev, 0, 0x11, 0xe9d4, 2);//DVBT_AD7_SETTING rtlsdr_demod_write_reg(dev, 1, 0xe5, 0xf0, 1);//DVBT_EN_GI_PGA rtlsdr_demod_write_reg(dev, 1, 0xd9, 0x00, 1);//DVBT_THD_LOCK_UP rtlsdr_demod_write_reg(dev, 1, 0xdb, 0x00, 1);//DVBT_THD_LOCK_DW rtlsdr_demod_write_reg(dev, 1, 0xdd, 0x14, 1);//DVBT_THD_UP1 rtlsdr_demod_write_reg(dev, 1, 0xde, 0xec, 1);//DVBT_THD_DW1 rtlsdr_demod_write_reg(dev, 1, 0xd8, 0x0c, 1);//DVBT_INTER_CNT_LEN rtlsdr_demod_write_reg(dev, 1, 0xe6, 0x02, 1);//DVBT_GI_PGA_STATE rtlsdr_demod_write_reg(dev, 1, 0xd7, 0x09, 1);//DVBT_EN_AGC_PGA rtlsdr_demod_write_reg(dev, 0, 0x10, 0x49, 1);//DVBT_REG_GPO rtlsdr_demod_write_reg(dev, 0, 0x0d, 0x85, 1);//DVBT_REG_MON,DVBT_REG_MONSEL rtlsdr_demod_write_reg(dev, 0, 0x13, 0x02, 1); #endif break; case RTLSDR_TUNER_FC0012: case RTLSDR_TUNER_FC0013: #if USE_OLD_DAB_IF_GAIN rtlsdr_demod_write_reg(dev, 1, 0x12, 0x5a, 1);//DVBT_DAGC_TRG_VAL rtlsdr_demod_write_reg(dev, 1, 0x02, 0x40, 1);//DVBT_AGC_TARG_VAL_0 rtlsdr_demod_write_reg(dev, 1, 0x03, 0x5a, 1);//DVBT_AGC_TARG_VAL_8_1 rtlsdr_demod_write_reg(dev, 1, 0xc7, 0x2c, 1);//DVBT_AAGC_LOOP_GAIN rtlsdr_demod_write_reg(dev, 1, 0x04, 0xcc, 1);//DVBT_LOOP_GAIN2_3_0 rtlsdr_demod_write_reg(dev, 1, 0x05, 0xbe, 1);//DVBT_LOOP_GAIN2_4 rtlsdr_demod_write_reg(dev, 1, 0xc8, 0x16, 1);//DVBT_LOOP_GAIN3 rtlsdr_demod_write_reg(dev, 1, 0x06, 0x35, 1);//DVBT_VTOP1 rtlsdr_demod_write_reg(dev, 1, 0xc9, 0x21, 1);//DVBT_VTOP2 rtlsdr_demod_write_reg(dev, 1, 0xca, 0x21, 1);//DVBT_VTOP3 rtlsdr_demod_write_reg(dev, 1, 0xcb, 0x00, 1);//DVBT_KRF1 rtlsdr_demod_write_reg(dev, 1, 0x07, 0x40, 1);//DVBT_KRF2 rtlsdr_demod_write_reg(dev, 1, 0xcd, 0x10, 1);//DVBT_KRF3 rtlsdr_demod_write_reg(dev, 1, 0xce, 0x10, 1);//DVBT_KRF4 rtlsdr_demod_write_reg(dev, 0, 0x11, 0xe9bf, 2);//DVBT_AD7_SETTING rtlsdr_demod_write_reg(dev, 1, 0xe5, 0xf0, 1);//DVBT_EN_GI_PGA rtlsdr_demod_write_reg(dev, 1, 0xd9, 0x00, 1);//DVBT_THD_LOCK_UP rtlsdr_demod_write_reg(dev, 1, 0xdb, 0x00, 1);//DVBT_THD_LOCK_DW rtlsdr_demod_write_reg(dev, 1, 0xdd, 0x11, 1);//DVBT_THD_UP1 rtlsdr_demod_write_reg(dev, 1, 0xde, 0xef, 1);//DVBT_THD_DW1 rtlsdr_demod_write_reg(dev, 1, 0xd8, 0x0c, 1);//DVBT_INTER_CNT_LEN rtlsdr_demod_write_reg(dev, 1, 0xe6, 0x02, 1);//DVBT_GI_PGA_STATE rtlsdr_demod_write_reg(dev, 1, 0xd7, 0x09, 1);//DVBT_EN_AGC_PGA #endif break; case RTLSDR_TUNER_R828D: // If NOT an RTL-SDR Blog V4, set typical R828D 16 MHz freq. Otherwise, keep at 28.8 MHz. if (!(rtlsdr_check_dongle_model(dev, "RTLSDRBlog", "Blog V4"))) { dev->tun_xtal = R828D_XTAL_FREQ; } /* fall-through */ case RTLSDR_TUNER_R820T: #if USE_OLD_DAB_IF_GAIN rtlsdr_demod_write_reg(dev, 1, 0x12, 0x5a, 1);//DVBT_DAGC_TRG_VAL rtlsdr_demod_write_reg(dev, 1, 0x02, 0x40, 1);//DVBT_AGC_TARG_VAL_0 rtlsdr_demod_write_reg(dev, 1, 0x03, 0x80, 1);//DVBT_AGC_TARG_VAL_8_1 rtlsdr_demod_write_reg(dev, 1, 0xc7, 0x24, 1);//DVBT_AAGC_LOOP_GAIN rtlsdr_demod_write_reg(dev, 1, 0x04, 0xcc, 1);//DVBT_LOOP_GAIN2_3_0 rtlsdr_demod_write_reg(dev, 1, 0x05, 0xbe, 1);//DVBT_LOOP_GAIN2_4 rtlsdr_demod_write_reg(dev, 1, 0xc8, 0x14, 1);//DVBT_LOOP_GAIN3 rtlsdr_demod_write_reg(dev, 1, 0x06, 0x35, 1);//DVBT_VTOP1 rtlsdr_demod_write_reg(dev, 1, 0xc9, 0x21, 1);//DVBT_VTOP2 rtlsdr_demod_write_reg(dev, 1, 0xca, 0x21, 1);//DVBT_VTOP3 rtlsdr_demod_write_reg(dev, 1, 0xcb, 0x00, 1);//DVBT_KRF1 rtlsdr_demod_write_reg(dev, 1, 0x07, 0x40, 1);//DVBT_KRF2 rtlsdr_demod_write_reg(dev, 1, 0xcd, 0x10, 1);//DVBT_KRF3 rtlsdr_demod_write_reg(dev, 1, 0xce, 0x10, 1);//DVBT_KRF4 rtlsdr_demod_write_reg(dev, 0, 0x11, 0xe9f4, 2);//DVBT_AD7_SETTING #endif /* disable Zero-IF mode */ rtlsdr_demod_write_reg(dev, 1, 0xb1, 0x1a, 1); /* only enable In-phase ADC input */ rtlsdr_demod_write_reg(dev, 0, 0x08, 0x4d, 1); /* the R82XX use 3.57 MHz IF for the DVB-T 6 MHz mode, and * 4.57 MHz for the 8 MHz mode */ rtlsdr_set_if_freq(dev, R82XX_IF_FREQ); /* enable spectrum inversion */ rtlsdr_demod_write_reg(dev, 1, 0x15, 0x01, 1); break; case RTLSDR_TUNER_UNKNOWN: fprintf(stderr, "No supported tuner found\n"); rtlsdr_set_direct_sampling(dev, 1); break; default: break; } if (dev->tuner->init) r = dev->tuner->init(dev); rtlsdr_set_i2c_repeater(dev, 0); #if INIT_R820T_TUNER_GAIN if ( dev->tuner_type == RTLSDR_TUNER_R820T ) { rtlsdr_set_tuner_if_mode(dev, 10000 + 11); rtlsdr_set_tuner_gain_mode(dev, 0); } #endif *out_dev = dev; return 0; err: if (dev) { if (dev->devh) libusb_close(dev->devh); if (dev->ctx) libusb_exit(dev->ctx); free(dev); } return r; } int rtlsdr_close(rtlsdr_dev_t *dev) { #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_close()\n"); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_close(dev); } #endif if (!dev) return -1; /* automatic de-activation of bias-T */ /* no: keep last bias-tee status, that rtl_biast hasn't to be called again */ /* rtlsdr_set_bias_tee(dev, 0); */ if(!dev->dev_lost) { /* block until all async operations have been completed (if any) */ while (RTLSDR_INACTIVE != dev->async_status) { #ifdef _WIN32 Sleep(1); #else usleep(1000); #endif } rtlsdr_deinit_baseband(dev); } else { fprintf(stderr, "Resetting device...\n"); libusb_reset_device(dev->devh); } softagc_uninit(dev); pthread_mutex_destroy(&dev->cs_mutex); libusb_release_interface(dev->devh, 0); #ifdef DETACH_KERNEL_DRIVER if (dev->driver_active) { if (!libusb_attach_kernel_driver(dev->devh, 0)) fprintf(stderr, "Reattached kernel driver\n"); else fprintf(stderr, "Reattaching kernel driver failed!\n"); } #endif libusb_close(dev->devh); libusb_exit(dev->ctx); free(dev); return 0; } int rtlsdr_reset_buffer(rtlsdr_dev_t *dev) { #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_reset_buffer(dev); } #endif if (!dev) return -1; rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x1002, 2); rtlsdr_write_reg(dev, USBB, USB_EPA_CTL, 0x0000, 2); return 0; } static void rtlsdr_process_env_opts(rtlsdr_dev_t *dev) { char * opts = getenv("LIBRTLSDR_OPT"); if ( opts ) { fprintf(stderr, "process options '%s' from environment 'LIBRTLSDR_OPT'\n", opts); rtlsdr_set_opt_string(dev, opts, 1); } dev->called_set_opt = 1; } int rtlsdr_read_sync(rtlsdr_dev_t *dev, void *buf, int len, int *n_read) { if (dev && !dev->called_set_opt ) rtlsdr_process_env_opts(dev); #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_read_sync(dev, buf, len, n_read); } #endif if (!dev) return -1; return libusb_bulk_transfer(dev->devh, 0x81, buf, len, n_read, BULK_TIMEOUT); } /* return == softagc got activated */ static int reactivate_softagc(rtlsdr_dev_t *dev, enum softagc_stateT newState) { if ( dev->softagc.softAgcMode > SOFTAGC_OFF ) { if ( dev->softagc.agcState != SOFTSTATE_OFF && dev->softagc.softAgcMode >= SOFTAGC_AUTO ) { /* softagc already running -> nothing to do */ if ( dev->softagc.verbose ) fprintf(stderr, "rtlsdr reactivate_softagc(): state already %d\n", dev->softagc.agcState); return 1; } else { dev->softagc.agcState = newState; if ( dev->softagc.verbose ) fprintf(stderr, "rtlsdr reactivate_softagc switched to state %d\n", newState); return 1; } } if ( dev->softagc.verbose ) fprintf(stderr, "*** rtlsdr reactivate_softagc(): Soft AGC is inactive!\n"); return 0; } static void *softagc_control_worker(void *arg) { rtlsdr_dev_t *dev = (rtlsdr_dev_t *)arg; struct softagc_state *agc = &dev->softagc; while(1) { safe_cond_wait(&agc->cond, &agc->mutex); if ( agc->exit_command_thread ) pthread_exit(0); if ( agc->command_changeGain ) { /* no need for extra mutex/buffer: next call is after DEAD_TIME */ agc->command_changeGain = 0; rtlsdr_set_tuner_gain( dev, dev->softagc.command_newGain ); dev->softagc.remainingDeadSps = dev->softagc.deadTimeSps; if ( dev->softagc.verbose ) fprintf(stderr, "rtlsdr softagc_control_worker(): applied gain %d\n" , dev->softagc.command_newGain ); } } } static void softagc_init(rtlsdr_dev_t *dev) { pthread_attr_t attr; /* prepare thread */ dev->softagc.exit_command_thread = 0; dev->softagc.command_newGain = 0; dev->softagc.command_changeGain = 0; /* create thread */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); pthread_mutex_init(&dev->softagc.mutex, NULL); pthread_cond_init(&dev->softagc.cond, NULL); pthread_create( &dev->softagc.command_thread, &attr, softagc_control_worker, dev); pthread_attr_destroy(&attr); /* manual gain mode for "softagc" */ rtlsdr_set_tuner_gain_mode(dev, 1 ); } static void softagc_uninit(rtlsdr_dev_t *dev) { if ( dev->softagc.softAgcMode == SOFTAGC_OFF ) return; dev->softagc.exit_command_thread = 1; safe_cond_signal(&dev->softagc.cond, &dev->softagc.mutex); pthread_join(dev->softagc.command_thread, NULL); pthread_cond_destroy(&dev->softagc.cond); pthread_mutex_destroy(&dev->softagc.mutex); } /* return == keepBlock */ static int softagc(rtlsdr_dev_t *dev, unsigned char *buf, int len) { struct softagc_state * agc = &dev->softagc; int distrib[16]; if ( agc->agcState == SOFTSTATE_INIT ) { agc->agcState = SOFTSTATE_RESET; #if 0 fprintf(stderr, "*** init softagc gainmode\n"); #endif return 0; /* throw away this block */ } else if ( agc->agcState == SOFTSTATE_RESET ) { int k, numGains = 0; const int * gains = get_tuner_gains(dev, &numGains ); #if 0 fprintf(stderr, "*** rtlsdr softagc: get_tuner_gains() delivered %d values\n", numGains); #endif if ( ! numGains ) { /* device is not initialized yet */ return 1; } if ( numGains == 1 ) { agc->softAgcMode = SOFTAGC_OFF; agc->agcState = SOFTSTATE_OFF; if ( dev->verbose || dev->softagc.verbose ) fprintf(stderr, "*** rtlsdr softagc(): just single gain -> deactivating\n"); return 1; } /* initialize measurement */ if (!agc->scanTimeSps) agc->scanTimeSps = (int)( (agc->scanTimeMs * dev->rate) / 1000 ); if (!agc->deadTimeSps) agc->deadTimeSps = (int)( (agc->deadTimeMs * dev->rate) / 1000 ); agc->remainingDeadSps = INT_MAX; agc->remainingScanSps = agc->scanTimeSps; agc->numInHisto = 0; for ( k = 0; k < 16; ++k ) agc->histo[k] = 0; dev->softagc.gainIdx = numGains - 1; dev->softagc.command_newGain = gains[dev->softagc.gainIdx]; dev->softagc.command_changeGain = 1; safe_cond_signal(&dev->softagc.cond, &dev->softagc.mutex); if ( dev->softagc.verbose ) fprintf(stderr, "rtlsdr softagc(): set maximum gain %d / 10 dB at idx %d\n" , gains[dev->softagc.gainIdx] , dev->softagc.gainIdx ); agc->agcState = SOFTSTATE_RESET_CONT; return 0; } if ( agc->remainingDeadSps == INT_MAX ) return 0; if ( agc->remainingDeadSps ) { if ( agc->remainingDeadSps >= len/2 ) { /* fprintf(stderr, "cont waiting dead samples: received %d of remaining %d smp\n", len, agc->remainingDeadSps); */ agc->remainingDeadSps -= len/2; return ( agc->agcState == SOFTSTATE_RESET_CONT ) ? 0 : 1; } else { buf = buf + ( 2 * agc->remainingDeadSps); len -= 2 * agc->remainingDeadSps; agc->remainingDeadSps = 0; } } /* finish when arrived at lowest possible gain */ if ( ! agc->gainIdx && agc->agcState == SOFTSTATE_RESET_CONT ) { agc->agcState = SOFTSTATE_OFF; /* TODO: try deactivating Bias-T */ if ( dev->softagc.verbose ) fprintf(stderr, "rtlsdr softagc(): gain idx is 0 -> finish soft agc\n"); return 1; } /* calculate histogram and distribution */ { int * histo = &(agc->histo[0]); int i, k; for ( i = 0; i < len; ++i ) { if ( buf[i] >= 128 ) ++histo[ ( (unsigned)buf[i] -128) >> 3 ]; /* -128 ==> max is then 127 == 7 bit */ else ++histo[ ( 127 - (unsigned)buf[i] ) >> 3 ]; } agc->numInHisto += len; agc->remainingScanSps -= len/2; distrib[15] = histo[15]; for ( k = 14; k >= 8; --k ) distrib[k] = distrib[k+1] + histo[k]; } /* detect oversteering */ if ( 64 * distrib[15] >= agc->numInHisto /* max more often than 1.56% (= 100/64) of all near 1 */ ||16 * distrib[12] >= agc->numInHisto /* more often than 6.25% of all >= 0.75 */ || 4 * distrib[ 8] >= agc->numInHisto ) /* more often than 25% of all >= 0.5 */ { const int N = agc->numInHisto; #if 0 fprintf(stderr, "dp[8-15]: "); for ( int k = 8; k < 16; ++k ) fprintf(stderr, "%d:%d, ", k, (distrib[k] * 100) / N); fprintf(stderr, "\ttotal %d\n", N); #endif if ( agc->gainIdx > 0 ) { int k, numGains = 0; const int * gains = get_tuner_gains(dev, &numGains ); agc->remainingDeadSps = INT_MAX; agc->remainingScanSps = agc->scanTimeSps; agc->numInHisto = 0; for ( k = 0; k < 16; ++k ) agc->histo[k] = 0; -- agc->gainIdx; agc->command_newGain = gains[agc->gainIdx]; agc->command_changeGain = 1; safe_cond_signal(&agc->cond, &agc->mutex); } return ( agc->agcState == SOFTSTATE_RESET_CONT ) ? 0 : 1; } if ( agc->remainingScanSps < 0 ) { /* TODO: check if we should increase gain .. or even activate Bias-T */ if ( dev->softagc.verbose ) fprintf(stderr, "*** rtlsdr softagc(): no more remaining samples to wait for\n"); agc->remainingScanSps = 0; switch ( agc->softAgcMode ) { case SOFTAGC_OFF: case SOFTAGC_ON_CHANGE: switch ( agc->agcState ) { case SOFTSTATE_OFF: case SOFTSTATE_RESET_CONT: agc->agcState = SOFTSTATE_OFF; if ( dev->softagc.verbose ) fprintf(stderr, "softagc finished. now mode %d, state %d\n", agc->softAgcMode, agc->agcState); return 1; case SOFTSTATE_ON: case SOFTSTATE_RESET: case SOFTSTATE_INIT: return 1; } break; case SOFTAGC_AUTO_ATTEN: case SOFTAGC_AUTO: agc->agcState = SOFTSTATE_ON; return 1; } } return ( agc->agcState == SOFTSTATE_RESET_CONT ) ? 0 : 1; } static void LIBUSB_CALL _libusb_callback(struct libusb_transfer *xfer) { rtlsdr_dev_t *dev = (rtlsdr_dev_t *)xfer->user_data; if (LIBUSB_TRANSFER_COMPLETED == xfer->status) { int keepBlock = 1; if ( dev->softagc.agcState != SOFTSTATE_OFF ) keepBlock = softagc(dev, xfer->buffer, xfer->actual_length); if (dev->cb && keepBlock) dev->cb(xfer->buffer, xfer->actual_length, dev->cb_ctx); libusb_submit_transfer(xfer); /* resubmit transfer */ dev->xfer_errors = 0; } else if (LIBUSB_TRANSFER_CANCELLED != xfer->status) { #ifndef _WIN32 if (LIBUSB_TRANSFER_ERROR == xfer->status || LIBUSB_TRANSFER_TIMED_OUT == xfer->status) dev->xfer_errors++; if (dev->xfer_errors >= dev->xfer_buf_num || LIBUSB_TRANSFER_NO_DEVICE == xfer->status) { #endif dev->dev_lost = 1; rtlsdr_cancel_async(dev); fprintf(stderr, "cb transfer status: %d, " "canceling...\n", xfer->status); #ifndef _WIN32 } #endif } } int rtlsdr_wait_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx) { if (dev && !dev->called_set_opt ) rtlsdr_process_env_opts(dev); #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_wait_async(dev, cb, ctx); } #endif return rtlsdr_read_async(dev, cb, ctx, 0, 0); } static int _rtlsdr_alloc_async_buffers(rtlsdr_dev_t *dev) { unsigned int i; if (!dev) return -1; if (!dev->xfer) { dev->xfer = malloc(dev->xfer_buf_num * sizeof(struct libusb_transfer *)); for(i = 0; i < dev->xfer_buf_num; ++i) dev->xfer[i] = libusb_alloc_transfer(0); } if (dev->xfer_buf) return -2; dev->xfer_buf = malloc(dev->xfer_buf_num * sizeof(unsigned char *)); memset(dev->xfer_buf, 0, dev->xfer_buf_num * sizeof(unsigned char *)); #if defined (__linux__) && LIBUSB_API_VERSION >= 0x01000105 fprintf(stderr, "Allocating %d zero-copy buffers\n", dev->xfer_buf_num); dev->use_zerocopy = 1; for (i = 0; i < dev->xfer_buf_num; ++i) { dev->xfer_buf[i] = libusb_dev_mem_alloc(dev->devh, dev->xfer_buf_len); if (!dev->xfer_buf[i]) { fprintf(stderr, "Failed to allocate zero-copy " "buffer for transfer %d\nFalling " "back to buffers in userspace\n", i); dev->use_zerocopy = 0; break; } } /* zero-copy buffer allocation failed (partially or completely) * we need to free the buffers again if already allocated */ if (!dev->use_zerocopy) { for (i = 0; i < dev->xfer_buf_num; ++i) { if (dev->xfer_buf[i]) libusb_dev_mem_free(dev->devh, dev->xfer_buf[i], dev->xfer_buf_len); } } #endif /* no zero-copy available, allocate buffers in userspace */ if (!dev->use_zerocopy) { fprintf(stderr, "Allocating %d (non-zero-copy) user-space buffers\n", dev->xfer_buf_num); for (i = 0; i < dev->xfer_buf_num; ++i) { dev->xfer_buf[i] = malloc(dev->xfer_buf_len); if (!dev->xfer_buf[i]) return -ENOMEM; } } return 0; } static int _rtlsdr_free_async_buffers(rtlsdr_dev_t *dev) { unsigned int i; if (!dev) return -1; if (dev->xfer) { for(i = 0; i < dev->xfer_buf_num; ++i) { if (dev->xfer[i]) { libusb_free_transfer(dev->xfer[i]); } } free(dev->xfer); dev->xfer = NULL; } if (dev->xfer_buf) { for (i = 0; i < dev->xfer_buf_num; ++i) { if (dev->xfer_buf[i]) { if (dev->use_zerocopy) { #if defined (__linux__) && LIBUSB_API_VERSION >= 0x01000105 libusb_dev_mem_free(dev->devh, dev->xfer_buf[i], dev->xfer_buf_len); #endif } else { free(dev->xfer_buf[i]); } } } free(dev->xfer_buf); dev->xfer_buf = NULL; } return 0; } int rtlsdr_read_async(rtlsdr_dev_t *dev, rtlsdr_read_async_cb_t cb, void *ctx, uint32_t buf_num, uint32_t buf_len) { unsigned int i; int r = 0; struct timeval tv = { 1, 0 }; struct timeval zerotv = { 0, 0 }; enum rtlsdr_async_status next_status = RTLSDR_INACTIVE; if (dev && !dev->called_set_opt ) rtlsdr_process_env_opts(dev); #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_read_async(buf_num %u, buf_len %u)\n", (unsigned)buf_num, (unsigned)buf_len); #endif #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_read_async(dev, cb, ctx, buf_num, buf_len); } #endif if (!dev) return -1; if (RTLSDR_INACTIVE != dev->async_status) return -2; dev->async_status = RTLSDR_RUNNING; dev->async_cancel = 0; dev->cb = cb; dev->cb_ctx = ctx; if (buf_num > 0) dev->xfer_buf_num = buf_num; else dev->xfer_buf_num = DEFAULT_BUF_NUMBER; if (buf_len > 0 && buf_len % 512 == 0) /* len must be multiple of 512 */ dev->xfer_buf_len = buf_len; else dev->xfer_buf_len = DEFAULT_BUF_LENGTH; _rtlsdr_alloc_async_buffers(dev); for(i = 0; i < dev->xfer_buf_num; ++i) { libusb_fill_bulk_transfer(dev->xfer[i], dev->devh, 0x81, dev->xfer_buf[i], dev->xfer_buf_len, _libusb_callback, (void *)dev, BULK_TIMEOUT); r = libusb_submit_transfer(dev->xfer[i]); if (r < 0) { fprintf(stderr, "Failed to submit transfer %i\n" "Please increase your allowed " "usbfs buffer size with the " "following command:\n" "echo 0 > /sys/module/usbcore" "/parameters/usbfs_memory_mb\n", i); dev->async_status = RTLSDR_CANCELING; break; } } while (RTLSDR_INACTIVE != dev->async_status) { r = libusb_handle_events_timeout_completed(dev->ctx, &tv, &dev->async_cancel); if (r < 0) { /*fprintf(stderr, "handle_events returned: %d\n", r);*/ if (r == LIBUSB_ERROR_INTERRUPTED) /* stray signal */ continue; break; } if (RTLSDR_CANCELING == dev->async_status) { next_status = RTLSDR_INACTIVE; if (!dev->xfer) break; for(i = 0; i < dev->xfer_buf_num; ++i) { if (!dev->xfer[i]) continue; if (LIBUSB_TRANSFER_CANCELLED != dev->xfer[i]->status) { r = libusb_cancel_transfer(dev->xfer[i]); /* handle events after canceling * to allow transfer status to * propagate */ libusb_handle_events_timeout_completed(dev->ctx, &zerotv, NULL); if (r < 0) continue; next_status = RTLSDR_CANCELING; } } if (dev->dev_lost || RTLSDR_INACTIVE == next_status) { /* handle any events that still need to * be handled before exiting after we * just cancelled all transfers */ libusb_handle_events_timeout_completed(dev->ctx, &zerotv, NULL); break; } } } _rtlsdr_free_async_buffers(dev); dev->async_status = next_status; return r; } int rtlsdr_cancel_async(rtlsdr_dev_t *dev) { #ifdef _ENABLE_RPC if (rtlsdr_rpc_is_enabled()) { return rtlsdr_rpc_cancel_async(dev); } #endif if (!dev) return -1; /* if streaming, try to cancel gracefully */ if (RTLSDR_RUNNING == dev->async_status) { dev->async_status = RTLSDR_CANCELING; dev->async_cancel = 1; return 0; } /* if called while in pending state, change the state forcefully */ #if 0 if (RTLSDR_INACTIVE != dev->async_status) { dev->async_status = RTLSDR_INACTIVE; return 0; } #endif return -2; } uint32_t rtlsdr_get_tuner_clock(void *dev) { uint32_t tuner_freq; if (!dev) return 0; /* read corrected clock value */ if (rtlsdr_get_xtal_freq((rtlsdr_dev_t *)dev, NULL, &tuner_freq)) return 0; return tuner_freq; } int rtlsdr_i2c_write_fn(void *dev, uint8_t addr, uint8_t *buf, int len) { if (dev) return rtlsdr_i2c_write(((rtlsdr_dev_t *)dev), addr, buf, len); return -1; } int rtlsdr_i2c_read_fn(void *dev, uint8_t addr, uint8_t *buf, int len) { if (dev) return rtlsdr_i2c_read(((rtlsdr_dev_t *)dev), addr, buf, len); return -1; } /* Infrared (IR) sensor support * based on Linux dvb_usb_rtl28xxu drivers/media/usb/dvb-usb-v2/rtl28xxu.h * Copyright (C) 2009 Antti Palosaari * Copyright (C) 2011 Antti Palosaari * Copyright (C) 2012 Thomas Mair */ struct rtl28xxu_req { uint16_t value; uint16_t index; uint16_t size; uint8_t *data; }; struct rtl28xxu_reg_val { uint16_t reg; uint8_t val; }; struct rtl28xxu_reg_val_mask { int block; uint16_t reg; uint8_t val; uint8_t mask; }; static int rtlsdr_read_regs(rtlsdr_dev_t *dev, uint8_t block, uint16_t addr, uint8_t *data, uint8_t len) { int r; uint16_t index = (block << 8); if (block == IRB) index = (SYSB << 8) | 0x01; r = libusb_control_transfer(dev->devh, CTRL_IN, 0, addr, index, data, len, CTRL_TIMEOUT); if (r < 0) fprintf(stderr, "%s failed with %d\n", __FUNCTION__, r); return r; } static int rtlsdr_write_reg_mask(rtlsdr_dev_t *d, int block, uint16_t reg, uint8_t val, uint8_t mask) { uint8_t tmp; /* no need for read if whole reg is written */ if (mask != 0xff) { tmp = rtlsdr_read_reg(d, block, reg, 1); val &= mask; tmp &= ~mask; val |= tmp; } return rtlsdr_write_reg(d, block, reg, (uint16_t)val, 1); } #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) int rtlsdr_ir_query(rtlsdr_dev_t *d, uint8_t *buf, size_t buf_len) { int ret = -1; size_t i, len; static const struct rtl28xxu_reg_val_mask refresh_tab[] = { {IRB, IR_RX_IF, 0x03, 0xff}, {IRB, IR_RX_BUF_CTRL, 0x80, 0xff}, {IRB, IR_RX_CTRL, 0x80, 0xff}, }; /* init remote controller */ if (!d->rc_active) { /* fprintf(stderr, "initializing remote controller\n"); */ static const struct rtl28xxu_reg_val_mask init_tab[] = { {USBB, DEMOD_CTL, 0x00, 0x04}, {USBB, DEMOD_CTL, 0x00, 0x08}, {USBB, USB_CTRL, 0x20, 0x20}, {USBB, GPD, 0x00, 0x08}, {USBB, GPOE, 0x08, 0x08}, {USBB, GPO, 0x08, 0x08}, {IRB, IR_MAX_DURATION0, 0xd0, 0xff}, {IRB, IR_MAX_DURATION1, 0x07, 0xff}, {IRB, IR_IDLE_LEN0, 0xc0, 0xff}, {IRB, IR_IDLE_LEN1, 0x00, 0xff}, {IRB, IR_GLITCH_LEN, 0x03, 0xff}, {IRB, IR_RX_CLK, 0x09, 0xff}, {IRB, IR_RX_CFG, 0x1c, 0xff}, {IRB, IR_MAX_H_TOL_LEN, 0x1e, 0xff}, {IRB, IR_MAX_L_TOL_LEN, 0x1e, 0xff}, {IRB, IR_RX_CTRL, 0x80, 0xff}, }; for (i = 0; i < ARRAY_SIZE(init_tab); i++) { ret = rtlsdr_write_reg_mask(d, init_tab[i].block, init_tab[i].reg, init_tab[i].val, init_tab[i].mask); if (ret < 0) { fprintf(stderr, "write %ld reg %d %.4x %.2x %.2x failed\n", (unsigned long)i, init_tab[i].block, init_tab[i].reg, init_tab[i].val, init_tab[i].mask); goto err; } } d->rc_active = 1; /* fprintf(stderr, "rc active\n"); */ } /* TODO: option to ir disable */ buf[0] = rtlsdr_read_reg(d, IRB, IR_RX_IF, 1); if (buf[0] != 0x83) { if (buf[0] == 0 || /* no IR signal */ /* also observed: 0x82, 0x81 - with lengths 1, 5, 0.. unknown, sometimes occurs at edges "IR not ready"? causes a -7 timeout if we read */ buf[0] == 0x82 || buf[0] == 0x81) { /* graceful exit */ } else { fprintf(stderr, "read IR_RX_IF unexpected: %.2x\n", buf[0]); } ret = 0; goto exit; } buf[0] = rtlsdr_read_reg(d, IRB, IR_RX_BC, 1); len = buf[0]; /* fprintf(stderr, "read IR_RX_BC len=%d\n", len); */ if (len > buf_len) { /* fprintf(stderr, "read IR_RX_BC too large for buffer, %lu > %lu\n", buf_len, buf_len); */ goto exit; } /* read raw code from hw */ ret = rtlsdr_read_regs(d, IRB, IR_RX_BUF, buf, len); if (ret < 0) goto err; /* let hw receive new code */ for (i = 0; i < ARRAY_SIZE(refresh_tab); i++) { ret = rtlsdr_write_reg_mask(d, refresh_tab[i].block, refresh_tab[i].reg, refresh_tab[i].val, refresh_tab[i].mask); if (ret < 0) goto err; } /* On success return length */ ret = len; exit: return ret; err: printf("failed=%d\n", ret); return ret; } int rtlsdr_set_bias_tee_gpio(rtlsdr_dev_t *dev, int gpio, int on) { if (!dev) return -1; #if LOG_API_CALLS fprintf(stderr, "LOG: rtlsdr_set_bias_tee_gpio(gpio %d, on %d)\n", gpio, on); #endif rtlsdr_set_gpio_output(dev, gpio); rtlsdr_set_gpio_bit(dev, gpio, on); reactivate_softagc(dev, SOFTSTATE_RESET); return 0; } int rtlsdr_set_bias_tee(rtlsdr_dev_t *dev, int on) { if (!dev) return -1; return rtlsdr_set_bias_tee_gpio(dev, dev->biast_gpio_pin_no, on); } int rtlsdr_set_harmonic_rx(rtlsdr_dev_t *dev, int harmonic) { if (!dev) return -1; if ( dev->tuner_type == RTLSDR_TUNER_R820T ) { if ( 0 <= harmonic && harmonic <= 16 ) { dev->r82xx_c.harmonic = harmonic; return 0; } return -2; } else return -3; } const char * rtlsdr_get_opt_help(int longInfo) { if ( longInfo ) return "\t[-O\tset RTL driver options seperated with ':', e.g. -O 'bc=30000:agc=0' ]\n" "\t\tf= set tuner frequency\n" "\t\tbw= set tuner bandwidth\n" "\t\tbc= set band center relative to the complex-base-band '0' frequency\n" "\t\t puts the tuner frequency onto this if frequency (default: 0)\n" "\t\tsb= set tuner sideband/mirror: 'L' or '0' for lower side band,\n" "\t\t 'U' or '1' for upper side band. default for R820T/2: 'L'\n" "\t\tagc= activates tuner agc with '1'. deactivates with '0'\n" "\t\tgain= set tuner gain. 400 for 40.0 dB\n" "\t\tifm= set R820T/2 tuner's variable-gain-amplifier (VGA). default: 10011\n" "\t\t 0: activate agc controlled from RTL2832's feedback\n" "\t\t around 0: set gain in 10th dB. 408 for +40.8 dB\n" "\t\t 5000+val: set gain to val in 10th dB. 5408 for +40.8 dB\n" "\t\t 10000+idx: set gain idx 0 .. 15: 10015 for maximum gain\n" "\t\tdagc= set RTL2832's digital agc (after ADC). 1 to activate. 0 to deactivate\n" "\t\tds= deactivate/bypass tuner with 1\n" "\t\tdm= set dynamic direct threshold mode or threshold frequency:\n" "\t\t 0: use I & Q; 1: use I; 2: use Q; 3: use I below threshold frequency;\n" "\t\t 4: use Q below threshold frequency (=RTL-SDR v3)\n" "\t\t other values set the threshold frequency\n" #if ENBALE_R820T_HARM_OPT "\t\tharm= R820T/2: use Nth harmonic for frequencies above 1.76 GHz. default: 5\n" #endif #if ENABLE_VCO_OPTIONS "\t\tvcocmin= set R820T/2 VCO current min: 0..7: higher value is more current\n" "\t\tvcocmax= set R820T/2 VCO current max: 0..7\n" "\t\tvcoalgo= set R820T/2 VCO algorithm. 0: default. 1: with vcomax=3.9G. 2: Youssef/Carl\n" #endif "\t\tTp= set GPIO pin for Bias T, default =0 for rtl-sdr.com compatible V3\n" "\t\tT= 1 activates power at antenna one some dongles, e.g. rtl-sdr.com's V3\n" #ifdef WITH_UDP_SERVER "\t\tport= 1 or tcp port number activates UDP server. default: 0.\n" "\t\t default port number: 32323\n" #endif ; else return "\t[-O\tset RTL options string seperated with ':', e.g. -O 'bc=30000:agc=0' ]\n" "\t\tverbose:f=:bw=:bc=:sb=\n" "\t\tagc=:gain=:ifm=:dagc=\n" #if ENBALE_R820T_HARM_OPT "\t\tharm=\n" #endif #if ENABLE_VCO_OPTIONS "\t\tds=:dm=:vcocmin=:vcocmax=:vcoalgo=\n" "\t\tT=\n" #else "\t\tds=:dm=:T=\n" #endif #ifdef WITH_UDP_SERVER "\t\tport=\n" #endif ; } int rtlsdr_set_opt_string(rtlsdr_dev_t *dev, const char *opts, int verbose) { char * optStr, * optPart; int retAll = 0; if (!dev) return -1; dev->called_set_opt = 1; /* set some defaults */ dev->softagc.deadTimeMs = 100; dev->softagc.scanTimeMs = 100; optStr = strdup(opts); if (!optStr) return -1; optPart = strtok(optStr, ":,"); while (optPart) { int ret = 0; //if (verbose || dev->verbose) // fprintf(stderr, "\nrtlsdr_set_opt_string(): parsing option '%s'\n", optPart); if (!strcmp(optPart, "verbose") || !strcmp(optPart, "v")) { fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed option verbose\n"); verbose = ++dev->verbose; dev->r82xx_c.verbose = verbose; ret = 0; } else if (!strncmp(optPart, "f=", 2)) { double freqDbl = parseFreq(optPart + 2); uint64_t freq = (uint64_t)(freqDbl + 0.5); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed frequency %f MHz\n", freq * 1E-6); ret = rtlsdr_set_center_freq64(dev, freq); } else if (!strncmp(optPart, "bw=", 3)) { uint32_t bw = (uint32_t)( atol(optPart +3) * 1000 ); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed bandwidth %u\n", (unsigned)bw); ret = rtlsdr_set_tuner_bandwidth(dev, bw); } else if (!strncmp(optPart, "bc=", 3)) { double freqDbl = parseFreq(optPart + 3); int32_t if_band_center_freq = (int32_t)(freqDbl + 0.5); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed band center %d\n", (int)if_band_center_freq); ret = rtlsdr_set_tuner_band_center(dev, if_band_center_freq ); } else if (!strncmp(optPart, "sb=", 3)) { int32_t sideband = (int32_t)(atoi(optPart +3)); if (!strcmp(optPart +3, "L") || !strcmp(optPart +3, "l") || !strcmp(optPart +3, "0")) sideband = 0; else if (!strcmp(optPart +3, "U") || !strcmp(optPart +3, "u") || !strcmp(optPart +3, "1")) sideband = 1; if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed sideband %d == %s\n", (int)sideband, (sideband ? "Upper" : "Lower") ); ret = rtlsdr_set_tuner_sideband(dev, sideband ); } else if (!strncmp(optPart, "agc=", 4)) { int manual = 1 - atoi(optPart +4); /* invert logic */ if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed tuner gain mode, manual=%d\n", manual); ret = rtlsdr_set_tuner_gain_mode(dev, manual); } else if (!strncmp(optPart, "gain=", 5)) { int gain = atoi(optPart +5); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed tuner gain = %d /10 dB\n", gain); ret = rtlsdr_set_tuner_gain(dev, gain); } else if (!strncmp(optPart, "agcv=", 5)) { /* previous option name */ int agcv = atoi(optPart +5); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed tuner if_mode = %d\n", agcv); if (agcv < 0) agcv = 0; ret = rtlsdr_set_tuner_if_mode(dev, agcv); } else if (!strncmp(optPart, "ifm=", 4)) { /* new option name */ int ifmode = atoi(optPart +4); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed tuner if_mode = %d\n", ifmode); ret = rtlsdr_set_tuner_if_mode(dev, ifmode); } else if (!strncmp(optPart, "dagc=", 5)) { int on = atoi(optPart +5); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed rtl/digital gain mode %d\n", on); ret = rtlsdr_set_agc_mode(dev, on); } else if (!strncmp(optPart, "ds=", 3)) { int on = atoi(optPart +3); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed direct sampling config %d\n", on); ret = rtlsdr_set_direct_sampling(dev, on); } else if (!strncmp(optPart, "dm=", 3)) { uint32_t dm = (uint32_t)parseFreq(optPart + 3); if (verbose) { if (dm <= RTLSDR_DS_Q_BELOW) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed direct sampling mode %u == %s\n", dm, dsmode_str[dm]); else fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed direct sampling threshold %u\n", dm); } if (dm <= RTLSDR_DS_Q_BELOW) dev->direct_sampling_mode = (enum rtlsdr_ds_mode)dm; else dev->direct_sampling_threshold = dm; ret = rtlsdr_set_ds_mode(dev, dev->direct_sampling_mode, dev->direct_sampling_threshold); } #if ENBALE_R820T_HARM_OPT else if (!strncmp(optPart, "harm=", 5)) { int harmonic = atoi(optPart +5); if ( 0 <= harmonic && harmonic <= 16 ) { dev->r82xx_c.harmonic = harmonic; ret = 0; if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed harmonic config %d\n", harmonic); } else if (verbose) { fprintf(stderr, "\nrtlsdr_set_opt_string(): error parsing harmonic config: valid range 0 .. 16\n"); ret = 1; } } #endif #if ENABLE_VCO_OPTIONS else if (!strncmp(optPart, "vcocmin=", 8)) { int current = atoi(optPart +8); if ( 0 <= current && current <= 7 ) { dev->r82xx_c.vco_curr_min = 7 - current; ret = 0; if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed vcocmin config %d\n", current); } else if (verbose) { fprintf(stderr, "\nrtlsdr_set_opt_string(): error parsing vcocmin config: valid range 0 .. 7\n"); ret = 1; } } else if (!strncmp(optPart, "vcocmax=", 8)) { int current = atoi(optPart +8); if ( 0 <= current && current <= 7 ) { dev->r82xx_c.vco_curr_max = 7 - current; ret = 0; if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed vcocmax config %d\n", current); } else if (verbose) { fprintf(stderr, "\nrtlsdr_set_opt_string(): error parsing vcocmax config: valid range 0 .. 7\n"); ret = 1; } } else if (!strncmp(optPart, "vcoalgo=", 8)) { int algo = atoi(optPart +8); if ( 0 <= algo && algo <= 2 ) { dev->r82xx_c.vco_curr_max = algo; ret = 0; if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed vcoalgo config %d\n", algo); } else if (verbose) { fprintf(stderr, "\nrtlsdr_set_opt_string(): error parsing vcoalgo config: valid range 0 .. 2\n"); ret = 1; } } #endif else if (!strncmp(optPart, "tp=", 3) || !strncmp(optPart, "Tp=", 3) || !strncmp(optPart, "TP=", 3) ) { int gpio_pin_no = atoi(optPart +3); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed bias tee GPIO pin %d\n", gpio_pin_no); dev->biast_gpio_pin_no = gpio_pin_no; } else if (!strncmp(optPart, "t=", 2) || !strncmp(optPart, "T=", 2)) { int on = atoi(optPart +2); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed bias tee %d\n", on); ret = rtlsdr_set_bias_tee(dev, on); } else if (!strncmp(optPart, "softagc=", 8)) { int on = atoi(optPart +8); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed soft agc mode %d\n", on); dev->softagc.softAgcMode = on; dev->softagc.agcState = on ? SOFTSTATE_INIT : SOFTSTATE_OFF; } else if (!strncmp(optPart, "softscantime=", 13)) { float d = atof(optPart +13); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed soft agc scan time %f ms\n", d); dev->softagc.scanTimeMs = d; } else if (!strncmp(optPart, "softdeadtime=", 13)) { float d = atof(optPart +13); if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed soft agc dead time %f ms\n", d); dev->softagc.deadTimeMs = d; } else if (!strcmp(optPart, "softverbose")) { fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed option softverbose for softagc\n"); dev->softagc.verbose = 1; ret = 0; } #ifdef WITH_UDP_SERVER else if (!strncmp(optPart, "port=", 5)) { int udpPortNo = atoi(optPart +5); if ( udpPortNo == 1 ) udpPortNo = 32323; udpPortNo &= 0xffff; if (verbose) fprintf(stderr, "rtlsdr_set_opt_string(): UDP control server port %d\n", udpPortNo); dev->udpPortNo = udpPortNo; } #endif else if (*optPart) { if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): parsed unknown option '%s'\n", optPart); ret = -1; /* unknown option */ } else { if (verbose) fprintf(stderr, "\nrtlsdr_set_opt_string(): skip empty option '%s'\n", optPart); ret = 0; } if (verbose) fprintf(stderr, " application of parsed option returned %d\n", ret); if (ret < 0) retAll = ret; optPart = strtok(NULL, ":,"); } if ( dev->softagc.agcState != SOFTSTATE_OFF ) softagc_init(dev); #ifdef WITH_UDP_SERVER if (dev->udpPortNo && dev->srv_started == 0 && dev->tuner_type==RTLSDR_TUNER_R820T) { /* signal(SIGPIPE, SIG_IGN); */ if(pthread_create(&dev->srv_thread, NULL, srv_server, dev)) { fprintf(stderr, "Error creating thread\n"); } else { dev->srv_started = 1; fprintf(stderr, "UDP server started on port %u\n", dev->udpPortNo); } } #endif free(optStr); return retAll; } const char * rtlsdr_get_ver_id() { return APP_VER_ID " (" __DATE__ ")"; } uint32_t rtlsdr_get_version() { return ((uint32_t)APP_VER_MAJOR << 16 ) | ((uint32_t)APP_VER_MINOR ); } ================================================ FILE: src/rtl_adsb.c ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2012 by Steve Markgraf * Copyright (C) 2012 by Hoernchen * Copyright (C) 2012 by Kyle Keen * Copyright (C) 2012 by Youssef Touil * Copyright (C) 2012 by Ian Gilmour * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #ifndef _WIN32 #include #else #include #include #include #include "getopt/getopt.h" #endif #ifdef NEED_PTHREADS_WORKARROUND #define HAVE_STRUCT_TIMESPEC #endif #include #include #include #include #include "convenience/convenience.h" #include "convenience/rtl_convenience.h" #ifdef _WIN32 #define sleep Sleep #if defined(_MSC_VER) && (_MSC_VER < 1800) #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) #endif #endif #define ADSB_RATE 2000000 #define ADSB_FREQ 1090000000 #define DEFAULT_ASYNC_BUF_NUMBER 12 #define DEFAULT_BUF_LENGTH (16 * 16384) #define AUTO_GAIN -100 #define MESSAGEGO 253 #define OVERWRITE 254 #define BADSAMPLE 255 static pthread_t demod_thread; static pthread_cond_t ready; static pthread_mutex_t ready_m; static volatile int do_exit = 0; static rtlsdr_dev_t *dev = NULL; uint16_t squares[256]; /* todo, bundle these up in a struct */ uint8_t *buffer; /* also abused for uint16_t */ int verbose_output = 0; int short_output = 0; int quality = 10; int allowed_errors = 5; FILE *file; int adsb_frame[14]; #define preamble_len 16 #define long_frame 112 #define short_frame 56 /* signals are not threadsafe by default */ #define safe_cond_signal(n, m) pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m) #define safe_cond_wait(n, m) pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m) void usage(void) { fprintf(stderr, "rtl_adsb, a simple ADS-B decoder\n" "rtl_adsb version %d.%d %s (%s)\n" "rtl-sdr library %d.%d %s\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__, rtlsdr_get_version() >>16, rtlsdr_get_version() & 0xFFFF, rtlsdr_get_ver_id() ); fprintf(stderr, "Usage:\trtl_adsb [-R] [-g gain] [-p ppm] [output file]\n" "\t[-d device_index or serial (default: 0)]\n" "\t[-V verbove output (default: off)]\n" "\t[-S show short frames (default: off)]\n" "\t[-Q quality (0: no sanity checks, 0.5: half bit, 1: one bit (default), 2: two bits)]\n" "\t[-e allowed_errors (default: 5)]\n" "\t[-g tuner_gain (default: automatic)]\n" "\t[-p ppm_error (default: 0)]\n" "\t[-T enable bias-T on GPIO PIN 0 (works for rtl-sdr.com v3 dongles)]\n" "\tfilename (a '-' dumps samples to stdout)\n" "\t (omitting the filename also uses stdout)\n\n" "Streaming with netcat:\n" "\trtl_adsb | netcat -lp 8080\n" "\twhile true; do rtl_adsb | nc -lp 8080; done\n" "Streaming with socat:\n" "\trtl_adsb | socat -u - TCP4:sdrsharp.com:47806\n" "\n"); exit(1); } #ifdef _WIN32 BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dev); return TRUE; } return FALSE; } #else static void sighandler(int signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dev); } #endif void display(int *frame, int len) { int i, df; if (!short_output && len <= short_frame) { return;} df = (frame[0] >> 3) & 0x1f; if (quality == 0 && !(df==11 || df==17 || df==18 || df==19)) { return;} fprintf(file, "*"); for (i=0; i<((len+7)/8); i++) { fprintf(file, "%02x", frame[i]);} fprintf(file, ";\r\n"); if (!verbose_output) { return;} fprintf(file, "DF=%i CA=%i\n", df, frame[0] & 0x07); fprintf(file, "ICAO Address=%06x\n", frame[1] << 16 | frame[2] << 8 | frame[3]); if (len <= short_frame) { return;} fprintf(file, "PI=0x%06x\n", frame[11] << 16 | frame[12] << 8 | frame[13]); fprintf(file, "Type Code=%i S.Type/Ant.=%x\n", (frame[4] >> 3) & 0x1f, frame[4] & 0x07); fprintf(file, "--------------\n"); } int abs8(int x) /* do not subtract 127 from the raw iq, this handles it */ { if (x >= 127) { return x - 127;} return 127 - x; } void squares_precompute(void) /* equiv to abs(x-128) ^ 2 */ { int i, j; // todo, check if this LUT is actually any faster for (i=0; i<256; i++) { j = abs8(i); squares[i] = (uint16_t)(j*j); } } int magnitute(uint8_t *buf, int len) /* takes i/q, changes buf in place (16 bit), returns new len (16 bit) */ { int i; uint16_t *m; for (i=0; i b; bit = c > d; if (quality == 0) { return bit;} if (quality == 5) { if ( bit && bit_p && b > c) { return BADSAMPLE;} if (!bit && !bit_p && b < c) { return BADSAMPLE;} return bit; } if (quality == 10) { if ( bit && bit_p && c > b) { return 1;} if ( bit && !bit_p && d < b) { return 1;} if (!bit && bit_p && d > b) { return 0;} if (!bit && !bit_p && c < b) { return 0;} return BADSAMPLE; } if ( bit && bit_p && c > b && d < a) { return 1;} if ( bit && !bit_p && c > a && d < b) { return 1;} if (!bit && bit_p && c < a && d > b) { return 0;} if (!bit && !bit_p && c < b && d > a) { return 0;} return BADSAMPLE; } static inline uint16_t min16(uint16_t a, uint16_t b) { return ab ? a : b; } static inline int preamble(uint16_t *buf, int i) /* returns 0/1 for preamble at index i */ { int i2; uint16_t low = 0; uint16_t high = 65535; for (i2=0; i2 allowed_errors) { buf[i2] = BADSAMPLE; break; } else { bit = a > b; /* these don't have to match the bit */ a = 0; b = 65535; } } buf[i] = buf[i+1] = OVERWRITE; buf[i2] = bit; } } } void messages(uint16_t *buf, int len) { int i, data_i, index, shift, frame_len; // todo, allow wrap across buffers for (i=0; i 1) { continue;} frame_len = long_frame; data_i = 0; for (index=0; index<14; index++) { adsb_frame[index] = 0;} for(; i= 0 ? r : -r; } ================================================ FILE: src/rtl_app_ver.h.in ================================================ #ifndef __RTL_APP_VER_H #define __RTL_APP_VER_H #define APP_VER_MAJOR @MAJOR_VERSION@ #define APP_VER_MINOR @MINOR_VERSION@ #define APP_VER_ID "github.com/librtlsdr" #define VER_TO_STRING(x) #x #define VAL_VER_TO_STR(x) VER_TO_STRING(x) #endif ================================================ FILE: src/rtl_biast.c ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * rtl_biast, tool to set bias tee gpio output * Copyright (C) 2012 by Steve Markgraf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #ifndef _WIN32 #include #else #include #include "getopt/getopt.h" #endif #include #include #include "convenience/convenience.h" #include "convenience/rtl_convenience.h" static rtlsdr_dev_t *dev = NULL; void usage(void) { fprintf(stderr, "rtl_biast, a tool for switching the RTL-SDR.com\n" "bias tee or any GPIO ON and OFF. Example to activate\n" "the bias tee: rtl_biast -d 0 -b 1\n" "Any GPIO: rtl_biast -d 0 -g 1 -b 1\n" "rtl_biast version %d.%d %s (%s)\n" "rtl-sdr library %d.%d %s\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__, rtlsdr_get_version() >>16, rtlsdr_get_version() & 0xFFFF, rtlsdr_get_ver_id() ); fprintf(stderr, "Usage:\trtl_biast [-options]\n" "\t[-d device_index (default: 0)]\n" "\t[-g GPIO select (default: 0)]\n" "\t[-b set write bias_on (default: 0, in output mode)]\n" "\t[-r read pin (in input mode)]\n" "\t[-w write pin (in output mode)]\n" "\t[-s read all GPIO pins status (0 = write, 1 = read ?? )]\n" "\t[-R read all GPIO pins ?? ]\n"); exit(1); } int main(int argc, char **argv) { int i, r, opt, val; int dev_index = 0; int dev_given = 0; int write_pin_given = 0; int read_pin_given = 0; int read_all_given = 0; int req_status = 0; uint32_t bias_on = 0; int gpio_pin = 0; int device_count; while ((opt = getopt(argc, argv, "d:b:w:g:srRh?")) != -1) { switch (opt) { case 'd': dev_index = verbose_device_search(optarg); dev_given = 1; break; case 'b': case 'w': bias_on = atoi(optarg); write_pin_given = 1; break; case 'g': gpio_pin = atoi(optarg); break; case 'r': read_pin_given = 1; break; case 'R': read_all_given = 1; break; case 's': req_status = 1; break; default: usage(); break; } } if (!dev_given) { dev_index = verbose_device_search("0"); } if (dev_index < 0) { exit(1); } r = rtlsdr_open(&dev, dev_index); if (r < 0) { fprintf(stderr, "error opening device with index %d\n", dev_index); return -r; } if (write_pin_given) { r = rtlsdr_set_bias_tee_gpio(dev, gpio_pin, bias_on); if (r < 0) fprintf(stderr, "error setting value %d on pin %d\n", bias_on, gpio_pin); } else if (read_pin_given) { r = rtlsdr_set_gpio_input(dev, gpio_pin); if (r < 0) fprintf(stderr, "error configuring pin %d to input\n", gpio_pin); r = rtlsdr_get_gpio_bit(dev, gpio_pin, &val); if (r < 0) fprintf(stderr, "error reading value for pin %d\n", gpio_pin); else printf("value %d at pin %d\n", val, gpio_pin); } else if (read_all_given) { r = rtlsdr_get_gpio_byte(dev, &val); if (r < 0) fprintf(stderr, "error reading value for all pins\n"); else { printf("GPIO 0x%02x = bin ", val); for (gpio_pin = 7; gpio_pin >= 4; --gpio_pin) printf("%d", ((val >> gpio_pin) & 1)); printf(" "); for (gpio_pin = 3; gpio_pin >= 0; --gpio_pin) printf("%d", ((val >> gpio_pin) & 1)); printf("\n"); } } else if (req_status) { r = rtlsdr_set_gpio_status(dev, &val); if (r < 0) fprintf(stderr, "error reading status for all pins\n"); else { printf("STATUS 0x%02x = bin ", val); for (gpio_pin = 7; gpio_pin >= 4; --gpio_pin) printf("%d", ((val >> gpio_pin) & 1)); printf(" "); for (gpio_pin = 3; gpio_pin >= 0; --gpio_pin) printf("%d", ((val >> gpio_pin) & 1)); printf("\n"); } } else { usage(); r = -1; } exit: /* * Note - rtlsdr_close() in this tree does not clear the bias tee * GPIO line, so it leaves the bias tee enabled if a client program * doesn't explictly disable it. * * If that behaviour changes then another rtlsdr_close() will be * needed that takes some extension flags, and one of them should * be to either explicitly close the biast or leave it alone. */ rtlsdr_close(dev); return r >= 0 ? r : -r; } ================================================ FILE: src/rtl_eeprom.c ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * rtl_eeprom, EEPROM modification tool * Copyright (C) 2012 by Steve Markgraf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #ifndef _WIN32 #include #else #include #include "getopt/getopt.h" #endif #include #include #define EEPROM_SIZE 256 #define MAX_STR_SIZE 256 #define STR_OFFSET 0x09 static rtlsdr_dev_t *dev = NULL; typedef struct rtlsdr_config { uint16_t vendor_id; uint16_t product_id; char manufacturer[MAX_STR_SIZE]; char product[MAX_STR_SIZE]; char serial[MAX_STR_SIZE]; int have_serial; int enable_ir; int remote_wakeup; } rtlsdr_config_t; void dump_config(rtlsdr_config_t *conf) { fprintf(stderr, "__________________________________________\n"); fprintf(stderr, "Vendor ID:\t\t0x%04x\n", conf->vendor_id); fprintf(stderr, "Product ID:\t\t0x%04x\n", conf->product_id); fprintf(stderr, "Manufacturer:\t\t%s\n", conf->manufacturer); fprintf(stderr, "Product:\t\t%s\n", conf->product); fprintf(stderr, "Serial number:\t\t%s\n", conf->serial); fprintf(stderr, "Serial number enabled:\t"); fprintf(stderr, conf->have_serial ? "yes\n": "no\n"); fprintf(stderr, "IR endpoint enabled:\t"); fprintf(stderr, conf->enable_ir ? "yes\n": "no\n"); fprintf(stderr, "Remote wakeup enabled:\t"); fprintf(stderr, conf->remote_wakeup ? "yes\n": "no\n"); fprintf(stderr, "__________________________________________\n"); } void usage(void) { fprintf(stderr, "rtl_eeprom, an EEPROM programming tool for RTL2832 based SDR-receivers\n" "rtl_eeprom version %d.%d %s (%s)\n" "rtl-sdr library %d.%d %s\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__, rtlsdr_get_version() >>16, rtlsdr_get_version() & 0xFFFF, rtlsdr_get_ver_id() ); fprintf(stderr, "Usage:\trtl_eeprom [-options]\n" "\t[-d device_index (default: 0)]\n" "\t[-m set manufacturer string]\n" "\t[-p set product string]\n" "\t[-M set manufacturer ID (aka vendor ID) in hexadecimal]\n" "\t[-P set product ID in hexadecimal]\n" "\t[-n sets manufacturer and product ID to 0x1209/0x2832]\n" "\t[ as with '-g realtek_sdr']\n" "\t[-s set serial number string]\n" "\t[-i <0,1> disable/enable IR-endpoint]\n" "\t[-g generate default config and write to device]\n" "\t[ can be one of:]\n" "\t[ realtek\t\tRealtek default (as without EEPROM)]\n" "\t[ realtek_oem\t\tRealtek default OEM with EEPROM]\n" "\t[ noxon\t\tTerratec NOXON DAB Stick]\n" "\t[ terratec_black\tTerratec T Stick Black]\n" "\t[ terratec_plus\tTerratec T Stick+ (DVB-T/DAB)]\n" "\t[ realtek_sdr\t\tRealtek SDR - without DVB compatibility]\n" "\t[-w write dumped file to device]\n" "\t[-r dump EEPROM to file]\n" "\t[-h display this help text]\n" "\nUse on your own risk, especially -w, -M and -P!\n"); exit(1); } int get_string_descriptor(int pos, uint8_t *data, char *str) { int len, i, j = 0; len = data[pos]; if (data[pos + 1] != 0x03) fprintf(stderr, "Error: invalid string descriptor!\n"); for (i = 2; i < len; i += 2) str[j++] = data[pos + i]; str[j] = 0x00; return pos + i; } int set_string_descriptor(int pos, uint8_t *data, char *str) { int i = 0, j = 2; if (pos < 0) return -1; data[pos + 1] = 0x03; while (str[i] != 0x00) { if ((pos + j) >= 78) { fprintf(stderr, "Error: string too long, truncated!\n"); return -1; } data[pos + j++] = str[i++]; data[pos + j++] = 0x00; } data[pos] = j; return pos + j; } int parse_eeprom_to_conf(rtlsdr_config_t *conf, uint8_t *dat) { int pos; if ((dat[0] != 0x28) || (dat[1] != 0x32)) fprintf(stderr, "Error: invalid RTL2832 EEPROM header!\n"); conf->vendor_id = dat[2] | (dat[3] << 8); conf->product_id = dat[4] | (dat[5] << 8); conf->have_serial = (dat[6] == 0xa5) ? 1 : 0; conf->remote_wakeup = (dat[7] & 0x01) ? 1 : 0; conf->enable_ir = (dat[7] & 0x02) ? 1 : 0; pos = get_string_descriptor(STR_OFFSET, dat, conf->manufacturer); pos = get_string_descriptor(pos, dat, conf->product); get_string_descriptor(pos, dat, conf->serial); return 0; } int gen_eeprom_from_conf(rtlsdr_config_t *conf, uint8_t *dat) { int pos; dat[0] = 0x28; dat[1] = 0x32; dat[2] = conf->vendor_id & 0xff; dat[3] = (conf->vendor_id >> 8) & 0xff ; dat[4] = conf->product_id & 0xff; dat[5] = (conf->product_id >> 8) & 0xff; dat[6] = conf->have_serial ? 0xa5 : 0x00; dat[7] = 0x14; dat[7] |= conf->remote_wakeup ? 0x01 : 0x00; dat[7] |= conf->enable_ir ? 0x02 : 0x00; dat[8] = 0x02; pos = set_string_descriptor(STR_OFFSET, dat, conf->manufacturer); pos = set_string_descriptor(pos, dat, conf->product); pos = set_string_descriptor(pos, dat, conf->serial); dat[78] = 0x00; /* length of IR config */ return pos; } enum configs { CONF_NONE = 0, REALTEK, REALTEK_EEPROM, TERRATEC_NOXON, TERRATEC_T_BLACK, TERRATEC_T_PLUS, REALTEK_SDR, }; void gen_default_conf(rtlsdr_config_t *conf, int config) { switch (config) { case REALTEK: fprintf(stderr, "Realtek default (as without EEPROM)\n"); conf->vendor_id = 0x0bda; conf->product_id = 0x2832; strcpy(conf->manufacturer, "Generic"); strcpy(conf->product, "RTL2832U DVB-T"); strcpy(conf->serial, "0"); conf->have_serial = 1; conf->enable_ir = 0; conf->remote_wakeup = 1; break; case REALTEK_EEPROM: fprintf(stderr, "Realtek default OEM with EEPROM\n"); conf->vendor_id = 0x0bda; conf->product_id = 0x2838; strcpy(conf->manufacturer, "Realtek"); strcpy(conf->product, "RTL2838UHIDIR"); strcpy(conf->serial, "00000001"); conf->have_serial = 1; conf->enable_ir = 1; conf->remote_wakeup = 0; break; case TERRATEC_NOXON: fprintf(stderr, "Terratec NOXON DAB Stick\n"); conf->vendor_id = 0x0ccd; conf->product_id = 0x00b3; strcpy(conf->manufacturer, "NOXON"); strcpy(conf->product, "DAB Stick"); strcpy(conf->serial, "0"); conf->have_serial = 1; conf->enable_ir = 0; conf->remote_wakeup = 1; break; case TERRATEC_T_BLACK: fprintf(stderr, "Terratec T Stick Black\n"); conf->vendor_id = 0x0ccd; conf->product_id = 0x00a9; strcpy(conf->manufacturer, "Realtek"); strcpy(conf->product, "RTL2838UHIDIR"); strcpy(conf->serial, "00000001"); conf->have_serial = 1; conf->enable_ir = 1; conf->remote_wakeup = 0; break; case TERRATEC_T_PLUS: fprintf(stderr, "Terratec ran T Stick+\n"); conf->vendor_id = 0x0ccd; conf->product_id = 0x00d7; strcpy(conf->manufacturer, "Realtek"); strcpy(conf->product, "RTL2838UHIDIR"); strcpy(conf->serial, "00000001"); conf->have_serial = 1; conf->enable_ir = 1; conf->remote_wakeup = 0; break; case REALTEK_SDR: fprintf(stderr, "Realtek SDR\n"); conf->vendor_id = 0x1209; conf->product_id = 0x2832; strcpy(conf->manufacturer, "Realtek"); strcpy(conf->product, "RTL2832U_SDR"); strcpy(conf->serial, "00000001"); conf->have_serial = 1; conf->enable_ir = 0; conf->remote_wakeup = 0; break; default: break; }; } int main(int argc, char **argv) { int i, r, opt; uint32_t dev_index = 0; int device_count; char *filename = NULL; FILE *file = NULL; char *manuf_str = NULL; char *product_str = NULL; int manuf_id = 0; int product_id = 0; char *serial_str = NULL; uint8_t buf[EEPROM_SIZE]; rtlsdr_config_t conf; int flash_file = 0; int default_config = 0; int change = 0; int ir_endpoint = 0; char ch; while ((opt = getopt(argc, argv, "d:m:p:M:P:ns:i:g:w:r:h?")) != -1) { switch (opt) { case 'd': dev_index = atoi(optarg); break; case 'm': manuf_str = optarg; change = 1; break; case 'p': product_str = optarg; change = 1; break; case 'M': manuf_id = (int)strtol(optarg, NULL, 16); change = 1; break; case 'P': product_id = (int)strtol(optarg, NULL, 16); change = 1; break; case 'n': manuf_id = 0x1209; product_id = 0x2832; change = 1; break; case 's': serial_str = optarg; change = 1; break; case 'i': ir_endpoint = (atoi(optarg) > 0) ? 1 : -1; change = 1; break; case 'g': if (!strcmp(optarg, "realtek")) default_config = REALTEK; else if (!strcmp(optarg, "realtek_oem")) default_config = REALTEK_EEPROM; else if (!strcmp(optarg, "noxon")) default_config = TERRATEC_NOXON; else if (!strcmp(optarg, "terratec_black")) default_config = TERRATEC_T_BLACK; else if (!strcmp(optarg, "terratec_plus")) default_config = TERRATEC_T_PLUS; else if (!strcmp(optarg, "realtek_sdr")) default_config = REALTEK_SDR; if (default_config != CONF_NONE) change = 1; break; case 'w': flash_file = 1; change = 1; /* fall-through */ case 'r': filename = optarg; break; default: usage(); break; } } device_count = rtlsdr_get_device_count(); if (!device_count) { fprintf(stderr, "No supported devices found.\n"); exit(1); } fprintf(stderr, "Found %d device(s):\n", device_count); for (i = 0; i < device_count; i++) fprintf(stderr, " %d: %s\n", i, rtlsdr_get_device_name(i)); fprintf(stderr, "\n"); fprintf(stderr, "Using device %d: %s\n", dev_index, rtlsdr_get_device_name(dev_index)); r = rtlsdr_open(&dev, dev_index); if (r < 0) { fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); exit(1); } fprintf(stderr, "\n"); r = rtlsdr_read_eeprom(dev, buf, 0, EEPROM_SIZE); if (r < 0) { if (r == -3) fprintf(stderr, "No EEPROM has been found.\n"); else fprintf(stderr, "Failed to read EEPROM, err %i.\n", r); goto exit; } if (r < 0) return -1; fprintf(stderr, "Current configuration:\n"); parse_eeprom_to_conf(&conf, buf); dump_config(&conf); if (filename) { file = fopen(filename, flash_file ? "rb" : "wb"); if (!file) { fprintf(stderr, "Error opening file!\n"); goto exit; } if (flash_file) { if (fread(buf, 1, sizeof(buf), file) != sizeof(buf)) fprintf(stderr, "Error reading file!\n"); } else { if (fwrite(buf, 1, sizeof(buf), file) != sizeof(buf)) fprintf(stderr, "Short write, exiting!\n"); else fprintf(stderr, "\nDump to %s successful.\n", filename); } } if (manuf_str) { int len = strlen(manuf_str); if (len > MAX_STR_SIZE) len = MAX_STR_SIZE; memset(&conf.manufacturer[0], 0, MAX_STR_SIZE * sizeof(char)); memcpy(&conf.manufacturer[0], manuf_str, len * sizeof(char)); } if (product_str) { int len = strlen(product_str); if (len > MAX_STR_SIZE) len = MAX_STR_SIZE; memset(&conf.product[0], 0, MAX_STR_SIZE * sizeof(char)); memcpy(&conf.product[0], product_str, len * sizeof(char)); } if (manuf_id > 0) conf.vendor_id = manuf_id; if (product_id > 0) conf.product_id = product_id; if (serial_str) { int len = strlen(serial_str); if (len > MAX_STR_SIZE) len = MAX_STR_SIZE; conf.have_serial = 1; memset(&conf.serial[0], 0, MAX_STR_SIZE * sizeof(char)); memcpy(&conf.serial[0], serial_str, len * sizeof(char)); } if (ir_endpoint != 0) conf.enable_ir = (ir_endpoint > 0) ? 1 : 0; if (!change) goto exit; fprintf(stderr, "\nNew configuration:\n"); if (default_config != CONF_NONE) gen_default_conf(&conf, default_config); if (!flash_file) { if (gen_eeprom_from_conf(&conf, buf) < 0) goto exit; } parse_eeprom_to_conf(&conf, buf); dump_config(&conf); fprintf(stderr, "Write new configuration to device [y/n]? "); while ((ch = getchar())) { if (ch != 'y') goto exit; else break; } r = rtlsdr_write_eeprom(dev, buf, 0, flash_file ? EEPROM_SIZE : 128); if (r < 0) fprintf(stderr, "Error while writing EEPROM: %i\n", r); else fprintf(stderr, "\nConfiguration successfully written.\n" "Please replug the device for changes" " to take effect.\n"); exit: if (file) fclose(file); rtlsdr_close(dev); return r >= 0 ? r : -r; } ================================================ FILE: src/rtl_fm.c ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2012 by Steve Markgraf * Copyright (C) 2012 by Hoernchen * Copyright (C) 2012 by Kyle Keen * Copyright (C) 2013 by Elias Oenal * Copyright (C) 2015 by Hayati Ayguen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * written because people could not do real time * FM demod on Atom hardware with GNU radio * based on rtl_sdr.c and rtl_tcp.c * * lots of locks, but that is okay * (no many-to-many locks) * * todo: * sanity checks * scale squelch to other input parameters * test all the demodulations * pad output on hop * frequency ranges could be stored better * scaled AM demod amplification * auto-hop after time limit * peak detector to tune onto stronger signals * fifo for active hop frequency * clips * noise squelch * merge stereo patch * merge soft agc patch * merge udp patch * testmode to detect overruns * watchdog to reset bad dongle * fix oversampling */ #include #include #include #include #include #include #ifndef _WIN32 #include #else #include #include #include #include "getopt/getopt.h" #define usleep(x) Sleep(x/1000) #if defined(_MSC_VER) && (_MSC_VER < 1900) #define snprintf _snprintf #endif #if defined(_MSC_VER) && (_MSC_VER < 1800) #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) #endif #define _USE_MATH_DEFINES #endif #ifdef NEED_PTHREADS_WORKARROUND #define HAVE_STRUCT_TIMESPEC #endif #include #include #include #include #include #include "convenience/convenience.h" #include "convenience/rtl_convenience.h" #include "convenience/wavewrite.h" #define DEFAULT_SAMPLE_RATE 24000 #define DEFAULT_BUF_LENGTH (1 * 16384) #define MAXIMUM_OVERSAMPLE 16 #define MAXIMUM_BUF_LENGTH (MAXIMUM_OVERSAMPLE * DEFAULT_BUF_LENGTH) #define AUTO_GAIN -100 #define DEFAULT_BUFFER_DUMP 4096 #define FREQUENCIES_LIMIT 1024 static int BufferDump = DEFAULT_BUFFER_DUMP; static int OutputToStdout = 1; static int MinCaptureRate = 1000000; static volatile int do_exit = 0; static int lcm_post[17] = {1,1,1,3,1,5,3,7,1,9,5,11,3,13,7,15,1}; static int ACTUAL_BUF_LENGTH; static int *atan_lut = NULL; static int atan_lut_size = 131072; /* 512 KB */ static int atan_lut_coef = 8; static int verbosity = 0; static int printLevels = 0; static int printLevelNo = 1; static int levelMax = 0; static int levelMaxMax = 0; static double levelSum = 0.0; static int32_t prev_if_band_center_freq = 0; enum trigExpr { crit_IN =0, crit_OUT, crit_LT, crit_GT }; char * aCritStr[] = { "in", "out", "<", ">" }; time_t stop_time; int duration = 0; struct cmd_state { const char * filename; FILE * file; int lineNo; char acLine[4096]; int checkADCmax; int checkADCrms; uint64_t prevFreq; uint64_t freq; int prevGain; uint32_t prevBandwidth; int gain; enum trigExpr trigCrit; double refLevel; double refLevelTol; int numMeas; int numBlockTrigger; char * command; char * args; double levelSum; int numSummed; int omitFirstFreqLevels; int waitTrigger[FREQUENCIES_LIMIT]; int statNumLevels[FREQUENCIES_LIMIT]; uint64_t statFreq[FREQUENCIES_LIMIT]; double statSumLevels[FREQUENCIES_LIMIT]; float statMinLevel[FREQUENCIES_LIMIT]; float statMaxLevel[FREQUENCIES_LIMIT]; }; struct dongle_state { int exit_flag; pthread_t thread; rtlsdr_dev_t *dev; int dev_index; uint64_t userFreq; uint64_t freq; uint32_t rate; uint32_t bandwidth; int bccorner; /* -1 for low band corner, 0 for band center, +1 for high band corner */ int gain; int16_t buf16[MAXIMUM_BUF_LENGTH]; uint32_t buf_len; int ppm_error; int offset_tuning; int direct_sampling; int mute; struct demod_state *demod_target; double samplePowSum; int samplePowCount; unsigned char sampleMax; }; struct demod_state { int exit_flag; pthread_t thread; int16_t lowpassed[MAXIMUM_BUF_LENGTH]; int lp_len; int16_t lp_i_hist[10][6]; int16_t lp_q_hist[10][6]; int16_t result[MAXIMUM_BUF_LENGTH]; int16_t droop_i_hist[9]; int16_t droop_q_hist[9]; int result_len; int rate_in; int rate_out; int rate_out2; int now_r, now_j; int pre_r, pre_j; int prev_index; int downsample; /* min 1, max 256 */ int post_downsample; int output_scale; int squelch_level, conseq_squelch, squelch_hits, terminate_on_squelch; int downsample_passes; int comp_fir_size; int custom_atan; int deemph, deemph_a; int now_lpr; int prev_lpr_index; int dc_block_audio, dc_avg, adc_block_const; int dc_block_raw, dc_avgI, dc_avgQ, rdc_block_const; void (*mode_demod)(struct demod_state*); pthread_rwlock_t rw; pthread_cond_t ready; pthread_mutex_t ready_m; struct output_state *output_target; struct cmd_state *cmd; }; struct output_state { int exit_flag; pthread_t thread; FILE *file; char *filename; char *tempfilename; int16_t result[MAXIMUM_BUF_LENGTH]; int result_len; int rate; pthread_rwlock_t rw; pthread_cond_t ready; pthread_mutex_t ready_m; }; struct controller_state { int exit_flag; pthread_t thread; uint32_t freqs[FREQUENCIES_LIMIT]; int freq_len; int freq_now; int edge; int wb_mode; pthread_cond_t hop; pthread_mutex_t hop_m; struct cmd_state *cmd; }; /* multiple of these, eventually */ struct dongle_state dongle; struct demod_state demod; struct output_state output; struct controller_state controller; struct cmd_state cmd; void usage(void) { fprintf(stderr, "rtl_fm, a simple demodulator for RTL2832 based SDR-receivers\n" "rtl_fm version %d.%d %s (%s)\n" "rtl-sdr library %d.%d %s\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__, rtlsdr_get_version() >>16, rtlsdr_get_version() & 0xFFFF, rtlsdr_get_ver_id() ); fprintf(stderr, "Usage:\trtl_fm -f freq [-options] [filename]\n" "\t-f frequency_to_tune_to [Hz]\n" "\t use multiple -f for scanning (requires squelch)\n" "\t ranges supported, -f 118M:137M:25k\n" "\t[-C command_filename: command file with comma seperated values (.csv). sets modulation 'raw']\n" "\t\tcommand file contains lines with: freq,gain,trig-crit,trig_level,trig_tolerance,#meas,#blocks,trigger_command,arguments\n" "\t\t with trig_crit one of 'in', 'out', 'lt' or 'gt'\n" "\t[-B num_samples at capture rate: remove that many samples at capture_rate after changing frequency (default: 4096)]\n" "\t[-m minimum_capture_rate Hz (default: 1m, min=900k, max=3.2m)]\n" "\t[-v increase verbosity (default: 0)]\n" "\t[-M modulation (default: fm)]\n" "\t fm or nbfm or nfm, wbfm or wfm, raw or iq, am, usb, lsb\n" "\t wbfm == -M fm -s 170k -o 4 -A fast -r 32k -l 0 -E deemp\n" "\t raw mode outputs 2x16 bit IQ pairs\n" "\t[-s sample_rate (default: 24k)]\n" "\t[-d device_index or serial (default: 0)]\n" "\t[-T enable bias-T on GPIO PIN 0 (works for rtl-sdr.com v3 dongles)]\n" "\t[-D direct_sampling_mode (default: 0, 1 = I, 2 = Q, 3 = I below threshold, 4 = Q below threshold)]\n" "\t[-D direct_sampling_threshold_frequency (default: 0 use tuner specific frequency threshold for 3 and 4)]\n" "\t[-g tuner_gain (default: automatic)]\n" "\t[-w tuner_bandwidth in Hz (default: automatic)]\n" "\t[-W length of single buffer in units of 512 samples (default: 32 was 256)]\n" "\t[-l squelch_level (default: 0/off)]\n" "\t[-L N prints levels every N calculations]\n" "\t output are comma separated values (csv):\n" "\t avg rms since last output, max rms since last output, overall max rms, squelch (paramed), rms, rms level, avg rms level\n" "\t[-c de-emphasis_time_constant in us for wbfm. 'us' or 'eu' for 75/50 us (default: us)]\n" #if 0 "\t for fm squelch is inverted\n" #endif "\t[-o oversampling (default: 1, 4 recommended)]\n" "\t[-p ppm_error (default: 0)]\n" "\t[-R run_seconds] specify number of seconds to run\n" "\t[-E enable_option (default: none)]\n" "\t use multiple -E to enable multiple options\n" "\t edge: enable lower edge tuning\n" "\t rdc: enable dc blocking filter on raw I/Q data at capture rate\n" "\t adc: enable dc blocking filter on demodulated audio\n" "\t dc: same as adc\n" "\t rtlagc: enable rtl2832's digital agc (default: off)\n" "\t agc: same as rtlagc\n" "\t deemp: enable de-emphasis filter\n" "\t direct: enable direct sampling (bypasses tuner, uses rtl2832 xtal)\n" "\t offset: enable offset tuning (only e4000 tuner)\n" "\t bcc: use tuner bandwidths center as band center (default)\n" "\t bclo: use tuner bandwidths low corner as band center\n" "\t bchi: use tuner bandwidths high corner as band center\n" "%s" "\t[-q dc_avg_factor for option rdc (default: 9)]\n" "\t[-n disables demodulation output to stdout/file]\n" "\t[-H write wave Header to file (default: off)]\n" "\t limitation: only 1st tuned frequency will be written into the header!\n" "\tfilename ('-' means stdout)\n" "\t omitting the filename also uses stdout\n\n" "Experimental options:\n" "\t[-r resample_rate (default: none / same as -s)]\n" "\t[-t squelch_delay (default: 10)]\n" "\t +values will mute/scan, -values will exit\n" "\t[-F fir_size (default: off)]\n" "\t enables low-leakage downsample filter\n" "\t size can be 0 or 9. 0 has bad roll off\n" "\t[-A std/fast/lut/ale choose atan math (default: std)]\n" #if 0 "\t[-C clip_path (default: off)\n" "\t (create time stamped raw clips, requires squelch)\n" "\t (path must have '\%s' and will expand to date_time_freq)\n" "\t[-H hop_fifo (default: off)\n" "\t (fifo will contain the active frequency)\n" #endif "\n" "Produces signed 16 bit ints, use Sox or aplay to hear them.\n" "\trtl_fm ... | play -t raw -r 24k -es -b 16 -c 1 -V1 -\n" "\t | aplay -r 24000 -f S16_LE -t raw -c 1\n" "\t -M wbfm | play -r 32k ... \n" "\t -s 22050 | multimon -t raw /dev/stdin\n\n" , rtlsdr_get_opt_help(1) ); exit(1); } #ifdef _WIN32 BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dongle.dev); return TRUE; } return FALSE; } #else static void sighandler(int signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dongle.dev); } #endif /* more cond dumbness */ #define safe_cond_signal(n, m) do { pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m); } while (0) #define safe_cond_wait(n, m) do { pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m); } while (0) /* {length, coef, coef, coef} and scaled by 2^15 for now, only length 9, optimal way to get +85% bandwidth */ #define CIC_TABLE_MAX 10 int cic_9_tables[][10] = { {0,}, {9, -156, -97, 2798, -15489, 61019, -15489, 2798, -97, -156}, {9, -128, -568, 5593, -24125, 74126, -24125, 5593, -568, -128}, {9, -129, -639, 6187, -26281, 77511, -26281, 6187, -639, -129}, {9, -122, -612, 6082, -26353, 77818, -26353, 6082, -612, -122}, {9, -120, -602, 6015, -26269, 77757, -26269, 6015, -602, -120}, {9, -120, -582, 5951, -26128, 77542, -26128, 5951, -582, -120}, {9, -119, -580, 5931, -26094, 77505, -26094, 5931, -580, -119}, {9, -119, -578, 5921, -26077, 77484, -26077, 5921, -578, -119}, {9, -119, -577, 5917, -26067, 77473, -26067, 5917, -577, -119}, {9, -199, -362, 5303, -25505, 77489, -25505, 5303, -362, -199}, }; #if defined(_MSC_VER) && (_MSC_VER < 1800) double log2(double n) { return log(n) / log(2.0); } #endif /* uint8_t negation = 255 - x */ #define NEG_U8( x ) ( 255 - x ) /* MUL_PLUS_J: (a + j*b ) * j = -b + j * a */ /* MUL_MINUS_ONE: (a + j*b ) * -1 = -a + j * -b */ /* MUL_MINUS_J: (a + j*b ) * -j = b + j * -a */ #define MUL_PLUS_J_U8( X, J ) \ tmp = X[J]; \ X[J] = NEG_U8( X[J+1] ); \ X[J+1] = tmp #define MUL_MINUS_ONE_U8( X, J ) \ X[J] = NEG_U8( X[J] ); \ X[J+1] = NEG_U8( X[J+1] ) #define MUL_MINUS_J_U8( X, J ) \ tmp = X[J]; \ X[J] = X[J+1]; \ X[J+1] = NEG_U8( tmp ) #define MUL_PLUS_J_INT( X, J ) \ tmp = X[J]; \ X[J] = - X[J+1]; \ X[J+1] = tmp #define MUL_MINUS_ONE_INT( X, J ) \ X[J] = - X[J]; \ X[J+1] = - X[J+1] #define MUL_MINUS_J_INT( X, J ) \ tmp = X[J]; \ X[J] = X[J+1]; \ X[J+1] = -tmp void rotate16_90(int16_t *buf, uint32_t len) { /* 90 degree rotation is 1, +j, -1, -j */ uint32_t i; int16_t tmp; for (i=0; ilp_len) { d->now_r += d->lowpassed[i]; d->now_j += d->lowpassed[i+1]; i += 2; d->prev_index++; if (d->prev_index < d->downsample) { continue; } d->lowpassed[i2] = d->now_r; /* * d->output_scale; */ d->lowpassed[i2+1] = d->now_j; /* * d->output_scale; */ d->prev_index = 0; d->now_r = 0; d->now_j = 0; i2 += 2; } d->lp_len = i2; } static char * trim(char * s) { char *p = s; int l = strlen(p); while(isspace(p[l - 1])) p[--l] = 0; while(*p && isspace(*p)) ++p; return p; } static void cmd_init(struct cmd_state *c) { int k; c->filename = NULL; c->file = NULL; c->lineNo = 1; c->checkADCmax = 0; c->checkADCrms = 0; c->prevFreq = -1; c->prevGain = -200; c->prevBandwidth = -1; c->freq = 0; c->gain = 0; c->trigCrit = crit_IN; c->refLevel = 0.0; c->refLevelTol = 0.0; c->numMeas = 0; c->numBlockTrigger = 0; c->command = NULL; c->args = NULL; c->levelSum = 0.0; c->numSummed = 0; c->omitFirstFreqLevels = 3; for (k = 0; k < FREQUENCIES_LIMIT; k++) { c->waitTrigger[k] = 0; c->statNumLevels[k] = 0; c->statFreq[k] = 0; c->statSumLevels[k] = 0.0; c->statMinLevel[k] = 0.0F; c->statMaxLevel[k] = 0.0F; } } static int toNextCmdLine(struct cmd_state *c) { const char * delim = ","; char * pLine = NULL; char * pCmdFreq = NULL; char * pCmdGain = NULL; char * pCmdTrigCrit = NULL; char * pCmdLevel = NULL; char * pCmdTol = NULL; char * pCmdNumMeas = NULL; char * pCmdNumBlockTrigger = NULL; int numValidLines = 1; /* assume valid lines */ while (1) { if (c->file && feof(c->file)) { if (!numValidLines) { fprintf(stderr, "error: command file '%s' does not contain any valid lines!\n", c->filename); return 0; } fclose(c->file); c->file = NULL; } if (!c->file) { c->file = fopen(c->filename, "r"); numValidLines = 0; c->lineNo = 0; } if (!c->file) return 0; pLine = fgets(c->acLine, 4096, c->file); if (!pLine) { continue; } c->lineNo++; pLine = trim(c->acLine); if (pLine[0]=='#' || pLine[0]==0) continue; /* detect comment lines and empty lines */ pCmdFreq = strtok(pLine, delim); if (!pCmdFreq) { fprintf(stderr, "error parsing frequency in line %d of command file!\n", c->lineNo); continue; } pCmdFreq = trim(pCmdFreq); /* check keywords */ if (!strcmp(pCmdFreq, "adc") || !strcmp(pCmdFreq, "adcmax")) { c->checkADCmax = 1; continue; } else if (!strcmp(pCmdFreq, "adcrms")) { c->checkADCrms = 1; continue; } c->freq = (uint64_t)atofs(pCmdFreq); pCmdGain = strtok(NULL, delim); if (!pCmdGain) { fprintf(stderr, "error parsing gain in line %d of command file!\n", c->lineNo); continue; } pCmdGain = trim(pCmdGain); if (!strcmp(pCmdGain,"auto") || !strcmp(pCmdGain,"a")) c->gain = AUTO_GAIN; else c->gain = (int)(atof(pCmdGain) * 10); pCmdTrigCrit = strtok(NULL, delim); if (!pCmdTrigCrit) { fprintf(stderr, "error parsing expr in line %d of command file!\n", c->lineNo); continue; } pCmdTrigCrit = trim(pCmdTrigCrit); if (!strcmp(pCmdTrigCrit,"in")) c->trigCrit = crit_IN; else if (!strcmp(pCmdTrigCrit,"==")) c->trigCrit = crit_IN; else if (!strcmp(pCmdTrigCrit,"out")) c->trigCrit = crit_OUT; else if (!strcmp(pCmdTrigCrit,"!=")) c->trigCrit = crit_OUT; else if (!strcmp(pCmdTrigCrit,"<>")) c->trigCrit = crit_OUT; else if (!strcmp(pCmdTrigCrit,"lt")) c->trigCrit = crit_LT; else if (!strcmp(pCmdTrigCrit,"<")) c->trigCrit = crit_LT; else if (!strcmp(pCmdTrigCrit,"gt")) c->trigCrit = crit_GT; else if (!strcmp(pCmdTrigCrit,">")) c->trigCrit = crit_GT; else { fprintf(stderr, "error parsing expr in line %d of command file!\n", c->lineNo); continue; } pCmdLevel = strtok(NULL, delim); if (!pCmdLevel) { fprintf(stderr, "error parsing level in line %d of command file!\n", c->lineNo); continue; } c->refLevel = atof(trim(pCmdLevel)); pCmdTol = strtok(NULL, delim); if (!pCmdTol) { fprintf(stderr, "error parsing tolerance in line %d of command file!\n", c->lineNo); continue; } c->refLevelTol = atof(trim(pCmdTol)); pCmdNumMeas = strtok(NULL, delim); if (!pCmdNumMeas) { fprintf(stderr, "error parsing #measurements in line %d of command file!\n", c->lineNo); continue; } c->numMeas = atoi(trim(pCmdNumMeas)); if (c->numMeas <= 0) { fprintf(stderr, "warning: fixed #measurements from %d to 10 in line %d of command file!\n", c->numMeas, c->lineNo); c->numMeas=10; } pCmdNumBlockTrigger = strtok(NULL, delim); if (!pCmdNumBlockTrigger) { fprintf(stderr, "error parsing #blockTrigger in line %d of command file!\n", c->lineNo); continue; } c->numBlockTrigger = atoi(trim(pCmdNumBlockTrigger)); c->command = strtok(NULL, delim); /* no check: allow empty string. just trim it */ if (c->command) c->command = trim(c->command); c->args = strtok(NULL, delim); /* no check: allow empty string. just trim it */ if (c->args) c->args = trim(c->args); if (verbosity >= 2) fprintf(stderr, "read from cmd file: freq %.3f kHz, gain %0.1f dB, level %s {%.1f +/- %.1f}, cmd '%s %s'\n", c->freq /1000.0, c->gain /10.0, aCritStr[c->trigCrit], c->refLevel, c->refLevelTol, (c->command ? c->command : "%"), (c->args ? c->args : "") ); numValidLines++; return 1; } return 0; } static int testTrigCrit(struct cmd_state *c, double level) { switch(c->trigCrit) { case crit_IN: return ( c->refLevel-c->refLevelTol <= level && level <= c->refLevel+c->refLevelTol ); case crit_OUT: return ( c->refLevel-c->refLevelTol > level || level > c->refLevel+c->refLevelTol ); case crit_LT: return ( level < c->refLevel-c->refLevelTol ); case crit_GT: return ( level > c->refLevel+c->refLevelTol ); } return 0; } static void checkTriggerCommand(struct cmd_state *c, unsigned char adcSampleMax, double powerSum, int powerCount ) { char acRepFreq[32], acRepGain[32], acRepMLevel[32], acRepRefLevel[32], acRepRefTolerance[32]; char * execSearchStrings[7] = { "!freq!", "!gain!", "!mlevel!", "!crit!", "!reflevel!", "!reftol!", NULL }; char * execReplaceStrings[7] = { acRepFreq, acRepGain, acRepMLevel, NULL, acRepRefLevel, acRepRefTolerance, NULL }; double triggerLevel; double adcRms = 0.0; int k, triggerCommand = 0; int adcMax = (int)adcSampleMax - 127; char adcText[128]; if (c->numSummed != c->numMeas) return; if (c->omitFirstFreqLevels) { /* workaround: measured levels of first controlled frequency looks wrong! */ c->omitFirstFreqLevels--; return; } /* decrease all counters */ for ( k = 0; k < FREQUENCIES_LIMIT; k++ ) { if ( c->waitTrigger[k] > 0 ) { c->waitTrigger[k] -= c->numMeas; if ( c->waitTrigger[k] < 0 ) c->waitTrigger[k] = 0; } } triggerLevel = 20.0 * log10( 1E-10 + c->levelSum / c->numSummed ); triggerCommand = testTrigCrit(c, triggerLevel); /* update statistics */ if ( c->lineNo < FREQUENCIES_LIMIT ) { if ( c->statNumLevels[c->lineNo] == 0 ) { ++c->statNumLevels[c->lineNo]; c->statFreq[c->lineNo] = c->freq; c->statSumLevels[c->lineNo] = triggerLevel; c->statMinLevel[c->lineNo] = (float)triggerLevel; c->statMaxLevel[c->lineNo] = (float)triggerLevel; } else if ( c->statFreq[c->lineNo] == c->freq ) { ++c->statNumLevels[c->lineNo]; c->statSumLevels[c->lineNo] += triggerLevel; if ( c->statMinLevel[c->lineNo] > (float)triggerLevel ) c->statMinLevel[c->lineNo] = (float)triggerLevel; if ( c->statMaxLevel[c->lineNo] < (float)triggerLevel ) c->statMaxLevel[c->lineNo] = (float)triggerLevel; } } adcText[0] = 0; if (c->checkADCmax && c->checkADCrms) { adcRms = (powerCount >0) ? sqrt( powerSum / powerCount ) : -1.0; sprintf(adcText, "adc max %3d%s rms %5.1f ", adcMax, (adcMax>=64 ? (adcMax>=120 ? "!!" : "! " ) : " "), adcRms ); } else if (c->checkADCmax) sprintf(adcText, "adc max %3d%s ", adcMax, (adcMax>=64 ? (adcMax>=120 ? "!!" : "! " ) : " ") ); else if (c->checkADCrms) { adcRms = (powerCount >0) ? sqrt( powerSum / powerCount ) : -1.0; sprintf(adcText, "adc rms %5.1f ", adcRms ); } if ( c->lineNo < FREQUENCIES_LIMIT && c->waitTrigger[c->lineNo] <= 0 ) { c->waitTrigger[c->lineNo] = triggerCommand ? c->numBlockTrigger : 0; if (verbosity) fprintf(stderr, "%.3f kHz: gain %4.1f + level %4.1f dB %s=> %s\n", (double)c->freq /1000.0, 0.1*c->gain, triggerLevel, adcText, (triggerCommand ? "activates trigger" : "does not trigger") ); if (triggerCommand && c->command && c->command[0]) { fprintf(stderr, "command to trigger is '%s %s'\n", c->command, c->args); /* prepare search/replace of special parameters for command arguments */ snprintf(acRepFreq, 32, "%.0f", (double)c->freq); snprintf(acRepGain, 32, "%d", c->gain); snprintf(acRepMLevel, 32, "%d", (int)(0.5 + triggerLevel*10.0) ); execReplaceStrings[3] = aCritStr[c->trigCrit]; snprintf(acRepRefLevel, 32, "%d", (int)(0.5 + c->refLevel*10.0) ); snprintf(acRepRefTolerance, 32, "%d", (int)(0.5 + c->refLevelTol*10.0) ); executeInBackground( c->command, c->args, execSearchStrings, execReplaceStrings ); } } else if (verbosity) { fprintf(stderr, "%.3f kHz: gain %4.1f + level %4.1f dB %s=> %s, blocks for %d\n", (double)c->freq /1000.0, 0.1*c->gain, triggerLevel, adcText, (triggerCommand ? "would trigger" : "does not trigger"), (c->lineNo < FREQUENCIES_LIMIT ? c->waitTrigger[c->lineNo] : -1 ) ); } c->numSummed++; } int low_pass_simple(int16_t *signal2, int len, int step) /* no wrap around, length must be multiple of step */ { int i, i2, sum; for(i=0; i < len; i+=step) { sum = 0; for(i2=0; i2rate_out; int slow = s->rate_out2; while (i < s->result_len) { s->now_lpr += s->result[i]; i++; s->prev_lpr_index += slow; if (s->prev_lpr_index < fast) { continue; } s->result[i2] = (int16_t)(s->now_lpr / (fast/slow)); s->prev_lpr_index -= fast; s->now_lpr = 0; i2 += 1; } s->result_len = i2; } void fifth_order(int16_t *data, int length, int16_t *hist) /* for half of interleaved data */ { int i; int16_t a, b, c, d, e, f; a = hist[1]; b = hist[2]; c = hist[3]; d = hist[4]; e = hist[5]; f = data[0]; /* a downsample should improve resolution, so don't fully shift */ data[0] = (a + (b+e)*5 + (c+d)*10 + f) >> 4; for (i=4; i> 4; } /* archive */ hist[0] = a; hist[1] = b; hist[2] = c; hist[3] = d; hist[4] = e; hist[5] = f; } void generic_fir(int16_t *data, int length, int *fir, int16_t *hist) /* Okay, not at all generic. Assumes length 9, fix that eventually. */ { int d, temp, sum; for (d=0; d> 15 ; hist[0] = hist[1]; hist[1] = hist[2]; hist[2] = hist[3]; hist[3] = hist[4]; hist[4] = hist[5]; hist[5] = hist[6]; hist[6] = hist[7]; hist[7] = hist[8]; hist[8] = temp; } } /* define our own complex math ops because ARMv5 has no hardware float */ void multiply(int ar, int aj, int br, int bj, int *cr, int *cj) { *cr = ar*br - aj*bj; *cj = aj*br + ar*bj; } int polar_discriminant(int ar, int aj, int br, int bj) { int cr, cj; double angle; multiply(ar, aj, br, -bj, &cr, &cj); angle = atan2((double)cj, (double)cr); return (int)(angle / 3.14159 * (1<<14)); } int fast_atan2(int y, int x) /* pre scaled for int16 */ { int yabs, angle; int pi4=(1<<12), pi34=3*(1<<12); /* note pi = 1<<14 */ if (x==0 && y==0) { return 0; } yabs = y; if (yabs < 0) { yabs = -yabs; } if (x >= 0) { angle = pi4 - pi4 * (x-yabs) / (x+yabs); } else { angle = pi34 - pi4 * (x+yabs) / (yabs-x); } if (y < 0) { return -angle; } return angle; } int polar_disc_fast(int ar, int aj, int br, int bj) { int cr, cj; multiply(ar, aj, br, -bj, &cr, &cj); return fast_atan2(cj, cr); } int atan_lut_init(void) { int i = 0; atan_lut = malloc(atan_lut_size * sizeof(int)); for (i = 0; i < atan_lut_size; i++) { atan_lut[i] = (int) (atan((double) i / (1< 0) {return 1 << 13;} if (cr == 0 && cj < 0) {return -(1 << 13);} if (cj == 0 && cr > 0) {return 0;} if (cj == 0 && cr < 0) {return 1 << 14;} } /* real range -32768 - 32768 use 64x range -> absolute maximum: 2097152 */ x = (cj << atan_lut_coef) / cr; x_abs = abs(x); if (x_abs >= atan_lut_size) { /* we can use linear range, but it is not necessary */ return (cj > 0) ? 1<<13 : -(1<<13); } if (x > 0) { return (cj > 0) ? atan_lut[x] : atan_lut[x] - (1<<14); } else { return (cj > 0) ? (1<<14) - atan_lut[-x] : -atan_lut[-x]; } return 0; } int esbensen(int ar, int aj, int br, int bj) /* input signal: s(t) = a*exp(-i*w*t+p) a = amplitude, w = angular freq, p = phase difference solve w s' = -i(w)*a*exp(-i*w*t+p) s'*conj(s) = -i*w*a*a s'*conj(s) / |s|^2 = -i*w */ { int cj, dr, dj; int scaled_pi = 2608; /* 1<<14 / (2*pi) */ dr = (br - ar) * 2; dj = (bj - aj) * 2; cj = bj*dr - br*dj; /* imag(ds*conj(s)) */ return (scaled_pi * cj / (ar*ar+aj*aj+1)); } void fm_demod(struct demod_state *fm) { int i, pcm; int16_t *lp = fm->lowpassed; pcm = polar_discriminant(lp[0], lp[1], fm->pre_r, fm->pre_j); fm->result[0] = (int16_t)pcm; for (i = 2; i < (fm->lp_len-1); i += 2) { switch (fm->custom_atan) { case 0: pcm = polar_discriminant(lp[i], lp[i+1], lp[i-2], lp[i-1]); break; case 1: pcm = polar_disc_fast(lp[i], lp[i+1], lp[i-2], lp[i-1]); break; case 2: pcm = polar_disc_lut(lp[i], lp[i+1], lp[i-2], lp[i-1]); break; case 3: pcm = esbensen(lp[i], lp[i+1], lp[i-2], lp[i-1]); break; } fm->result[i/2] = (int16_t)pcm; } fm->pre_r = lp[fm->lp_len - 2]; fm->pre_j = lp[fm->lp_len - 1]; fm->result_len = fm->lp_len/2; } void am_demod(struct demod_state *fm) /* todo, fix this extreme laziness */ { int i, pcm; int16_t *lp = fm->lowpassed; int16_t *r = fm->result; for (i = 0; i < fm->lp_len; i += 2) { /* hypot uses floats but won't overflow * r[i/2] = (int16_t)hypot(lp[i], lp[i+1]); */ pcm = lp[i] * lp[i]; pcm += lp[i+1] * lp[i+1]; r[i/2] = (int16_t)sqrt(pcm) * fm->output_scale; } fm->result_len = fm->lp_len/2; /* lowpass? (3khz) highpass? (dc) */ } void usb_demod(struct demod_state *fm) { int i, pcm; int16_t *lp = fm->lowpassed; int16_t *r = fm->result; for (i = 0; i < fm->lp_len; i += 2) { pcm = lp[i] + lp[i+1]; r[i/2] = (int16_t)pcm * fm->output_scale; } fm->result_len = fm->lp_len/2; } void lsb_demod(struct demod_state *fm) { int i, pcm; int16_t *lp = fm->lowpassed; int16_t *r = fm->result; for (i = 0; i < fm->lp_len; i += 2) { pcm = lp[i] - lp[i+1]; r[i/2] = (int16_t)pcm * fm->output_scale; } fm->result_len = fm->lp_len/2; } void raw_demod(struct demod_state *fm) { int i; for (i = 0; i < fm->lp_len; i++) { fm->result[i] = (int16_t)fm->lowpassed[i]; } fm->result_len = fm->lp_len; } void deemph_filter(struct demod_state *fm) { static int avg; /* cheating... */ int i, d; /* de-emph IIR * avg = avg * (1 - alpha) + sample * alpha; */ for (i = 0; i < fm->result_len; i++) { d = fm->result[i] - avg; if (d > 0) { avg += (d + fm->deemph_a/2) / fm->deemph_a; } else { avg += (d - fm->deemph_a/2) / fm->deemph_a; } fm->result[i] = (int16_t)avg; } } void dc_block_audio_filter(struct demod_state *fm) { int i, avg; int64_t sum = 0; for (i=0; i < fm->result_len; i++) { sum += fm->result[i]; } avg = sum / fm->result_len; avg = (avg + fm->dc_avg * fm->adc_block_const) / ( fm->adc_block_const + 1 ); for (i=0; i < fm->result_len; i++) { fm->result[i] -= avg; } fm->dc_avg = avg; } void dc_block_raw_filter(struct demod_state *fm, int16_t *buf, int len) { /* derived from dc_block_audio_filter, running over the raw I/Q components */ int i, avgI, avgQ; int64_t sumI = 0; int64_t sumQ = 0; for (i = 0; i < len; i += 2) { sumI += buf[i]; sumQ += buf[i+1]; } avgI = sumI / ( len / 2 ); avgQ = sumQ / ( len / 2 ); avgI = (avgI + fm->dc_avgI * fm->rdc_block_const) / ( fm->rdc_block_const + 1 ); avgQ = (avgQ + fm->dc_avgQ * fm->rdc_block_const) / ( fm->rdc_block_const + 1 ); for (i = 0; i < len; i += 2) { buf[i] -= avgI; buf[i+1] -= avgQ; } fm->dc_avgI = avgI; fm->dc_avgQ = avgQ; } int mad(int16_t *samples, int len, int step) /* mean average deviation */ { int i=0, sum=0, ave=0; if (len == 0) {return 0;} for (i=0; i step * 32768) /* 8 bit squared = 16 bit. limit to 2^16 for 32 bit squared sum */ ++step; /* increase step to prevent overflow */ for (i=0; i len2) { tick -= len2; i++; } if (i >= len1) { i = len1 - 1; tick = len2; } } } void arbitrary_downsample(int16_t *buf1, int16_t *buf2, int len1, int len2) /* fractional boxcar lowpass, len1 > len2 */ { int i = 1; int j = 0; int tick = 0; double remainder = 0; double frac; /* use integers... */ buf2[0] = 0; while (j < len2) { frac = 1.0; if ((tick + len2) > len1) { frac = (double)(len1 - tick) / (double)len2;} buf2[j] += (int16_t)((double)buf1[i] * frac + remainder); remainder = (double)buf1[i] * (1.0-frac); tick += len2; i++; if (tick > len1) { j++; buf2[j] = 0; tick -= len1; } if (i >= len1) { i = len1 - 1; tick = len1; } } for (j=0; jcmd; double freqK, avgRms, rmsLevel, avgRmsLevel; int i, ds_p; int sr = 0; static int printBlockLen = 1; ds_p = d->downsample_passes; if (ds_p) { for (i=0; i < ds_p; i++) { fifth_order(d->lowpassed, (d->lp_len >> i), d->lp_i_hist[i]); fifth_order(d->lowpassed+1, (d->lp_len >> i) - 1, d->lp_q_hist[i]); } d->lp_len = d->lp_len >> ds_p; /* droop compensation */ if (d->comp_fir_size == 9 && ds_p <= CIC_TABLE_MAX) { generic_fir(d->lowpassed, d->lp_len, cic_9_tables[ds_p], d->droop_i_hist); generic_fir(d->lowpassed+1, d->lp_len-1, cic_9_tables[ds_p], d->droop_q_hist); } } else { low_pass(d); } /* power squelch */ if (d->squelch_level) { sr = rms(d->lowpassed, d->lp_len, 1, d->dc_block_raw); if (sr >= 0) { if (sr < d->squelch_level) { d->squelch_hits++; for (i=0; ilp_len; i++) { d->lowpassed[i] = 0; } } else { d->squelch_hits = 0;} } } if (printLevels) { if (!sr) sr = rms(d->lowpassed, d->lp_len, 1, d->dc_block_raw); --printLevelNo; if (printLevels && sr >= 0) { levelSum += sr; if (levelMax < sr) levelMax = sr; if (levelMaxMax < sr) levelMaxMax = sr; if (!printLevelNo) { printLevelNo = printLevels; freqK = dongle.userFreq /1000.0; avgRms = levelSum / printLevels; rmsLevel = 20.0 * log10( 1E-10 + sr ); avgRmsLevel = 20.0 * log10( 1E-10 + avgRms ); fprintf(stderr, "%.3f kHz, %.1f avg rms, %d max rms, %d max max rms, %d squelch rms, %d rms, %.1f dB rms level, %.2f dB avg rms level\n", freqK, avgRms, levelMax, levelMaxMax, d->squelch_level, sr, rmsLevel, avgRmsLevel ); levelMax = 0; levelSum = 0; } } } if (c->filename) { if (!sr) sr = rms(d->lowpassed, d->lp_len, 1, d->dc_block_raw); if ( printBlockLen && verbosity ) { fprintf(stderr, "block length for rms after decimation is %d samples\n", d->lp_len); if ( d->lp_len < 128 ) fprintf(stderr, "\n WARNING: increase block length with option -W\n\n"); --printBlockLen; } if (!c->numSummed) c->levelSum = 0; if (c->numSummed < c->numMeas && sr >= 0) { c->levelSum += sr; c->numSummed++; } } d->mode_demod(d); /* lowpassed -> result */ if (d->mode_demod == &raw_demod) { return; } /* todo, fm noise squelch */ /* use nicer filter here too? */ if (d->post_downsample > 1) { d->result_len = low_pass_simple(d->result, d->result_len, d->post_downsample);} if (d->deemph) { deemph_filter(d);} if (d->dc_block_audio) { dc_block_audio_filter(d);} if (d->rate_out2 > 0) { low_pass_real(d); /* arbitrary_resample(d->result, d->result, d->result_len, d->result_len * d->rate_out2 / d->rate_out); */ } } static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) { struct dongle_state *s = ctx; struct demod_state *d = s->demod_target; struct cmd_state *c = d->cmd; int i, muteLen = s->mute; unsigned char sampleMax; uint32_t sampleP, samplePowSum = 0.0; int samplePowCount = 0, step = 2; time_t rawtime; if (do_exit) { return;} if (!ctx) { return;} time(&rawtime); if (duration > 0 && rawtime >= stop_time) { do_exit = 1; fprintf(stderr, "Time expired, exiting!\n"); rtlsdr_cancel_async(dongle.dev); } if (s->mute) { if(muteLen > (int)len) muteLen = len; s->mute -= muteLen; /* we may need to mute multiple blocks */ if(!c->filename) { for (i=0; isamplePowSum = 0.0; s->samplePowCount = 0; s->sampleMax = 0; } /* OR all samples to allow checking overflow * - before conversion to 16 bit and before DC filtering. * we only get bitmask of positive samples (after -127) but that won't matter */ if (c->checkADCmax ) { sampleMax = s->sampleMax; for (i=0; i<(int)len; i++) { if ( buf[i] > sampleMax ) sampleMax = buf[i]; } s->sampleMax = sampleMax; } if (c->checkADCrms ) { while ( (int)len >= 16384 * step ) step += 2; for (i=0; i<(int)len; i+= step) { sampleP = ( (int)buf[i] -127 ) * ( (int)buf[i] -127 ); /* I^2 */ sampleP += ( (int)buf[i+1] -127 ) * ( (int)buf[i+1] -127 ); /* Q^2 */ samplePowSum += sampleP; ++samplePowCount; } s->samplePowSum += (double)samplePowSum / samplePowCount; s->samplePowCount += 1; } /* 1st: convert to 16 bit - to allow easier calculation of DC */ for (i=0; i<(int)len; i++) { s->buf16[i] = ( (int16_t)buf[i] - 127 ); } /* 2nd: do DC filtering BEFORE up-mixing */ if (d->dc_block_raw) { dc_block_raw_filter(d, s->buf16, (int)len); } if (muteLen && c->filename) return; /* "mute" after the dc_block_raw_filter(), giving it time to remove the new DC */ /* 3rd: down-mixing */ if (!s->offset_tuning) { rotate16_neg90(s->buf16, (int)len); } pthread_rwlock_wrlock(&d->rw); memcpy(d->lowpassed, s->buf16, 2*len); d->lp_len = len; pthread_rwlock_unlock(&d->rw); safe_cond_signal(&d->ready, &d->ready_m); } static void *dongle_thread_fn(void *arg) { struct dongle_state *s = arg; rtlsdr_read_async(s->dev, rtlsdr_callback, s, 0, s->buf_len); return 0; } static void *demod_thread_fn(void *arg) { struct demod_state *d = arg; struct output_state *o = d->output_target; struct cmd_state *c = d->cmd; while (!do_exit) { safe_cond_wait(&d->ready, &d->ready_m); pthread_rwlock_wrlock(&d->rw); full_demod(d); pthread_rwlock_unlock(&d->rw); if (d->exit_flag) { do_exit = 1; } if (d->squelch_level && d->squelch_hits > d->conseq_squelch) { d->squelch_hits = d->conseq_squelch + 1; /* hair trigger */ safe_cond_signal(&controller.hop, &controller.hop_m); continue; } if (do_exit) break; if (c->filename && c->numSummed >= c->numMeas) { checkTriggerCommand(c, dongle.sampleMax, dongle.samplePowSum, dongle.samplePowCount); safe_cond_signal(&controller.hop, &controller.hop_m); continue; } if (OutputToStdout) { pthread_rwlock_wrlock(&o->rw); memcpy(o->result, d->result, 2*d->result_len); o->result_len = d->result_len; pthread_rwlock_unlock(&o->rw); safe_cond_signal(&o->ready, &o->ready_m); } } return 0; } static void *output_thread_fn(void *arg) { struct output_state *s = arg; if (!waveHdrStarted) { while (!do_exit) { /* use timedwait and pad out under runs */ safe_cond_wait(&s->ready, &s->ready_m); pthread_rwlock_rdlock(&s->rw); fwrite(s->result, 2, s->result_len, s->file); pthread_rwlock_unlock(&s->rw); } } else { while (!do_exit) { /* use timedwait and pad out under runs */ safe_cond_wait(&s->ready, &s->ready_m); pthread_rwlock_rdlock(&s->rw); /* distinguish for endianness: wave requires little endian */ waveWriteSamples(s->file, s->result, s->result_len, 0); pthread_rwlock_unlock(&s->rw); } } return 0; } static void optimal_settings(uint64_t freq, uint32_t rate) { /* giant ball of hacks * seems unable to do a single pass, 2:1 */ uint64_t capture_freq; uint32_t capture_rate; struct dongle_state *d = &dongle; struct demod_state *dm = &demod; struct controller_state *cs = &controller; dm->downsample = (MinCaptureRate / dm->rate_in) + 1; if (dm->downsample_passes) { dm->downsample_passes = (int)log2(dm->downsample) + 1; dm->downsample = 1 << dm->downsample_passes; } if (verbosity >= 2) { fprintf(stderr, "downsample_passes = %d (= # of fifth_order() iterations), downsample = %d\n", dm->downsample_passes, dm->downsample ); } capture_freq = freq; capture_rate = dm->downsample * dm->rate_in; if (verbosity >= 2) fprintf(stderr, "capture_rate = dm->downsample * dm->rate_in = %d * %d = %d\n", dm->downsample, dm->rate_in, capture_rate ); if (!d->offset_tuning) { capture_freq = freq - capture_rate/4; if (verbosity >= 2) fprintf(stderr, "optimal_settings(freq = %f MHz): capture_freq = freq - capture_rate/4 = %f MHz\n", freq * 1E-6, capture_freq * 1E-6 ); } capture_freq += cs->edge * dm->rate_in / 2; if (verbosity >= 2) fprintf(stderr, "optimal_settings(freq = %f MHz): capture_freq += cs->edge * dm->rate_in / 2 = %d * %d / 2 = %f MHz\n", freq * 1E-6, cs->edge, dm->rate_in, capture_freq * 1E-6 ); dm->output_scale = (1<<15) / (128 * dm->downsample); if (dm->output_scale < 1) { dm->output_scale = 1;} if (dm->mode_demod == &fm_demod) { dm->output_scale = 1;} d->userFreq = freq; d->freq = capture_freq; d->rate = capture_rate; if (verbosity >= 2) fprintf(stderr, "optimal_settings(freq = %f MHz) delivers freq %f MHz, rate %.0f\n", freq * 1E-6, d->freq * 1E-6, (double)d->rate ); } static void *controller_thread_fn(void *arg) { /* thoughts for multiple dongles * might be no good using a controller thread if retune/rate blocks */ int i, r, execWaitHop = 1; int32_t if_band_center_freq = 0; struct controller_state *s = arg; struct cmd_state *c = s->cmd; if (s->wb_mode) { if (verbosity) fprintf(stderr, "wbfm: adding 16000 Hz to every input frequency\n"); for (i=0; i < s->freq_len; i++) { s->freqs[i] += 16000;} } /* set up primary channel */ if (c->filename) { dongle.mute = dongle.rate; /* over a second - until parametrized the dongle */ toNextCmdLine(c); /*fprintf(stderr, "\nswitched to next command line. new freq %u\n", c->freq);*/ s->freqs[0] = c->freq; execWaitHop = 0; } optimal_settings(s->freqs[0], demod.rate_in); if (dongle.direct_sampling) { verbose_direct_sampling(dongle.dev, 1);} if (dongle.offset_tuning) { verbose_offset_tuning(dongle.dev);} /* Set the frequency */ if (verbosity) { fprintf(stderr, "verbose_set_frequency(%f MHz)\n", dongle.userFreq * 1E-6); if (!dongle.offset_tuning) fprintf(stderr, " frequency is away from parametrized one, to avoid negative impact from dc\n"); } verbose_set_frequency(dongle.dev, dongle.freq); fprintf(stderr, "Oversampling input by: %ix.\n", demod.downsample); fprintf(stderr, "Oversampling output by: %ix.\n", demod.post_downsample); fprintf(stderr, "Buffer size: %0.2fms\n", 1000 * 0.5 * (float)ACTUAL_BUF_LENGTH / (float)dongle.rate); /* Set the sample rate */ if (verbosity) fprintf(stderr, "verbose_set_sample_rate(%.0f Hz)\n", (double)dongle.rate); verbose_set_sample_rate(dongle.dev, dongle.rate); fprintf(stderr, "Output at %u Hz.\n", demod.rate_in/demod.post_downsample); if ( dongle.bandwidth ) { if_band_center_freq = dongle.userFreq - dongle.freq; if (dongle.bccorner < 0) if_band_center_freq += ( dongle.bandwidth - demod.rate_out ) / 2; else if (dongle.bccorner > 0) if_band_center_freq -= ( dongle.bandwidth - demod.rate_out ) / 2; if ( prev_if_band_center_freq != if_band_center_freq ) { r = rtlsdr_set_tuner_band_center(dongle.dev, if_band_center_freq ); if (r) fprintf(stderr, "WARNING: Failed to set band center.\n"); else { prev_if_band_center_freq = if_band_center_freq; if (verbosity) fprintf(stderr, "rtlsdr_set_tuner_band_center(%.0f Hz) successful\n", (double)if_band_center_freq); } } } while (!do_exit) { if (execWaitHop) safe_cond_wait(&s->hop, &s->hop_m); execWaitHop = 1; /* execute following safe_cond_wait()'s */ /* fprintf(stderr, "\nreceived hop condition\n"); */ if (s->freq_len <= 1 && !c->filename) { continue;} if (!c->filename) { /* hacky hopping */ s->freq_now = (s->freq_now + 1) % s->freq_len; optimal_settings(s->freqs[s->freq_now], demod.rate_in); rtlsdr_set_center_freq64(dongle.dev, dongle.freq); if ( dongle.bandwidth ) { if_band_center_freq = dongle.userFreq - dongle.freq; if ( prev_if_band_center_freq != if_band_center_freq ) { r = rtlsdr_set_tuner_band_center(dongle.dev, if_band_center_freq ); if (r) fprintf(stderr, "WARNING: Failed to set band center.\n"); else { prev_if_band_center_freq = if_band_center_freq; if (verbosity) fprintf(stderr, "rtlsdr_set_tuner_band_center(%.0f Hz) successful\n", (double)if_band_center_freq); } } } dongle.mute = DEFAULT_BUFFER_DUMP; } else { dongle.mute = 2 * dongle.rate; /* over a second - until parametrized the dongle */ c->numSummed = 0; toNextCmdLine(c); optimal_settings(c->freq, demod.rate_in); /* 1- set center frequency */ if (c->prevFreq != dongle.freq) { rtlsdr_set_center_freq64(dongle.dev, dongle.freq); c->prevFreq = dongle.freq; } /* 2- Set the tuner gain */ if (c->prevGain != c->gain) { if (c->gain == AUTO_GAIN) { r = rtlsdr_set_tuner_gain_mode(dongle.dev, 0); if (r != 0) fprintf(stderr, "WARNING: Failed to set automatic tuner gain.\n"); else c->prevGain = c->gain; } else { c->gain = nearest_gain(dongle.dev, c->gain); r = rtlsdr_set_tuner_gain_mode(dongle.dev, 1); if (r < 0) fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); else { r = rtlsdr_set_tuner_gain(dongle.dev, c->gain); if (r != 0) fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); else c->prevGain = c->gain; } } } /* 3- Set tuner bandwidth */ if (c->prevBandwidth != dongle.bandwidth) { r = rtlsdr_set_tuner_bandwidth(dongle.dev, dongle.bandwidth); if (r < 0) fprintf(stderr, "WARNING: Failed to set bandwidth.\n"); else c->prevBandwidth = dongle.bandwidth; } /* */ if ( dongle.bandwidth ) { if_band_center_freq = dongle.userFreq - dongle.freq; if ( prev_if_band_center_freq != if_band_center_freq ) { r = rtlsdr_set_tuner_band_center(dongle.dev, if_band_center_freq ); if (r) fprintf(stderr, "WARNING: Failed to set band center.\n"); else { prev_if_band_center_freq = if_band_center_freq; if (verbosity) fprintf(stderr, "rtlsdr_set_tuner_band_center(%.0f Hz) successful\n", (double)if_band_center_freq); } } } /* 4- Set ADC samplerate * r = rtlsdr_set_sample_rate(dongle.dev, dongle.rate); if (r < 0) fprintf(stderr, "WARNING: Failed to set sample rate.\n"); */ c->levelSum = 0; c->numSummed = 0; /* reset DC filters */ demod.dc_avg = 0; demod.dc_avgI = 0; demod.dc_avgQ = 0; dongle.mute = BufferDump; /* reset adc max and power */ dongle.samplePowSum = 0.0; dongle.samplePowCount = 0; dongle.sampleMax = 0; } } return 0; } void frequency_range(struct controller_state *s, char *arg) { char *start, *stop, *step; int i; start = arg; stop = strchr(start, ':') + 1; stop[-1] = '\0'; step = strchr(stop, ':') + 1; step[-1] = '\0'; for(i=(int)atofs(start); i<=(int)atofs(stop); i+=(int)atofs(step)) { s->freqs[s->freq_len] = (uint32_t)i; s->freq_len++; if (s->freq_len >= FREQUENCIES_LIMIT) { break;} } stop[-1] = ':'; step[-1] = ':'; } void dongle_init(struct dongle_state *s) { s->rate = DEFAULT_SAMPLE_RATE; s->gain = AUTO_GAIN; /* tenths of a dB */ s->mute = 0; s->direct_sampling = 0; s->offset_tuning = 0; s->demod_target = &demod; s->samplePowSum = 0.0; s->samplePowCount = 0; s->sampleMax = 0; s->bandwidth = 0; s->bccorner = 0; s->buf_len = 32 * 512; /* see rtl_tcp */ } void demod_init(struct demod_state *s) { s->rate_in = DEFAULT_SAMPLE_RATE; s->rate_out = DEFAULT_SAMPLE_RATE; s->squelch_level = 0; s->conseq_squelch = 10; s->terminate_on_squelch = 0; s->squelch_hits = 11; s->downsample_passes = 0; s->comp_fir_size = 0; s->prev_index = 0; s->post_downsample = 1; // once this works, default = 4 s->custom_atan = 0; s->deemph = 0; s->rate_out2 = -1; // flag for disabled s->mode_demod = &fm_demod; s->pre_j = s->pre_r = s->now_r = s->now_j = 0; s->prev_lpr_index = 0; s->deemph_a = 0; s->now_lpr = 0; s->dc_block_audio = 0; s->dc_avg = 0; s->adc_block_const = 9; s->dc_block_raw = 0; s->dc_avgI = 0; s->dc_avgQ = 0; s->rdc_block_const = 9; pthread_rwlock_init(&s->rw, NULL); pthread_cond_init(&s->ready, NULL); pthread_mutex_init(&s->ready_m, NULL); s->output_target = &output; s->cmd = &cmd; } void demod_cleanup(struct demod_state *s) { pthread_rwlock_destroy(&s->rw); pthread_cond_destroy(&s->ready); pthread_mutex_destroy(&s->ready_m); } void output_init(struct output_state *s) { s->rate = DEFAULT_SAMPLE_RATE; pthread_rwlock_init(&s->rw, NULL); pthread_cond_init(&s->ready, NULL); pthread_mutex_init(&s->ready_m, NULL); } void output_cleanup(struct output_state *s) { pthread_rwlock_destroy(&s->rw); pthread_cond_destroy(&s->ready); pthread_mutex_destroy(&s->ready_m); } void controller_init(struct controller_state *s) { s->freqs[0] = 100000000; s->freq_len = 0; s->edge = 0; s->wb_mode = 0; pthread_cond_init(&s->hop, NULL); pthread_mutex_init(&s->hop_m, NULL); s->cmd = &cmd; } void controller_cleanup(struct controller_state *s) { pthread_cond_destroy(&s->hop); pthread_mutex_destroy(&s->hop_m); } void sanity_checks(void) { if (controller.freq_len == 0) { fprintf(stderr, "Please specify a frequency.\n"); exit(1); } if (controller.freq_len >= FREQUENCIES_LIMIT) { fprintf(stderr, "Too many channels, maximum %i.\n", FREQUENCIES_LIMIT); exit(1); } if (controller.freq_len > 1 && demod.squelch_level == 0) { fprintf(stderr, "Please specify a squelch level. Required for scanning multiple frequencies.\n"); exit(1); } } int main(int argc, char **argv) { #ifndef _WIN32 struct sigaction sigact; #endif int r, opt; int dev_given = 0; int writeWav = 0; int custom_ppm = 0; int enable_biastee = 0; const char * rtlOpts = NULL; enum rtlsdr_ds_mode ds_mode = RTLSDR_DS_IQ; uint32_t ds_temp, ds_threshold = 0; int timeConstant = 75; /* default: U.S. 75 uS */ int rtlagc = 0; dongle_init(&dongle); demod_init(&demod); output_init(&output); controller_init(&controller); cmd_init(&cmd); while ((opt = getopt(argc, argv, "d:f:g:s:b:l:o:t:r:p:R:E:O:F:A:M:hTC:B:m:L:q:c:w:W:D:nHv")) != -1) { switch (opt) { case 'd': dongle.dev_index = verbose_device_search(optarg); dev_given = 1; break; case 'f': if (controller.freq_len >= FREQUENCIES_LIMIT) { break;} if (strchr(optarg, ':')) {frequency_range(&controller, optarg);} else { controller.freqs[controller.freq_len] = (uint32_t)atofs(optarg); controller.freq_len++; } break; case 'C': cmd.filename = optarg; demod.mode_demod = &raw_demod; break; case 'm': MinCaptureRate = (int)atofs(optarg); break; case 'B': BufferDump = atoi(optarg); break; case 'n': OutputToStdout = 0; break; case 'g': dongle.gain = (int)(atof(optarg) * 10); break; case 'l': demod.squelch_level = (int)atof(optarg); break; case 'L': printLevels = (int)atof(optarg); break; case 's': demod.rate_in = (uint32_t)atofs(optarg); demod.rate_out = (uint32_t)atofs(optarg); break; case 'r': output.rate = (int)atofs(optarg); demod.rate_out2 = (int)atofs(optarg); break; case 'o': fprintf(stderr, "Warning: -o is very buggy\n"); demod.post_downsample = (int)atof(optarg); if (demod.post_downsample < 1 || demod.post_downsample > MAXIMUM_OVERSAMPLE) { fprintf(stderr, "Oversample must be between 1 and %i\n", MAXIMUM_OVERSAMPLE);} break; case 't': demod.conseq_squelch = (int)atof(optarg); if (demod.conseq_squelch < 0) { demod.conseq_squelch = -demod.conseq_squelch; demod.terminate_on_squelch = 1; } break; case 'p': dongle.ppm_error = atoi(optarg); custom_ppm = 1; break; case 'R': time(&stop_time); duration = atoi(optarg); if (duration < 1) { fprintf(stderr, "Duration '%s' was not positive integer; will continue indefinitely\n", optarg); } else { stop_time += duration; } break; case 'E': if (strcmp("edge", optarg) == 0) { controller.edge = 1;} if (strcmp("dc", optarg) == 0 || strcmp("adc", optarg) == 0) { demod.dc_block_audio = 1;} if (strcmp("rdc", optarg) == 0) { demod.dc_block_raw = 1;} if (strcmp("deemp", optarg) == 0) { demod.deemph = 1;} if (strcmp("direct", optarg) == 0) { dongle.direct_sampling = 1;} if (strcmp("offset", optarg) == 0) { dongle.offset_tuning = 1;} if (strcmp("rtlagc", optarg) == 0 || strcmp("agc", optarg) == 0) { rtlagc = 1;} if (strcmp("bclo", optarg) == 0 || strcmp("bcL", optarg) == 0 || strcmp("bcl", optarg) == 0) { dongle.bccorner = -1; } if (strcmp("bcc", optarg) == 0 || strcmp("bcC", optarg) == 0) { dongle.bccorner = 0; } if (strcmp("bchi", optarg) == 0 || strcmp("bcH", optarg) == 0 || strcmp("bch", optarg) == 0) { dongle.bccorner = 1; } break; case 'O': rtlOpts = optarg; break; case 'q': demod.rdc_block_const = atoi(optarg); break; case 'F': demod.downsample_passes = 1; /* truthy placeholder */ demod.comp_fir_size = atoi(optarg); break; case 'A': if (strcmp("std", optarg) == 0) { demod.custom_atan = 0;} if (strcmp("fast", optarg) == 0) { demod.custom_atan = 1;} if (strcmp("lut", optarg) == 0) { atan_lut_init(); demod.custom_atan = 2;} if (strcmp("ale", optarg) == 0) { demod.custom_atan = 3;} break; case 'M': if (strcmp("nbfm", optarg) == 0 || strcmp("nfm", optarg) == 0 || strcmp("fm", optarg) == 0) { demod.mode_demod = &fm_demod;} if (strcmp("raw", optarg) == 0 || strcmp("iq", optarg) == 0) { demod.mode_demod = &raw_demod;} if (strcmp("am", optarg) == 0) { demod.mode_demod = &am_demod;} if (strcmp("usb", optarg) == 0) { demod.mode_demod = &usb_demod;} if (strcmp("lsb", optarg) == 0) { demod.mode_demod = &lsb_demod;} if (strcmp("wbfm", optarg) == 0 || strcmp("wfm", optarg) == 0) { controller.wb_mode = 1; demod.mode_demod = &fm_demod; demod.rate_in = 170000; demod.rate_out = 170000; demod.rate_out2 = 32000; output.rate = 32000; demod.custom_atan = 1; //demod.post_downsample = 4; demod.deemph = 1; demod.squelch_level = 0;} break; case 'T': enable_biastee = 1; break; case 'c': if (strcmp("us", optarg) == 0) timeConstant = 75; else if (strcmp("eu", optarg) == 0) timeConstant = 50; else timeConstant = (int)atof(optarg); break; case 'D': ds_temp = (uint32_t)( atofs(optarg) + 0.5 ); if (ds_temp <= RTLSDR_DS_Q_BELOW) ds_mode = (enum rtlsdr_ds_mode)ds_temp; else ds_threshold = ds_temp; break; case 'H': writeWav = 1; break; case 'v': ++verbosity; break; case 'w': dongle.bandwidth = (uint32_t)atofs(optarg); break; case 'W': dongle.buf_len = 512 * atoi(optarg); if (dongle.buf_len > MAXIMUM_BUF_LENGTH) dongle.buf_len = MAXIMUM_BUF_LENGTH; break; case 'h': case '?': default: usage(); break; } } if (verbosity) fprintf(stderr, "verbosity set to %d\n", verbosity); /* quadruple sample_rate to limit to Δθ to ±π/2 */ demod.rate_in *= demod.post_downsample; if (!output.rate) { output.rate = demod.rate_out;} sanity_checks(); if (controller.freq_len > 1) { demod.terminate_on_squelch = 0;} if (optind < argc) { output.filename = argv[optind]; } else { output.filename = "-"; } ACTUAL_BUF_LENGTH = lcm_post[demod.post_downsample] * DEFAULT_BUF_LENGTH; if (!dev_given) { dongle.dev_index = verbose_device_search("0"); } if (dongle.dev_index < 0) { exit(1); } r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index); if (r < 0) { fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dongle.dev_index); exit(1); } #ifndef _WIN32 sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif if (demod.deemph) { double tc = (double)timeConstant * 1e-6; demod.deemph_a = (int)round(1.0/((1.0-exp(-1.0/(demod.rate_out * tc))))); if (verbosity) fprintf(stderr, "using wbfm deemphasis filter with time constant %d us\n", timeConstant ); } /* Set the tuner gain */ if (dongle.gain == AUTO_GAIN) { verbose_auto_gain(dongle.dev); } else { dongle.gain = nearest_gain(dongle.dev, dongle.gain); verbose_gain_set(dongle.dev, dongle.gain); } rtlsdr_set_agc_mode(dongle.dev, rtlagc); rtlsdr_set_bias_tee(dongle.dev, enable_biastee); if (enable_biastee) fprintf(stderr, "activated bias-T on GPIO PIN 0\n"); verbose_ppm_set(dongle.dev, dongle.ppm_error); /* Set direct sampling with threshold */ rtlsdr_set_ds_mode(dongle.dev, ds_mode, ds_threshold); verbose_set_bandwidth(dongle.dev, dongle.bandwidth); if (verbosity && dongle.bandwidth) { int r; uint32_t in_bw, out_bw, last_bw = 0; fprintf(stderr, "Supported bandwidth values in kHz:\n"); for ( in_bw = 1; in_bw < 3200; ++in_bw ) { r = rtlsdr_set_and_get_tuner_bandwidth(dongle.dev, in_bw*1000, &out_bw, 0 /* =apply_bw */); if ( r == 0 && out_bw != 0 && ( out_bw != last_bw || in_bw == 1 ) ) fprintf(stderr, "%s%.1f", (in_bw==1 ? "" : ", "), out_bw/1000.0 ); last_bw = out_bw; } fprintf(stderr,"\n"); } if (rtlOpts) { rtlsdr_set_opt_string(dongle.dev, rtlOpts, verbosity); } if (strcmp(output.filename, "-") == 0) { /* Write samples to stdout */ output.file = stdout; #ifdef _WIN32 _setmode(_fileno(output.file), _O_BINARY); #endif } else { const char * filename_to_open = output.filename; if (writeWav) { output.tempfilename = malloc( strlen(output.filename)+8 ); strcpy(output.tempfilename, output.filename); strcat(output.tempfilename, ".tmp"); filename_to_open = output.tempfilename; } output.file = fopen(filename_to_open, "wb"); if (!output.file) { fprintf(stderr, "Failed to open %s\n", filename_to_open); exit(1); } else { fprintf(stderr, "Open %s for write\n", filename_to_open); if (writeWav) { int nChan = (demod.mode_demod == &raw_demod) ? 2 : 1; int srate = (demod.rate_out2 > 0) ? demod.rate_out2 : demod.rate_out; uint32_t f = controller.freqs[0]; /* only 1st frequency!!! */ waveWriteHeader(srate, f, 16, nChan, output.file); } } } //r = rtlsdr_set_testmode(dongle.dev, 1); /* Reset endpoint before we start reading from it (mandatory) */ verbose_reset_buffer(dongle.dev); pthread_create(&controller.thread, NULL, controller_thread_fn, (void *)(&controller)); usleep(1000000); /* it looks, that startup of dongle level takes some time at startup! */ pthread_create(&output.thread, NULL, output_thread_fn, (void *)(&output)); pthread_create(&demod.thread, NULL, demod_thread_fn, (void *)(&demod)); pthread_create(&dongle.thread, NULL, dongle_thread_fn, (void *)(&dongle)); while (!do_exit) { usleep(100000); } if (do_exit) { fprintf(stderr, "\nUser cancel, exiting...\n");} else { fprintf(stderr, "\nLibrary error %d, exiting...\n", r);} rtlsdr_cancel_async(dongle.dev); pthread_join(dongle.thread, NULL); safe_cond_signal(&demod.ready, &demod.ready_m); pthread_join(demod.thread, NULL); safe_cond_signal(&output.ready, &output.ready_m); pthread_join(output.thread, NULL); safe_cond_signal(&controller.hop, &controller.hop_m); pthread_join(controller.thread, NULL); /* dongle_cleanup(&dongle); */ demod_cleanup(&demod); output_cleanup(&output); controller_cleanup(&controller); if (cmd.filename) { int k; /* output scan statistics */ for (k = 0; k < FREQUENCIES_LIMIT; k++) { if (cmd.statNumLevels[k] > 0) fprintf(stderr, "%.0f, %.1f, %.2f, %.1f\n", (double)(cmd.statFreq[k]), cmd.statMinLevel[k], cmd.statSumLevels[k] / cmd.statNumLevels[k], cmd.statMaxLevel[k] ); } } if (output.file != stdout) { if (writeWav) { int r; waveFinalizeHeader(output.file); fclose(output.file); remove(output.filename); /* delete, in case file already exists */ r = rename( output.tempfilename, output.filename ); /* #include */ if ( r ) fprintf( stderr, "%s: error %d '%s' renaming'%s' to '%s'\n" , argv[0], errno, strerror(errno), output.tempfilename, output.filename ); } else { fclose(output.file); } } rtlsdr_close(dongle.dev); return r >= 0 ? r : -r; } /* vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab */ ================================================ FILE: src/rtl_ir.c ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2009 Antti Palosaari * Copyright (C) 2011 Antti Palosaari * Copyright (C) 2012 Thomas Mair * Copyright (C) 2012 by Steve Markgraf * Copyright (C) 2012 by Hoernchen * Copyright (C) 2012 by Kyle Keen * Copyright (C) 2013 by Elias Oenal * Copyright (C) 2016 by Robert X. Seger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #ifndef _WIN32 #include #else #include #include #include #include "getopt/getopt.h" #define usleep(x) Sleep(x/1000) #if defined(_MSC_VER) && (_MSC_VER < 1800) #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) #endif #define _USE_MATH_DEFINES #endif #include #include #include #include #include "convenience/convenience.h" #include "convenience/rtl_convenience.h" static volatile int do_exit = 0; struct dongle_state { int exit_flag; rtlsdr_dev_t *dev; int dev_index; }; void dongle_init(struct dongle_state *s) { memset(s, 0, sizeof(struct dongle_state)); } struct dongle_state dongle; void usage(void) { fprintf(stderr, "rtl_ir, display received IR signals\n" "rtl_ir version %d.%d %s (%s)\n" "rtl-sdr library %d.%d %s\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__, rtlsdr_get_version() >>16, rtlsdr_get_version() & 0xFFFF, rtlsdr_get_ver_id() ); fprintf(stderr, "Usage:\trtl_ir [-options]\n" "\t[-d device_index (default: 0)]\n" "\t[-w wait_usec]\tDelay to wait before each iteration (10000)\n" "\t[-c max_count]\tMaximum number of loop iterations (0)\n" "\t[-b]\tDisplay output in binary (default), pulse=1, space=0; each 20 usec\n" "\t[-t]\tDisplay output in text format\n" "\t[-x]\tDisplay output in raw packed bytes, MSB=pulse/space, 7LSB=duration*20 usec\n" "\t[-h]\tHelp\n" ); exit(1); } #ifdef _WIN32 BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dongle.dev); return TRUE; } return FALSE; } #else static void sighandler(int signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dongle.dev); } #endif int main(int argc, char **argv) { #ifndef _WIN32 struct sigaction sigact; #endif int r, opt; int i, j; int dev_given = 0; unsigned int wait_usec = 100000; int max_count = 0, iteration_count = 0; int output_binary = 0, output_text = 0, output_packed = 0; uint8_t buf[128] = { 0 }; dongle_init(&dongle); while ((opt = getopt(argc, argv, "d:c:w:btxh")) != -1) { switch (opt) { case 'd': dongle.dev_index = verbose_device_search(optarg); dev_given = 1; break; case 'w': wait_usec = atoi(optarg); break; case 'c': max_count = atoi(optarg); break; case 'b': output_binary = 1; break; case 't': output_text = 1; break; case 'x': output_packed = 1; break; case 'h': default: usage(); break; } } if (dongle.dev_index < 0) { exit(1); } r = rtlsdr_open(&dongle.dev, (uint32_t)dongle.dev_index); if (r < 0) { fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dongle.dev_index); exit(1); } #ifndef _WIN32 sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif verbose_reset_buffer(dongle.dev); if (!output_binary && !output_text && !output_packed) output_binary = 1; while (!do_exit) { usleep(wait_usec); r = rtlsdr_ir_query(dongle.dev, buf, sizeof(buf)); if (r < 0) { fprintf(stderr, "rtlsdr_ir_query failed: %d\n", r); } for (i = 0; i < r; i++) { int pulse = buf[i] >> 7; int duration = buf[i] & 0x7f; if (output_text) { printf("pulse %d, duration %d usec\n", pulse, duration * 20); } if (output_binary) { for (j = 0; j < duration; ++j) { printf("%d", pulse); } } if (output_packed) { putchar(buf[i]); } } if (r != 0) printf("\n"); fflush(stdout); if (max_count != 0 && ++iteration_count >= max_count) do_exit = 1; } if (do_exit) { fprintf(stderr, "\nUser cancel, exiting...\n");} else { fprintf(stderr, "\nLibrary error %d, exiting...\n", r);} rtlsdr_cancel_async(dongle.dev); rtlsdr_close(dongle.dev); return r >= 0 ? r : -r; return 0; } ================================================ FILE: src/rtl_power.c ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2012 by Steve Markgraf * Copyright (C) 2012 by Hoernchen * Copyright (C) 2012 by Kyle Keen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * rtl_power: general purpose FFT integrator * -f low_freq:high_freq:max_bin_size * -i seconds * outputs CSV * time, low, high, step, db, db, db ... * db optional? raw output might be better for noise correction * todo: * threading * randomized hopping * noise correction * continuous IIR * general astronomy usefulness * multiple dongles * multiple FFT workers * check edge cropping for off-by-one and rounding errors * 1.8MS/s for hiding xtal harmonics */ #include #include #include #include #include #include #ifndef _WIN32 #include #else #include #include #include #include "getopt/getopt.h" #define usleep(x) Sleep(x/1000) #if defined(_MSC_VER) && (_MSC_VER < 1800) #define round(x) (x > 0.0 ? floor(x + 0.5): ceil(x - 0.5)) #endif #define _USE_MATH_DEFINES #endif #include #ifdef NEED_PTHREADS_WORKARROUND #define HAVE_STRUCT_TIMESPEC #endif #include #include #include #include #include "convenience/convenience.h" #include "convenience/rtl_convenience.h" #define MAX(x, y) (((x) > (y)) ? (x) : (y)) #define DEFAULT_BUF_LENGTH (1 * 16384) #define AUTO_GAIN -100 #define BUFFER_DUMP (1<<12) #define MAXIMUM_RATE 2800000 #define MINIMUM_RATE 1000000 static volatile int do_exit = 0; static rtlsdr_dev_t *dev = NULL; FILE *file; int16_t* Sinewave; double* power_table; int N_WAVE, LOG2_N_WAVE; int next_power; int16_t *fft_buf; int *window_coefs; struct tuning_state /* one per tuning range */ { uint64_t freq; int rate; int bin_e; int64_t *avg; /* length == 2^bin_e */ int samples; int downsample; int downsample_passes; /* for the recursive filter */ double crop; //pthread_rwlock_t avg_lock; //pthread_mutex_t avg_mutex; /* having the iq buffer here is wasteful, but will avoid contention */ uint8_t *buf8; int buf_len; //int *comp_fir; //pthread_rwlock_t buf_lock; //pthread_mutex_t buf_mutex; }; enum time_modes { VERBOSE_TIME, EPOCH_TIME }; /* 3000 is enough for 3GHz b/w worst case */ #define MAX_TUNES 3000 struct tuning_state tunes[MAX_TUNES]; int tune_count = 0; int boxcar = 1; int comp_fir_size = 0; int peak_hold = 0; static enum time_modes time_mode = VERBOSE_TIME; void usage(void) { fprintf(stderr, "rtl_power, a simple FFT logger for RTL2832 based SDR-receivers\n" "rtl_power version %d.%d %s (%s)\n" "rtl-sdr library %d.%d %s\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__, rtlsdr_get_version() >>16, rtlsdr_get_version() & 0xFFFF, rtlsdr_get_ver_id() ); fprintf(stderr, "Usage:\trtl_power -f freq_range [-options] [filename]\n" "\t-f lower:upper:bin_size [Hz]\n" "\t (bin size is a maximum, smaller more convenient bins\n" "\t will be used. valid range 1Hz - 2.8MHz)\n" "\t[-i integration_interval (default: 10 seconds)]\n" "\t (buggy if a full sweep takes longer than the interval)\n" "\t[-1 enables single-shot mode (default: off)]\n" "\t[-e exit_timer (default: off/0)]\n" //"\t[-s avg/iir smoothing (default: avg)]\n" //"\t[-t threads (default: 1)]\n" "\t[-d device_index or serial (default: 0)]\n" "\t[-g tuner_gain (default: automatic)]\n" "\t[-p ppm_error (default: 0)]\n" "\t[-T enable bias-T on GPIO PIN 0 (works for rtl-sdr.com v3 dongles)]\n" "\t[-D direct_sampling_mode (default: 0, 1 = I, 2 = Q, 3 = I below threshold, 4 = Q below threshold)]\n" "\t[-D direct_sampling_threshold_frequency (default: 0 use tuner specific frequency threshold for 3 and 4)]\n" "\tfilename (a '-' dumps samples to stdout)\n" "\t (omitting the filename also uses stdout)\n" "\n" "Experimental options:\n" "\t[-w window (default: rectangle)]\n" "\t (hamming, blackman, blackman-harris, hann-poisson, bartlett, youssef)\n" // kaiser "\t[-c crop_percent (default: 0%%, recommended: 20%%-50%%)]\n" "\t (discards data at the edges, 100%% discards everything)\n" "\t (has no effect for bins larger than 1MHz)\n" "\t[-F fir_size (default: disabled)]\n" "\t (enables low-leakage downsample filter,\n" "\t fir_size can be 0 or 9. 0 has bad roll off,\n" "\t try with '-c 50%%')\n" "\t[-P enables peak hold (default: off)]\n" "\t[-D enable direct sampling (default: off)]\n" "\t[-O enable offset tuning (default: off)]\n" "\n" "CSV FFT output columns:\n" "\tdate, time, Hz low, Hz high, Hz step, samples, dbm, dbm, ...\n\n" "Examples:\n" "\trtl_power -f 88M:108M:125k fm_stations.csv\n" "\t (creates 160 bins across the FM band,\n" "\t individual stations should be visible)\n" "\trtl_power -f 100M:1G:1M -i 5m -1 survey.csv\n" "\t (a five minute low res scan of nearly everything)\n" "\trtl_power -f ... -i 15m -1 log.csv\n" "\t (integrate for 15 minutes and exit afterwards)\n" "\trtl_power -f ... -e 1h | gzip > log.csv.gz\n" "\t (collect data for one hour and compress it on the fly)\n\n" "Convert CSV to a waterfall graphic with:\n" "\t http://kmkeen.com/tmp/heatmap.py.txt \n"); exit(1); } void multi_bail(void) { if (do_exit == 1) { fprintf(stderr, "Signal caught, finishing scan pass.\n"); } if (do_exit >= 2) { fprintf(stderr, "Signal caught, aborting immediately.\n"); } } #ifdef _WIN32 BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { do_exit++; multi_bail(); return TRUE; } return FALSE; } #else static void sighandler(int signum) { do_exit++; multi_bail(); } #endif /* more cond dumbness */ #define safe_cond_signal(n, m) pthread_mutex_lock(m); pthread_cond_signal(n); pthread_mutex_unlock(m) #define safe_cond_wait(n, m) pthread_mutex_lock(m); pthread_cond_wait(n, m); pthread_mutex_unlock(m) /* {length, coef, coef, coef} and scaled by 2^15 for now, only length 9, optimal way to get +85% bandwidth */ #define CIC_TABLE_MAX 10 int cic_9_tables[][10] = { {0,}, {9, -156, -97, 2798, -15489, 61019, -15489, 2798, -97, -156}, {9, -128, -568, 5593, -24125, 74126, -24125, 5593, -568, -128}, {9, -129, -639, 6187, -26281, 77511, -26281, 6187, -639, -129}, {9, -122, -612, 6082, -26353, 77818, -26353, 6082, -612, -122}, {9, -120, -602, 6015, -26269, 77757, -26269, 6015, -602, -120}, {9, -120, -582, 5951, -26128, 77542, -26128, 5951, -582, -120}, {9, -119, -580, 5931, -26094, 77505, -26094, 5931, -580, -119}, {9, -119, -578, 5921, -26077, 77484, -26077, 5921, -578, -119}, {9, -119, -577, 5917, -26067, 77473, -26067, 5917, -577, -119}, {9, -199, -362, 5303, -25505, 77489, -25505, 5303, -362, -199}, }; #if defined(_MSC_VER) && (_MSC_VER < 1800) double log2(double n) { return log(n) / log(2.0); } #endif /* FFT based on fix_fft.c by Roberts, Slaney and Bouras http://www.jjj.de/fft/fftpage.html 16 bit ints for everything -32768..+32768 maps to -1.0..+1.0 */ void sine_table(int size) { int i; double d; LOG2_N_WAVE = size; N_WAVE = 1 << LOG2_N_WAVE; Sinewave = malloc(sizeof(int16_t) * N_WAVE*3/4); power_table = malloc(sizeof(double) * N_WAVE); for (i=0; i> 14; b = c & 0x01; return (c >> 1) + b; } int fix_fft(int16_t iq[], int m) /* interleaved iq[], 0 <= n < 2**m, changes in place */ { int mr, nn, i, j, l, k, istep, n, shift; int16_t qr, qi, tr, ti, wr, wi; n = 1 << m; if (n > N_WAVE) {return -1;} mr = 0; nn = n - 1; /* decimation in time - re-order data */ for (m=1; m<=nn; ++m) { l = n; do {l >>= 1;} while (mr+l > nn); mr = (mr & (l-1)) + l; if (mr <= m) {continue;} // real = 2*m, imag = 2*m+1 tr = iq[2*m]; iq[2*m] = iq[2*mr]; iq[2*mr] = tr; ti = iq[2*m+1]; iq[2*m+1] = iq[2*mr+1]; iq[2*mr+1] = ti; } l = 1; k = LOG2_N_WAVE-1; while (l < n) { shift = 1; istep = l << 1; for (m=0; m>= 1; wi >>= 1;} for (i=m; i>= 1; qi >>= 1;} iq[2*j] = qr - tr; iq[2*j+1] = qi - ti; iq[2*i] = qr + tr; iq[2*i+1] = qi + ti; } } --k; l = istep; } return 0; } double rectangle(int i, int length) { return 1.0; } double hamming(int i, int length) { double a, b, w, N1; a = 25.0/46.0; b = 21.0/46.0; N1 = (double)(length-1); w = a - b*cos(2*i*M_PI/N1); return w; } double blackman(int i, int length) { double a0, a1, a2, w, N1; a0 = 7938.0/18608.0; a1 = 9240.0/18608.0; a2 = 1430.0/18608.0; N1 = (double)(length-1); w = a0 - a1*cos(2*i*M_PI/N1) + a2*cos(4*i*M_PI/N1); return w; } double blackman_harris(int i, int length) { double a0, a1, a2, a3, w, N1; a0 = 0.35875; a1 = 0.48829; a2 = 0.14128; a3 = 0.01168; N1 = (double)(length-1); w = a0 - a1*cos(2*i*M_PI/N1) + a2*cos(4*i*M_PI/N1) - a3*cos(6*i*M_PI/N1); return w; } double hann_poisson(int i, int length) { double a, N1, w; a = 2.0; N1 = (double)(length-1); w = 0.5 * (1 - cos(2*M_PI*i/N1)) * \ pow(M_E, (-a*(double)abs((int)(N1-1-2*i)))/N1); return w; } double youssef(int i, int length) /* really a blackman-harris-poisson window, but that is a mouthful */ { double a, a0, a1, a2, a3, w, N1; a0 = 0.35875; a1 = 0.48829; a2 = 0.14128; a3 = 0.01168; N1 = (double)(length-1); w = a0 - a1*cos(2*i*M_PI/N1) + a2*cos(4*i*M_PI/N1) - a3*cos(6*i*M_PI/N1); a = 0.0025; w *= pow(M_E, (-a*(double)abs((int)(N1-1-2*i)))/N1); return w; } double kaiser(int i, int length) // todo, become more smart { return 1.0; } double bartlett(int i, int length) { double N1, L, w; L = (double)length; N1 = L - 1; w = (i - N1/2) / (L/2); if (w < 0) { w = -w;} w = 1 - w; return w; } void rms_power(struct tuning_state *ts) /* for bins between 1MHz and 2MHz */ { int i, s; uint8_t *buf = ts->buf8; int buf_len = ts->buf_len; int64_t p, t; double dc, err; p = t = 0L; for (i=0; iavg[0] += p; } else { ts->avg[0] = MAX(ts->avg[0], p); } ts->samples += 1; } void frequency_range(char *arg, double crop) /* flesh out the tunes[] for scanning */ // do we want the fewest ranges (easy) or the fewest bins (harder)? { char *start, *stop, *step; uint64_t upper, lower; int i, j, max_size, bw_seen, bw_used, bin_e, buf_len; int downsample, downsample_passes; double bin_size; struct tuning_state *ts; /* hacky string parsing */ start = arg; stop = strchr(start, ':') + 1; stop[-1] = '\0'; step = strchr(stop, ':') + 1; step[-1] = '\0'; lower = (uint64_t)(atofs(start) + 0.5); upper = (uint64_t)(atofs(stop) + 0.5); max_size = (int)atofs(step); stop[-1] = ':'; step[-1] = ':'; downsample = 1; downsample_passes = 0; /* evenly sized ranges, as close to MAXIMUM_RATE as possible */ // todo, replace loop with algebra for (i=1; i<1500; i++) { bw_seen = (upper - lower) / i; bw_used = (int)((double)(bw_seen) / (1.0 - crop)); if (bw_used > MAXIMUM_RATE) { continue;} tune_count = i; break; } /* unless small bandwidth */ if (bw_used < MINIMUM_RATE) { tune_count = 1; downsample = MAXIMUM_RATE / bw_used; bw_used = bw_used * downsample; } if (!boxcar && downsample > 1) { downsample_passes = (int)log2(downsample); downsample = 1 << downsample_passes; bw_used = (int)((double)(bw_seen * downsample) / (1.0 - crop)); } /* number of bins is power-of-two, bin size is under limit */ // todo, replace loop with log2 for (i=1; i<=21; i++) { bin_e = i; bin_size = (double)bw_used / (double)((1<= MINIMUM_RATE) { bw_seen = max_size; bw_used = max_size; tune_count = (upper - lower) / bw_seen; bin_e = 0; crop = 0; } if (tune_count > MAX_TUNES) { fprintf(stderr, "Error: bandwidth too wide.\n"); exit(1); } buf_len = 2 * (1<freq = lower + i*bw_seen + bw_seen/2; ts->rate = bw_used; ts->bin_e = bin_e; ts->samples = 0; ts->crop = crop; ts->downsample = downsample; ts->downsample_passes = downsample_passes; ts->avg = (int64_t*)malloc((1<avg) { fprintf(stderr, "Error: malloc.\n"); exit(1); } for (j=0; j<(1<avg[j] = 0L; } ts->buf8 = (uint8_t*)malloc(buf_len * sizeof(uint8_t)); if (!ts->buf8) { fprintf(stderr, "Error: malloc.\n"); exit(1); } ts->buf_len = buf_len; } /* report */ fprintf(stderr, "Number of frequency hops: %i\n", tune_count); fprintf(stderr, "Dongle bandwidth: %iHz\n", bw_used); fprintf(stderr, "Downsampling by: %ix\n", downsample); fprintf(stderr, "Cropping by: %0.2f%%\n", crop*100); fprintf(stderr, "Total FFT bins: %i\n", tune_count * (1<> 4; data[2] = ((b+c)*10 + (a+d)*5 + e + f) >> 4; data[4] = (a + (b+e)*5 + (c+d)*10 + f) >> 4; for (i=12; i> 4; } } void remove_dc(int16_t *data, int length) /* works on interleaved data */ { int i; int16_t ave; int64_t sum = 0L; for (i=0; i < length; i+=2) { sum += data[i]; } ave = (int16_t)(sum / (int64_t)(length)); if (ave == 0) { return;} for (i=0; i < length; i+=2) { data[i] -= ave; } } void generic_fir(int16_t *data, int length, int *fir) /* Okay, not at all generic. Assumes length 9, fix that eventually. */ { int d, temp, sum; int hist[9] = {0,}; /* cheat on the beginning, let it go unfiltered */ for (d=0; d<18; d+=2) { hist[d/2] = data[d]; } for (d=18; d> 15) ; hist[0] = hist[1]; hist[1] = hist[2]; hist[2] = hist[3]; hist[3] = hist[4]; hist[4] = hist[5]; hist[5] = hist[6]; hist[6] = hist[7]; hist[7] = hist[8]; hist[8] = temp; } } void downsample_iq(int16_t *data, int length) { fifth_order(data, length); //remove_dc(data, length); fifth_order(data+1, length-1); //remove_dc(data+1, length-1); } int64_t real_conj(int16_t real, int16_t imag) /* real(n * conj(n)) */ { return ((int64_t)real*(int64_t)real + (int64_t)imag*(int64_t)imag); } void scanner(void) { int i, j, j2, n_read, offset, bin_e, bin_len, buf_len, ds, ds_p; uint64_t f; int32_t w; struct tuning_state *ts; bin_e = tunes[0].bin_e; bin_len = 1 << bin_e; buf_len = tunes[0].buf_len; for (i=0; i= 2) {return;} ts = &tunes[i]; f = rtlsdr_get_center_freq64(dev); if (f != ts->freq) { retune(dev, ts->freq);} rtlsdr_read_sync(dev, ts->buf8, buf_len, &n_read); if (n_read != buf_len) { fprintf(stderr, "Error: dropped samples.\n");} /* rms */ if (bin_len == 1) { rms_power(ts); continue; } /* prep for fft */ for (j=0; jbuf8[j] - 127; } ds = ts->downsample; ds_p = ts->downsample_passes; if (boxcar && ds > 1) { j=2, j2=0; while (j < buf_len) { fft_buf[j2] += fft_buf[j]; fft_buf[j2+1] += fft_buf[j+1]; fft_buf[j] = 0; fft_buf[j+1] = 0; j += 2; if (j % (ds*2) == 0) { j2 += 2;} } } else if (ds_p) { /* recursive */ for (j=0; j < ds_p; j++) { downsample_iq(fft_buf, buf_len >> j); } /* droop compensation */ if (comp_fir_size == 9 && ds_p <= CIC_TABLE_MAX) { generic_fir(fft_buf, buf_len >> j, cic_9_tables[ds_p]); generic_fir(fft_buf+1, (buf_len >> j)-1, cic_9_tables[ds_p]); } } remove_dc(fft_buf, buf_len / ds); remove_dc(fft_buf+1, (buf_len / ds) - 1); /* window function and fft */ for (offset=0; offset<(buf_len/ds); offset+=(2*bin_len)) { // todo, let rect skip this for (j=0; javg[j] += real_conj(fft_buf[offset+j*2], fft_buf[offset+j*2+1]); } } else { for (j=0; javg[j] = MAX(real_conj(fft_buf[offset+j*2], fft_buf[offset+j*2+1]), ts->avg[j]); } } ts->samples += ds; } } } void csv_dbm(struct tuning_state *ts) { int i, len, ds, i1, i2, bw2, bin_count; int64_t tmp; double dbm; len = 1 << ts->bin_e; ds = ts->downsample; /* fix FFT stuff quirks */ if (ts->bin_e > 0) { /* nuke DC component (not effective for all windows) */ ts->avg[0] = ts->avg[1]; /* FFT is translated by 180 degrees */ for (i=0; iavg[i]; ts->avg[i] = ts->avg[i+len/2]; ts->avg[i+len/2] = tmp; } } /* Hz low, Hz high, Hz step, samples, dbm, dbm, ... */ bin_count = (int)((double)len * (1.0 - ts->crop)); bw2 = (int)(((double)ts->rate * (double)bin_count) / (len * 2 * ds)); fprintf(file, "%.0f, %.0f, %.2f, %i, ", (double)(ts->freq - bw2), (double)(ts->freq + bw2), (double)ts->rate / (double)(len*ds), ts->samples); // something seems off with the dbm math i1 = 0 + (int)((double)len * ts->crop * 0.5); i2 = (len-1) - (int)((double)len * ts->crop * 0.5); for (i=i1; i<=i2; i++) { dbm = (double)ts->avg[i]; dbm /= (double)ts->rate; dbm /= (double)ts->samples; dbm = 10 * log10(dbm); fprintf(file, "%.2f, ", dbm); } dbm = (double)ts->avg[i2] / ((double)ts->rate * (double)ts->samples); if (ts->bin_e == 0) { dbm = ((double)ts->avg[0] / \ ((double)ts->rate * (double)ts->samples));} dbm = 10 * log10(dbm); fprintf(file, "%.2f\n", dbm); for (i=0; iavg[i] = 0L; } ts->samples = 0; } int main(int argc, char **argv) { #ifndef _WIN32 struct sigaction sigact; #endif char *filename = NULL; int i, length, r, opt, wb_mode = 0; int f_set = 0; int gain = AUTO_GAIN; // tenths of a dB int dev_index = 0; char dev_label[256]; int dev_given = 0; int ppm_error = 0; int interval = 10; int fft_threads = 1; int smoothing = 0; int single = 0; int direct_sampling = 0; int offset_tuning = 0; int enable_biastee = 0; enum rtlsdr_ds_mode ds_mode = RTLSDR_DS_IQ; uint32_t ds_temp, ds_threshold = 0; double crop = 0.0; char *freq_optarg; time_t next_tick; time_t time_now; time_t exit_time = 0; char t_str[512]; struct tm *cal_time; double (*window_fn)(int, int) = rectangle; freq_optarg = ""; while ((opt = getopt(argc, argv, "f:i:s:t:d:g:p:e:w:c:F:1EPOhTD:")) != -1) { switch (opt) { case 'f': // lower:upper:bin_size freq_optarg = strdup(optarg); f_set = 1; break; case 'd': dev_index = verbose_device_search(optarg); strncpy(dev_label, optarg, 255); dev_label[255] = 0; dev_given = 1; break; case 'g': gain = (int)(atof(optarg) * 10); break; case 'c': crop = atofp(optarg); break; case 'i': interval = (int)round(atoft(optarg)); break; case 'e': exit_time = (time_t)((int)round(atoft(optarg))); break; case 's': if (strcmp("avg", optarg) == 0) { smoothing = 0;} if (strcmp("iir", optarg) == 0) { smoothing = 1;} break; case 'w': if (strcmp("rectangle", optarg) == 0) { window_fn = rectangle;} if (strcmp("hamming", optarg) == 0) { window_fn = hamming;} if (strcmp("blackman", optarg) == 0) { window_fn = blackman;} if (strcmp("blackman-harris", optarg) == 0) { window_fn = blackman_harris;} if (strcmp("hann-poisson", optarg) == 0) { window_fn = hann_poisson;} if (strcmp("youssef", optarg) == 0) { window_fn = youssef;} if (strcmp("kaiser", optarg) == 0) { window_fn = kaiser;} if (strcmp("bartlett", optarg) == 0) { window_fn = bartlett;} break; case 't': fft_threads = atoi(optarg); break; case 'p': ppm_error = atoi(optarg); break; case '1': single = 1; break; case 'E': time_mode = EPOCH_TIME; break; case 'P': peak_hold = 1; break; case 'D': if(!optarg) { direct_sampling = 1; } else { ds_temp = (uint32_t)( atofs(optarg) + 0.5 ); if (ds_temp <= RTLSDR_DS_Q_BELOW) ds_mode = (enum rtlsdr_ds_mode)ds_temp; else ds_threshold = ds_temp; } break; case 'O': offset_tuning = 1; break; case 'F': boxcar = 0; comp_fir_size = atoi(optarg); break; case 'T': enable_biastee = 1; break; case 'h': default: usage(); break; } } if (!f_set) { fprintf(stderr, "No frequency range provided.\n"); exit(1); } if ((crop < 0.0) || (crop > 1.0)) { fprintf(stderr, "Crop value outside of 0 to 1.\n"); exit(1); } frequency_range(freq_optarg, crop); if (tune_count == 0) { usage();} if (argc <= optind) { filename = "-"; } else { filename = argv[optind]; } if (interval < 1) { interval = 1;} fprintf(stderr, "Reporting every %i seconds\n", interval); if (!dev_given) { dev_index = verbose_device_search("0"); } if (dev_index < 0) { exit(1); } r = rtlsdr_open(&dev, (uint32_t)dev_index); if (r < 0) { fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); exit(1); } #ifndef _WIN32 sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif if (direct_sampling) { verbose_direct_sampling(dev, 1); } /* Set direct sampling with threshold */ rtlsdr_set_ds_mode(dev, ds_mode, ds_threshold); if (offset_tuning) { verbose_offset_tuning(dev); } /* Set the tuner gain */ if (gain == AUTO_GAIN) { verbose_auto_gain(dev); } else { gain = nearest_gain(dev, gain); verbose_gain_set(dev, gain); } verbose_ppm_set(dev, ppm_error); rtlsdr_set_bias_tee(dev, enable_biastee); if (enable_biastee) fprintf(stderr, "activated bias-T on GPIO PIN 0\n"); if (strcmp(filename, "-") == 0) { /* Write log to stdout */ file = stdout; #ifdef _WIN32 // Is this necessary? Output is ascii. _setmode(_fileno(file), _O_BINARY); #endif } else { file = fopen(filename, "wb"); if (!file) { fprintf(stderr, "Failed to open %s\n", filename); exit(1); } } /* Reset endpoint before we start reading from it (mandatory) */ verbose_reset_buffer(dev); /* actually do stuff */ rtlsdr_set_sample_rate(dev, (uint32_t)tunes[0].rate); sine_table(tunes[0].bin_e); next_tick = time(NULL) + interval; if (exit_time) { exit_time = time(NULL) + exit_time;} fft_buf = malloc(tunes[0].buf_len * sizeof(int16_t)); length = 1 << tunes[0].bin_e; window_coefs = malloc(length * sizeof(int)); for (i=0; i= next_tick) { next_tick += interval;} if (single) { do_exit = 1;} if (exit_time && time(NULL) >= exit_time) { do_exit = 1;} } /* clean up */ if (do_exit) { fprintf(stderr, "\nUser cancel, exiting...\n");} else { fprintf(stderr, "\nLibrary error %d, exiting...\n", r);} if (file != stdout) { fclose(file);} rtlsdr_close(dev); free(fft_buf); free(window_coefs); if (freq_optarg[0]) free(freq_optarg); //for (i=0; i= 0 ? r : -r; } // vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab ================================================ FILE: src/rtl_raw2wav.c ================================================ /* * rtl-raw2wav, converts binary/raw data into wave files - including frequency * Copyright (C) 2019 by Hayati Ayguen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #else #include #include #include #include #include #include "getopt/getopt.h" #if defined(_MSC_VER) && (_MSC_VER < 1900) #define snprintf _snprintf #endif #endif #include #include "convenience/convenience.h" #include "convenience/wavewrite.h" static volatile int do_exit = 0; static int verbosity = 0; void usage(void) { fprintf(stderr, "rtl_raw2wav, a raw (binary sampledata) to wave file converter\n" "rtl_raw2wav version %d.%d %s (%s)\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__ ); fprintf(stderr, "Usage:\trtl_raw2wav -w [-options] [input_raw_filename]\n" "\t-w filename output filename\n" "\t-f frequency frequency, to write into output filename\n" "\t-s samplerate samplerate, of raw input\n" "\t-c #channels number of channels, default: 2 - for I and Q\n" "\t-b #bits number of bits per sample, default: 8 - 8 or 16 allowed\n" "\t-u time set start time in UTC: 'yyy-mm-ddThh:mm:dd.zzz'\n" "\t-t time set start time in localtime: 'yyy-mm-ddThh:mm:dd.zzz'\n" "\t-v verbose output\n" "\t-r filename input filename for raw samples, default: - for stdin\n\n" ); } #ifdef _WIN32 BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; return TRUE; } return FALSE; } #else static void sighandler(int signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; } #endif int main(int argc, char **argv) { #ifndef _WIN32 struct sigaction sigact; #endif int r, opt; const char * rawfilename = NULL; const char * wavfilename = NULL; char *tempfilename = NULL; FILE * outfile = NULL; FILE * inpfile = NULL; uint32_t freq = 0; uint32_t srate = 0; int nChan = 2; int nBits = 8; char acBuf[ 65536 * sizeof(int16_t) * 2 ]; /* 64 K frames with 2 channels and 16 Bit */ size_t smpSize; size_t nRead; time_t tim = 0; double fraction = 0.0; int haveTime = 0; while ((opt = getopt(argc, argv, "f:s:c:b:r:u:t:w:vh")) != -1) { switch (opt) { case 'f': freq = (uint32_t)atofs(optarg); break; case 's': srate = (uint32_t)atofs(optarg); break; case 'c': nChan = atoi(optarg); break; case 'b': nBits = atoi(optarg); if (nBits <= 4) /* assume intention was: bytes per sample */ nBits *= 8; break; case 'v': ++verbosity; break; case 'r': rawfilename = optarg; break; case 'u': tim = utctimestr_to_time(optarg, &fraction); if (tim) haveTime = 1; break; case 't': tim = localtimestr_to_time(optarg, &fraction); if (tim) haveTime = 1; break; case 'w': wavfilename = optarg; break; case 'h': case '?': default: usage(); exit(1); break; } } if (verbosity) fprintf(stderr, "verbosity set to %d\n", verbosity); if (optind < argc) { rawfilename = argv[optind]; } while (!wavfilename) { if (rawfilename) { int slen = strlen(rawfilename); const char * rawEnd = rawfilename + slen; if (slen > 4 && (!strcmp(rawEnd - 4, ".bin") || !strcmp(rawEnd - 4, ".raw"))) { char * wfn = strdup(rawfilename); strcpy(wfn+slen-4, ".wav"); wavfilename = wfn; if (verbosity) fprintf(stderr, "Warning: deduced .wav filename '%s' from rawfilename '%s'\n", wavfilename, rawfilename); break; /* the while() */ } } usage(); fprintf(stderr, "Error: missing output wave filename!\n"); exit(1); } if (nChan < 1 || nChan > 2) { usage(); fprintf(stderr, "Error: number of channels must be 1 or 2!\n"); exit(1); } if (nBits != 8 && nBits != 16) { usage(); fprintf(stderr, "Error: number of bits per sample must be 8 or 16!\n"); exit(1); } #ifndef _WIN32 sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif smpSize = nChan * ( nBits == 16 ? sizeof(int16_t) : sizeof(uint8_t) ); if (verbosity >= 2) fprintf(stderr, "Frame size = %d channels * %d bits = %d bytes\n", nChan, nBits, (int)smpSize); if (!rawfilename || !strcmp(rawfilename, "-")) { #ifdef _WIN32 _setmode(_fileno(stdin), _O_BINARY); #endif inpfile = stdin; if (verbosity) fprintf(stderr, "Using stdin as input\n"); } else { if (verbosity >= 2) fprintf(stderr, "Opening input '%s'\n", rawfilename); inpfile = fopen(rawfilename, "rb"); if (!inpfile) { fprintf(stderr, "Error: Failed to open input %s\n", rawfilename); exit(1); } if (verbosity) fprintf(stderr, "Opened '%s' for input\n", rawfilename); if (!haveTime) { int gotmodtim = 0; #if defined(_WIN32) struct _stat attr; if (!_stat(rawfilename, &attr)) { tim = attr.st_mtime; fraction = 0.0; gotmodtim = 1; } #elif defined(__APPLE__) struct stat attr; if (!stat(rawfilename, &attr)) { tim = attr.st_mtime; fraction = attr.st_mtimespec.tv_nsec / 1E9; gotmodtim = 1; } #else struct stat attr; if (!stat(rawfilename, &attr)) { tim = attr.st_mtime; fraction = attr.st_mtim.tv_nsec / 1E9; gotmodtim = 1; } #endif if (gotmodtim) { long fs = 0; if (verbosity >= 2) fprintf(stderr, "Got 'last modified' from input file '%s'\n", rawfilename); if (!fseek(inpfile, 0, SEEK_END)) { fs = ftell(inpfile); if (fs > 0) { double dur = (double)(fs) / ( (double)srate * smpSize ); double di = floor(dur); double df = dur - di; tim -= (time_t)di; fraction -= df; if (fraction < 0.0) { fraction += 1.0; tim -= 1; } if (verbosity) fprintf(stderr, "Set output's timestamp from input files 'last modified' minus duration of %f seconds\n", dur); haveTime = 1; } } fseek(inpfile, 0, SEEK_SET); } } } tempfilename = malloc( strlen(wavfilename)+8 ); strcpy(tempfilename, wavfilename); strcat(tempfilename, ".tmp"); if (verbosity >= 2) fprintf(stderr, "Opening output '%s'\n", tempfilename); outfile = fopen(tempfilename, "wb"); if (!outfile) { fprintf(stderr, "Error: Failed to open output '%s'\n", tempfilename); exit(1); } else { if (verbosity) fprintf(stderr, "Opened '%s' for output\n", tempfilename); waveWriteHeader(srate, freq, nBits, nChan, outfile); if (haveTime) { if (verbosity >= 2) fprintf(stderr, "Setting start time of output file\n"); waveSetStartTime(tim, fraction); } } while ( !feof(inpfile) ) { if (do_exit) { fprintf(stderr, "\nUser cancel, exiting...\n"); break; } nRead = fread(acBuf, smpSize, 65536, inpfile); if (nRead > 0) waveWriteFrames(outfile, acBuf, nRead, 0); if (verbosity >= 2) fprintf(stderr, "."); } if (verbosity >= 2) fprintf(stderr, "\nWriting output header\n"); waveFinalizeHeader(outfile); fclose(outfile); if (verbosity) fprintf(stderr, "Closed output file.\n"); if (verbosity >= 2) fprintf(stderr, "Deleting '%s' - in case it exists\n", wavfilename); remove(wavfilename); /* delete, in case file already exists */ if (verbosity >= 2) fprintf(stderr, "Renaming '%s' into '%s'\n", tempfilename, wavfilename); r = rename( tempfilename, wavfilename ); /* #include */ if ( r ) fprintf( stderr, "%s: Error %d '%s' renaming'%s' to '%s'\n" , argv[0], errno, strerror(errno), tempfilename, wavfilename ); else if (verbosity) fprintf( stderr, "Renamed output file into '%s'\n", wavfilename ); if (inpfile != stdin) fclose(inpfile); return 0; } /* vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab */ ================================================ FILE: src/rtl_rpcd.c ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtlsdr_rpc_msg.h" #if 1 #include #define PRINTF(__s, ...) \ do { \ fprintf(stderr, __s, ##__VA_ARGS__); \ fflush(stderr); \ } while (0) #define TRACE() PRINTF("[t] %s,%u\n", __FILE__, __LINE__) #define ERROR() PRINTF("[e] %s,%u\n", __FILE__, __LINE__) #else #define TRACE() #define ERROR() #define PRINTF(...) #endif typedef struct { int listen_sock; int cli_sock; rtlsdr_dev_t* dev; uint32_t did; rtlsdr_rpc_msg_t query_msg; rtlsdr_rpc_msg_t reply_msg; rtlsdr_rpc_msg_t event_msg; unsigned int async_replied; uint8_t async_id; int port_ir; const char *addr_ir; int wait_ir; } rpcd_t; static int resolve_ip_addr ( struct sockaddr_storage saddr_both[2], size_t size_both[2], const char* addr, const char* port ) { struct addrinfo ai; struct addrinfo* aip = NULL; int err = -1; size_t i; memset(&ai, 0, sizeof(ai)); ai.ai_family = AF_UNSPEC; ai.ai_socktype = SOCK_STREAM; ai.ai_flags = AI_PASSIVE; if (getaddrinfo(addr, port, &ai, &aip)) goto on_error; size_both[0] = 0; size_both[1] = 0; i = 0; for (; (i != 2) && (aip != NULL); aip = aip->ai_next) { if ((aip->ai_family != AF_INET) && (aip->ai_family != AF_INET6)) continue ; if (aip->ai_addrlen == 0) continue ; memcpy(&saddr_both[i], aip->ai_addr, aip->ai_addrlen); size_both[i] = aip->ai_addrlen; ++i; } if (i == 0) goto on_error; err = 0; on_error: if (aip != NULL) freeaddrinfo(aip); return err; } static int open_nonblock_socket ( struct sockaddr_storage saddr_both[2], size_t size_both[2], int type, int proto, struct sockaddr_storage** saddr_used, size_t* size_used ) { size_t i; int fd = -1; for (i = 0; (i != 2) && (size_both[i]); ++i) { const struct sockaddr* const sa = (const struct sockaddr*)&saddr_both[i]; fd = socket(sa->sa_family, type, proto); if (fd != -1) break ; } if ((i == 2) || (size_both[i] == 0)) return -1; *saddr_used = &saddr_both[i]; *size_used = size_both[i]; if (fcntl(fd, F_SETFL, O_NONBLOCK)) { close(fd); return -1; } return fd; } static int init_rpcd(rpcd_t* rpcd, const char* addr, const char* port) { struct sockaddr_storage saddrs[2]; struct sockaddr_storage* saddr; size_t sizes[2]; size_t size; int err; int enable = 1; /* TODO: handle errors */ rtlsdr_rpc_msg_init(&rpcd->query_msg, 0); rtlsdr_rpc_msg_init(&rpcd->reply_msg, 0); rtlsdr_rpc_msg_init(&rpcd->event_msg, 0); if (resolve_ip_addr(saddrs, sizes, addr, port)) { ERROR(); goto on_error_0; } rpcd->listen_sock = open_nonblock_socket (saddrs, sizes, SOCK_STREAM, IPPROTO_TCP, &saddr, &size); if (rpcd->listen_sock == -1) { ERROR(); goto on_error_0; } err = setsockopt (rpcd->listen_sock, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)); if (err) { ERROR(); goto on_error_1; } err = bind (rpcd->listen_sock, (const struct sockaddr*)saddr, (socklen_t)size); if (err) { ERROR(); goto on_error_1; } if (listen(rpcd->listen_sock, 5)) { ERROR(); goto on_error_1; } rpcd->cli_sock = -1; rpcd->dev = NULL; return 0; on_error_1: close(rpcd->listen_sock); on_error_0: return -1; } static int fini_rpcd(rpcd_t* rpcd) { if (rpcd->cli_sock != -1) { shutdown(rpcd->cli_sock, SHUT_RDWR); close(rpcd->cli_sock); } shutdown(rpcd->listen_sock, SHUT_RDWR); close(rpcd->listen_sock); rtlsdr_rpc_msg_fini(&rpcd->query_msg); rtlsdr_rpc_msg_fini(&rpcd->reply_msg); rtlsdr_rpc_msg_fini(&rpcd->event_msg); return 0; } static int recv_all(int fd, uint8_t* buf, size_t size) { ssize_t n; fd_set rset; while (1) { errno = 0; n = recv(fd, buf, size, 0); if (n <= 0) { if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) return -1; } else { size -= (size_t)n; buf += (size_t)n; if (size == 0) break ; } FD_ZERO(&rset); FD_SET(fd, &rset); if (select(fd + 1, &rset, NULL, NULL, NULL) <= 0) { return -1; } } return 0; } static int send_all_check_recv (int fd, const uint8_t* buf, size_t size, unsigned int* is_recv) { ssize_t n; fd_set wset; fd_set rset; fd_set* rsetp; rsetp = NULL; if (is_recv != NULL) *is_recv = 0; while (1) { FD_ZERO(&wset); FD_SET(fd, &wset); rsetp = NULL; if ((is_recv != NULL) && (*is_recv == 0)) { FD_ZERO(&rset); FD_SET(fd, &rset); rsetp = &rset; } if (select(fd + 1, rsetp, &wset, NULL, NULL) <= 0) { return -1; } if ((rsetp != NULL) && FD_ISSET(fd, rsetp)) { *is_recv = 1; } if (FD_ISSET(fd, &wset)) { errno = 0; n = send(fd, buf, size, 0); if (n <= 0) { if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) return -1; } else { size -= (size_t)n; buf += (size_t)n; if (size == 0) break ; } } } return 0; } static int send_all(int fd, const uint8_t* buf, size_t size) { return send_all_check_recv(fd, buf, size, NULL); } static int recv_msg(int fd, rtlsdr_rpc_msg_t* m) { uint32_t size; if (recv_all(fd, (uint8_t*)&size, sizeof(uint32_t))) { ERROR(); return -1; } rtlsdr_rpc_msg_set_size(m, size); size = rtlsdr_rpc_msg_get_size(m); if (rtlsdr_rpc_msg_realloc(m, size)) { ERROR(); return -1; } if (recv_all(fd, m->fmt + sizeof(uint32_t), size - sizeof(uint32_t))) { ERROR(); return -1; } return 0; } static int send_msg(int fd, rtlsdr_rpc_msg_t* m) { return send_all(fd, m->fmt, m->off); } static int recv_query(rpcd_t* rpcd, rtlsdr_rpc_msg_t** q) { *q = NULL; rtlsdr_rpc_msg_reset(&rpcd->query_msg); if (recv_msg(rpcd->cli_sock, &rpcd->query_msg)) return -1; *q = &rpcd->query_msg; return 0; } static int send_reply(rpcd_t* rpcd, rtlsdr_rpc_msg_t* r) { return send_msg(rpcd->cli_sock, r); } static void read_async_cb (unsigned char* buf, uint32_t len, void* ctx) { const size_t off = offsetof(rtlsdr_rpc_fmt_t, data); rpcd_t* const rpcd = ctx; uint8_t fmt[offsetof(rtlsdr_rpc_fmt_t, data)]; rtlsdr_rpc_msg_t msg; unsigned int is_recv; if (rpcd->async_replied == 0) { send_reply(rpcd, &rpcd->reply_msg); rpcd->async_replied = 1; } msg.off = off; msg.size = off + len; msg.fmt = fmt; rtlsdr_rpc_msg_set_size(&msg, msg.size); rtlsdr_rpc_msg_set_op(&msg, RTLSDR_RPC_OP_READ_ASYNC); rtlsdr_rpc_msg_set_id(&msg, rpcd->async_id); rtlsdr_rpc_msg_set_err(&msg, 0); send_all(rpcd->cli_sock, fmt, off); send_all_check_recv(rpcd->cli_sock, buf, len, &is_recv); if (is_recv) rtlsdr_cancel_async(rpcd->dev); } __attribute__((unused)) static const char* op_to_string(rtlsdr_rpc_op_t op) { const char* const s[] = { "RTLSDR_RPC_OP_GET_DEVICE_COUNT", "RTLSDR_RPC_OP_GET_DEVICE_NAME", "RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS", "RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL", "RTLSDR_RPC_OP_OPEN", "RTLSDR_RPC_OP_CLOSE", "RTLSDR_RPC_OP_SET_XTAL_FREQ", "RTLSDR_RPC_OP_GET_XTAL_FREQ", "RTLSDR_RPC_OP_GET_USB_STRINGS", "RTLSDR_RPC_OP_WRITE_EEPROM", "RTLSDR_RPC_OP_READ_EEPROM", "RTLSDR_RPC_OP_SET_CENTER_FREQ", "RTLSDR_RPC_OP_GET_CENTER_FREQ", "RTLSDR_RPC_OP_SET_FREQ_CORRECTION", "RTLSDR_RPC_OP_GET_FREQ_CORRECTION", "RTLSDR_RPC_OP_GET_TUNER_TYPE", "RTLSDR_RPC_OP_GET_TUNER_GAINS", "RTLSDR_RPC_OP_SET_TUNER_GAIN", "RTLSDR_RPC_OP_GET_TUNER_GAIN", "RTLSDR_RPC_OP_SET_TUNER_IF_GAIN", "RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE", "RTLSDR_RPC_OP_SET_GET_TUNER_BW", "RTLSDR_RPC_OP_SET_SAMPLE_RATE", "RTLSDR_RPC_OP_GET_SAMPLE_RATE", "RTLSDR_RPC_OP_SET_TESTMODE", "RTLSDR_RPC_OP_SET_AGC_MODE", "RTLSDR_RPC_OP_SET_DIRECT_SAMPLING", "RTLSDR_RPC_OP_GET_DIRECT_SAMPLING", "RTLSDR_RPC_OP_SET_OFFSET_TUNING", "RTLSDR_RPC_OP_GET_OFFSET_TUNING", "RTLSDR_RPC_OP_RESET_BUFFER", "RTLSDR_RPC_OP_READ_SYNC", "RTLSDR_RPC_OP_WAIT_ASYNC", "RTLSDR_RPC_OP_READ_ASYNC", "RTLSDR_RPC_OP_CANCEL_ASYNC", "RTLSDR_RPC_OP_EVENT_STATE", "RTLSDR_RPC_OP_INVALID" }; if (op >= RTLSDR_RPC_OP_INVALID) op = RTLSDR_RPC_OP_INVALID; return s[op]; } static int handle_query (rpcd_t* rpcd, rtlsdr_rpc_msg_t* q, rtlsdr_rpc_msg_t** rr) { rtlsdr_rpc_msg_t* const r = &rpcd->reply_msg; rtlsdr_rpc_op_t op; int err = -1; *rr = NULL; rtlsdr_rpc_msg_reset(r); op = rtlsdr_rpc_msg_get_op(q); switch (op) { case RTLSDR_RPC_OP_GET_DEVICE_COUNT: { uint32_t n; n = rtlsdr_get_device_count(); if (rtlsdr_rpc_msg_push_uint32(r, n)) goto on_error; err = 0; break ; } case RTLSDR_RPC_OP_GET_DEVICE_NAME: { const char* s; uint32_t i; if (rtlsdr_rpc_msg_pop_uint32(q, &i)) goto on_error; s = rtlsdr_get_device_name(i); if (s == NULL) s = ""; if (rtlsdr_rpc_msg_push_str(r, s)) goto on_error; err = 0; break ; } case RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS: { char manuf[256]; char product[256]; char serial[256]; uint32_t i; if (rtlsdr_rpc_msg_pop_uint32(q, &i)) goto on_error; err = rtlsdr_get_device_usb_strings(i, manuf, product, serial); if (err) goto on_error; if (rtlsdr_rpc_msg_push_str(r, manuf)) { err = -1; goto on_error; } if (rtlsdr_rpc_msg_push_str(r, product)) { err = -1; goto on_error; } if (rtlsdr_rpc_msg_push_str(r, serial)) { err = -1; goto on_error; } break ; } case RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL: { const char* serial; if (rtlsdr_rpc_msg_pop_str(q, &serial)) goto on_error; err = rtlsdr_get_index_by_serial(serial); if (err < 0) goto on_error; break ; } case RTLSDR_RPC_OP_OPEN: { if (rpcd->dev != NULL) { /* already opened */ err = 0; goto on_error; } if (rtlsdr_rpc_msg_pop_uint32(q, &rpcd->did)) goto on_error; err = rtlsdr_open(&rpcd->dev, rpcd->did); if (err) { rpcd->dev = NULL; goto on_error; } break ; } case RTLSDR_RPC_OP_CLOSE: { uint32_t did; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_close(rpcd->dev); rpcd->dev = NULL; break ; } case RTLSDR_RPC_OP_SET_XTAL_FREQ: { uint32_t did; uint32_t rtl_freq; uint32_t tuner_freq; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &rtl_freq)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &tuner_freq)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_set_xtal_freq(rpcd->dev, rtl_freq, tuner_freq); if (err) goto on_error; break ; } case RTLSDR_RPC_OP_GET_XTAL_FREQ: { uint32_t did; uint32_t rtl_freq; uint32_t tuner_freq; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_get_xtal_freq(rpcd->dev, &rtl_freq, &tuner_freq); if (err) goto on_error; if (rtlsdr_rpc_msg_push_uint32(r, rtl_freq)) { err = -1; goto on_error; } if (rtlsdr_rpc_msg_push_uint32(r, tuner_freq)) { err = -1; goto on_error; } break ; } case RTLSDR_RPC_OP_GET_USB_STRINGS: { uint32_t did; char manuf[256]; char product[256]; char serial[256]; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_get_usb_strings(rpcd->dev, manuf, product, serial); if (err) goto on_error; if (rtlsdr_rpc_msg_push_str(r, manuf)) { err = -1; goto on_error; } if (rtlsdr_rpc_msg_push_str(r, product)) { err = -1; goto on_error; } if (rtlsdr_rpc_msg_push_str(r, serial)) { err = -1; goto on_error; } break ; } case RTLSDR_RPC_OP_SET_CENTER_FREQ: { uint32_t did; uint32_t freq; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &freq)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_set_center_freq(rpcd->dev, freq); if (err) goto on_error; break ; } case RTLSDR_RPC_OP_GET_CENTER_FREQ: { uint32_t did; uint32_t freq; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; freq = rtlsdr_get_center_freq(rpcd->dev); if (freq == 0) goto on_error; if (rtlsdr_rpc_msg_push_uint32(r, freq)) goto on_error; err = 0; break ; } case RTLSDR_RPC_OP_SET_FREQ_CORRECTION: { uint32_t did; uint32_t ppm; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &ppm)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_set_freq_correction(rpcd->dev, (int)ppm); if (err) goto on_error; break ; } case RTLSDR_RPC_OP_GET_FREQ_CORRECTION: { uint32_t did; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_get_freq_correction(rpcd->dev); break ; } case RTLSDR_RPC_OP_GET_TUNER_TYPE: { uint32_t did; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_get_tuner_type(rpcd->dev); break ; } case RTLSDR_RPC_OP_GET_TUNER_GAINS: { uint32_t did; uint32_t is_null; uint32_t gain_size; size_t new_size; uint8_t* buf; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &is_null)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &gain_size)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; if (is_null) { err = rtlsdr_get_tuner_gains(rpcd->dev, NULL); if (err <= 0) goto on_error; } else { new_size = r->off + sizeof(uint32_t) + gain_size; if (rtlsdr_rpc_msg_realloc(r, new_size)) goto on_error; buf = r->fmt + r->off + sizeof(uint32_t); err = rtlsdr_get_tuner_gains(rpcd->dev, (int*)buf); if (err <= 0) goto on_error; rtlsdr_rpc_msg_push_uint32_safe(r, gain_size); rtlsdr_rpc_msg_skip_safe(r, gain_size); } break ; } case RTLSDR_RPC_OP_SET_TUNER_GAIN: { uint32_t did; uint32_t gain; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &gain)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_set_tuner_gain(rpcd->dev, (int)gain); if (err) goto on_error; break ; } case RTLSDR_RPC_OP_GET_TUNER_GAIN: { uint32_t did; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_get_tuner_gain(rpcd->dev); break ; } case RTLSDR_RPC_OP_SET_TUNER_IF_GAIN: { uint32_t did; uint32_t stage; uint32_t gain; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &stage)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &gain)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_set_tuner_if_gain(rpcd->dev, (int)stage, (int)gain); if (err) goto on_error; break ; } case RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE: { uint32_t did; uint32_t manual; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &manual)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_set_tuner_gain_mode(rpcd->dev, (int)manual); if (err) goto on_error; break ; } case RTLSDR_RPC_OP_SET_GET_TUNER_BW: { uint32_t did; uint32_t bw; uint32_t applied_bw; int32_t apply_bw; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &bw)) goto on_error; if (rtlsdr_rpc_msg_pop_int32(q, &apply_bw)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_set_and_get_tuner_bandwidth(rpcd->dev, bw, &applied_bw, (int)apply_bw); if (err) goto on_error; if (rtlsdr_rpc_msg_push_uint32(r, applied_bw)) goto on_error; break ; } case RTLSDR_RPC_OP_SET_SAMPLE_RATE: { uint32_t did; uint32_t rate; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &rate)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_set_sample_rate(rpcd->dev, rate); if (err) goto on_error; break ; } case RTLSDR_RPC_OP_GET_SAMPLE_RATE: { uint32_t did; uint32_t rate; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; rate = rtlsdr_get_sample_rate(rpcd->dev); if (rate == 0) goto on_error; if (rtlsdr_rpc_msg_push_uint32(r, rate)) goto on_error; err = 0; break ; } case RTLSDR_RPC_OP_SET_TESTMODE: { uint32_t did; uint32_t on; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &on)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_set_testmode(rpcd->dev, (int)on); if (err) goto on_error; break ; } case RTLSDR_RPC_OP_SET_AGC_MODE: { uint32_t did; uint32_t on; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &on)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_set_agc_mode(rpcd->dev, (int)on); if (err) goto on_error; break ; } case RTLSDR_RPC_OP_SET_DIRECT_SAMPLING: { uint32_t did; uint32_t on; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &on)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_set_direct_sampling(rpcd->dev, (int)on); if (err) goto on_error; break ; } case RTLSDR_RPC_OP_GET_DIRECT_SAMPLING: { uint32_t did; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_get_direct_sampling(rpcd->dev); break ; } case RTLSDR_RPC_OP_SET_OFFSET_TUNING: { uint32_t did; uint32_t on; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &on)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_set_offset_tuning(rpcd->dev, (int)on); if (err) goto on_error; break ; } case RTLSDR_RPC_OP_GET_OFFSET_TUNING: { uint32_t did; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_get_offset_tuning(rpcd->dev); break ; } case RTLSDR_RPC_OP_RESET_BUFFER: { uint32_t did; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; err = rtlsdr_reset_buffer(rpcd->dev); if (err) goto on_error; break ; } case RTLSDR_RPC_OP_READ_ASYNC: { uint32_t did; uint32_t buf_num; uint32_t buf_len; rtlsdr_rpc_op_t new_op; rtlsdr_rpc_msg_t* new_q; rtlsdr_rpc_msg_t* new_r; uint8_t id; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &buf_num)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &buf_len)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; /* prepare the reply here */ id = rtlsdr_rpc_msg_get_id(q); rtlsdr_rpc_msg_set_size(r, r->off); rtlsdr_rpc_msg_set_op(r, op); rtlsdr_rpc_msg_set_id(r, id); rtlsdr_rpc_msg_set_err(r, 0); rpcd->async_id = id; rpcd->async_replied = 0; while (1) { rtlsdr_read_async (rpcd->dev, read_async_cb, rpcd, buf_num, buf_len); if (rpcd->async_replied == 0) goto on_error; if (recv_query(rpcd, &new_q)) goto on_error; new_op = rtlsdr_rpc_msg_get_op(new_q); /* do not reply is cancel_async */ if (new_op == RTLSDR_RPC_OP_CANCEL_ASYNC) return 0; handle_query(rpcd, new_q, &new_r); if (new_r != NULL) send_reply(rpcd, new_r); } /* do not resend reply */ if (rpcd->async_replied) return 0; goto on_error; break ; } case RTLSDR_RPC_OP_CANCEL_ASYNC: { uint32_t did; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; /* already cancelled if here */ err = 0; break ; } case RTLSDR_RPC_OP_READ_SYNC: { uint32_t did; uint32_t len; int n_read; uint8_t* buf; size_t new_size; if (rtlsdr_rpc_msg_pop_uint32(q, &did)) goto on_error; if (rtlsdr_rpc_msg_pop_uint32(q, &len)) goto on_error; if ((rpcd->dev == NULL) || (rpcd->did != did)) goto on_error; new_size = r->off + sizeof(uint32_t) + len; if (rtlsdr_rpc_msg_realloc(r, new_size)) goto on_error; buf = r->fmt + r->off + sizeof(uint32_t); err = rtlsdr_read_sync(rpcd->dev, buf, (int)len, &n_read); if (err) goto on_error; rtlsdr_rpc_msg_push_uint32_safe(r, (uint32_t)n_read); rtlsdr_rpc_msg_skip_safe(r, (size_t)n_read); break ; } default: { PRINTF("invalid op: %u\n", op); break ; } } on_error: rtlsdr_rpc_msg_set_size(r, r->off); rtlsdr_rpc_msg_set_op(r, op); rtlsdr_rpc_msg_set_id(r, rtlsdr_rpc_msg_get_id(q)); rtlsdr_rpc_msg_set_err(r, err); *rr = r; return 0; } static int do_rpcd(rpcd_t* rpcd) { fd_set rset; int max_fd; while (1) { FD_ZERO(&rset); if (rpcd->cli_sock != -1) { FD_SET(rpcd->cli_sock, &rset); max_fd = rpcd->cli_sock; } else { FD_SET(rpcd->listen_sock, &rset); max_fd = rpcd->listen_sock; } if (select(max_fd + 1, &rset, NULL, NULL, NULL) <= 0) { ERROR(); return -1; } if (FD_ISSET(rpcd->listen_sock, &rset)) { PRINTF("new client\n"); rpcd->cli_sock = accept(rpcd->listen_sock, NULL, NULL); if (rpcd->cli_sock == -1) { if ((errno == EWOULDBLOCK) || (errno == EAGAIN)) continue ; ERROR(); break ; } if (fcntl(rpcd->cli_sock, F_SETFL, O_NONBLOCK)) { ERROR(); break ; } } else if (FD_ISSET(rpcd->cli_sock, &rset)) { rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; if (recv_query(rpcd, &q)) { PRINTF("cli closed\n"); shutdown(rpcd->cli_sock, SHUT_RDWR); close(rpcd->cli_sock); rpcd->cli_sock = -1; } else if (q != NULL) { handle_query(rpcd, q, &r); if (r != NULL) send_reply(rpcd, r); } } } return 0; } void usage(void) { fprintf(stderr, "rtl_rpcd, a Remote Procedure Call server for RTL2832 based SDR-receivers\n" "rtl_rpcd version %d.%d %s (%s)\n" "rtl-sdr library %d.%d %s\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__, rtlsdr_get_version() >>16, rtlsdr_get_version() & 0xFFFF, rtlsdr_get_ver_id() ); fprintf(stderr, "Usage:\trtl_rpcd [-options]\n" "\t[-a address]\tAddress to listen on (default: 0.0.0.0 for all), or RTLSDR_RPC_SERV_ADDR\n" "\t[-p port]\tPort number to listen on (default: 40000), or RTLSDR_RPC_SERV_PORT\n" "\t[-I port_ir]\tinfrared sensor listen port (default: 0=none)\n" "\t[-w wait_ir]\tinfrared sensor query wait interval usec (default: 10000)\n" "\t[-h]\t\tHelp\n" "\n" "On the remote client, set RTLSDR_RPC_IS_ENABLED and matching address/port\n" ); exit(1); } void *ir_thread_fn(void *arg) { int r = 1; struct linger ling = {1,0}; int listensocket; int irsocket; struct sockaddr_in local, remote; socklen_t rlen; uint8_t buf[128]; int ret = 0, len; struct timeval tv; fd_set writefds, errorfds; rpcd_t *rpcd = (rpcd_t *)arg; rtlsdr_dev_t *dev = rpcd->dev; const char *addr = rpcd->addr_ir; int port = rpcd->port_ir; int wait = rpcd->wait_ir; memset(&local,0,sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(port); local.sin_addr.s_addr = inet_addr(addr); listensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); r = 1; setsockopt(listensocket, SOL_SOCKET, SO_REUSEADDR, (char *)&r, sizeof(int)); setsockopt(listensocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); bind(listensocket,(struct sockaddr *)&local,sizeof(local)); while(1) { printf("listening on IR port %d...\n", port); listen(listensocket,1); irsocket = accept(listensocket,(struct sockaddr *)&remote, &rlen); setsockopt(irsocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); printf("IR client accepted!\n"); while(1) { dev = rpcd->dev; if (!dev) { printf("no device available yet\n"); // Keep waiting until RPC connects and opens device usleep(1000000); continue; } ret = rtlsdr_ir_query(dev, buf, sizeof(buf)); if (ret < 0) { printf("rtlsdr_ir_query error %d\n", ret); break; } len = ret; FD_ZERO(&writefds); FD_SET(irsocket, &writefds); FD_ZERO(&errorfds); FD_SET(irsocket, &errorfds); tv.tv_sec = 1; tv.tv_usec = 0; select(irsocket+1, NULL, &writefds, &errorfds, &tv); if (FD_ISSET(irsocket, &errorfds)) { printf("error condition from irsocket, breaking\n"); break; } if (FD_ISSET(irsocket, &writefds)) { ret = send(irsocket, buf, len, 0); if (ret != len){ printf("incomplete write to ir client: %d != %d\n", ret,len); break; } } else { printf("irsocket not ready to write, breaking\n"); break; } usleep(wait); } close(irsocket); } return 0; } int main(int ac, char** av) { rpcd_t rpcd; const char* addr = NULL; const char* port = NULL; int opt; int port_ir = 0, wait_ir = 10000; pthread_t thread_ir; while ((opt = getopt(ac, av, "a:p:I:w:h")) != -1) { switch (opt) { case 'a': addr = optarg; break; case 'p': port = optarg; break; case 'I': port_ir = atoi(optarg); break; case 'w': wait_ir = atoi(optarg); break; case 'h': default: usage(); } } if (addr == NULL) addr = getenv("RTLSDR_RPC_SERV_ADDR"); if (addr == NULL) addr = "0.0.0.0"; if (port == NULL) port = getenv("RTLSDR_RPC_SERV_PORT"); if (port == NULL) port = "40000"; if (init_rpcd(&rpcd, addr, port)) return -1; if (port_ir) { rpcd.port_ir = port_ir; rpcd.addr_ir = addr; rpcd.wait_ir = wait_ir; pthread_create(&thread_ir, NULL, &ir_thread_fn, (void *)(&rpcd)); } do_rpcd(&rpcd); fini_rpcd(&rpcd); return 0; } ================================================ FILE: src/rtl_sdr.c ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2012 by Steve Markgraf * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #ifndef _WIN32 #include #else #include #include #include #include "getopt/getopt.h" #endif #include #include #include "convenience/convenience.h" #include "convenience/rtl_convenience.h" #include "convenience/wavewrite.h" #define DEFAULT_SAMPLE_RATE 2048000 #define DEFAULT_BANDWIDTH 0 /* automatic bandwidth */ #define DEFAULT_BUF_LENGTH (16 * 16384) #define MINIMAL_BUF_LENGTH 512 #define MAXIMAL_BUF_LENGTH (256 * 16384) static int do_exit = 0; static uint32_t iq_frames_to_read = 0; static rtlsdr_dev_t *dev = NULL; void usage(void) { fprintf(stderr, "rtl_sdr, an I/Q recorder for RTL2832 based SDR-receivers\n" "rtl_sdr version %d.%d %s (%s)\n" "rtl-sdr library %d.%d %s\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__, rtlsdr_get_version() >>16, rtlsdr_get_version() & 0xFFFF, rtlsdr_get_ver_id() ); fprintf(stderr, "Usage:\trtl_sdr -f frequency_to_tune_to [Hz]\n" "\t[-s samplerate (default: 2048000 Hz)]\n" "\t[-w tuner_bandwidth (default: automatic)]\n" "\t[-d device_index or serial (default: 0)]\n" "\t[-g gain (default: 0 for auto)]\n" "\t[-p ppm_error (default: 0)]\n" "%s" "\t[-b output_block_size (default: 16 * 16384)]\n" "\t[-n number of samples to read (default: 0, infinite)]\n" "\t[-S force sync output (default: async)]\n" "\t[-N no dithering (default: use dithering)]\n" "\t[-H write wave Header to file (default: off)]\n" "\tfilename (a '-' dumps samples to stdout)\n\n" , rtlsdr_get_opt_help(1) ); exit(1); } #ifdef _WIN32 BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dev); return TRUE; } return FALSE; } #else static void sighandler(int signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dev); } #endif static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) { if (ctx) { if (do_exit) return; if ((iq_frames_to_read) && (iq_frames_to_read < len/2)) { len = 2U * iq_frames_to_read; iq_frames_to_read = 0; do_exit = 1; rtlsdr_cancel_async(dev); } if (!waveHdrStarted) { size_t wr = fwrite(buf, 1, len, (FILE*)ctx); if ( wr != len) { fprintf(stderr, "Short write (wrote %ld of %ld bytes), samples lost, exiting!\n" , (long)wr, (long)len ); rtlsdr_cancel_async(dev); } } else { if ( waveWriteFrames((FILE*)ctx, buf, len/2, 0) ) { fprintf(stderr, "Short write, samples lost, exiting!\n"); rtlsdr_cancel_async(dev); } } if (iq_frames_to_read) { if (iq_frames_to_read > len/2) iq_frames_to_read -= len/2; else { do_exit = 1; rtlsdr_cancel_async(dev); } } } } int main(int argc, char **argv) { #ifndef _WIN32 struct sigaction sigact; #endif char *filename = NULL; char *tempfilename = NULL; int n_read; int r, opt; int gain = 0; int ppm_error = 0; int sync_mode = 0; int dithering = 1; FILE *file; uint8_t *buffer; const char * rtlOpts = NULL; int dev_index = 0; int dev_given = 0; int writeWav = 0; uint64_t frequency = 100000000; uint32_t bandwidth = DEFAULT_BANDWIDTH; uint32_t samp_rate = DEFAULT_SAMPLE_RATE; uint32_t out_block_size = DEFAULT_BUF_LENGTH; int verbosity = 0; while ((opt = getopt(argc, argv, "d:f:g:s:w:b:n:p:O:SNHv")) != -1) { switch (opt) { case 'd': dev_index = verbose_device_search(optarg); dev_given = 1; break; case 'f': frequency = (uint64_t)atofs(optarg); break; case 'g': gain = (int)(atof(optarg) * 10); /* tenths of a dB */ break; case 's': samp_rate = (uint32_t)atofs(optarg); break; case 'w': bandwidth = (uint32_t)atofs(optarg); break; case 'p': ppm_error = atoi(optarg); break; case 'O': rtlOpts = optarg; break; case 'b': out_block_size = (uint32_t)atof(optarg); break; case 'n': iq_frames_to_read = (uint32_t)atof(optarg); break; case 'S': sync_mode = 1; break; case 'N': dithering = 0; break; case 'H': writeWav = 1; break; case 'v': ++verbosity; break; default: usage(); break; } } if (argc <= optind) { usage(); } else { filename = argv[optind]; } if(out_block_size < MINIMAL_BUF_LENGTH || out_block_size > MAXIMAL_BUF_LENGTH ){ fprintf(stderr, "Output block size wrong value, falling back to default\n"); fprintf(stderr, "Minimal length: %u\n", MINIMAL_BUF_LENGTH); fprintf(stderr, "Maximal length: %u\n", MAXIMAL_BUF_LENGTH); out_block_size = DEFAULT_BUF_LENGTH; } buffer = malloc(out_block_size * sizeof(uint8_t)); if (!dev_given) { dev_index = verbose_device_search("0"); } if (dev_index < 0) { exit(1); } r = rtlsdr_open(&dev, (uint32_t)dev_index); if (r < 0) { fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); exit(1); } #ifndef _WIN32 sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif if (!dithering) { fprintf(stderr, "Disabling dithering... "); r = rtlsdr_set_dithering(dev, dithering); if (r) { fprintf(stderr, "failure\n"); } else { fprintf(stderr, "success\n"); } } /* Set the sample rate */ verbose_set_sample_rate(dev, samp_rate); /* Set the tuner bandwidth */ verbose_set_bandwidth(dev, bandwidth); /* Set the frequency */ verbose_set_frequency(dev, frequency); if (0 == gain) { /* Enable automatic gain */ verbose_auto_gain(dev); } else { /* Enable manual gain */ gain = nearest_gain(dev, gain); verbose_gain_set(dev, gain); } if (rtlOpts) { rtlsdr_set_opt_string(dev, rtlOpts, verbosity); } verbose_ppm_set(dev, ppm_error); if(strcmp(filename, "-") == 0) { /* Write samples to stdout */ file = stdout; #ifdef _WIN32 _setmode(_fileno(stdout), _O_BINARY); #endif } else { const char * filename_to_open = filename; if (writeWav) { tempfilename = malloc( strlen(filename)+8 ); strcpy(tempfilename, filename); strcat(tempfilename, ".tmp"); filename_to_open = tempfilename; } file = fopen(filename_to_open, "wb"); if (!file) { fprintf(stderr, "Failed to open %s\n", filename_to_open); goto out; } if (writeWav) { waveWriteHeader(samp_rate, frequency, 8, 2, file); } } /* Reset endpoint before we start reading from it (mandatory) */ verbose_reset_buffer(dev); if (sync_mode) { fprintf(stderr, "Reading samples in sync mode...\n"); while (!do_exit) { r = rtlsdr_read_sync(dev, buffer, out_block_size, &n_read); if (r < 0) { fprintf(stderr, "WARNING: sync read failed.\n"); break; } if ((iq_frames_to_read) && (iq_frames_to_read < ((uint32_t)n_read /2))) { n_read = 2U * iq_frames_to_read; do_exit = 1; } if (!waveHdrStarted) { size_t wr = fwrite(buffer, 1, n_read, file); if (wr != (size_t)n_read) { fprintf(stderr, "Short write (wrote %ld of %ld bytes), samples lost, exiting!\n" , (long)wr, (long)n_read ); break; } } else { if ( waveWriteSamples(file, buffer, n_read/2, 0) ) { fprintf(stderr, "Short write, samples lost, exiting!\n"); break; } } if ((uint32_t)n_read < out_block_size) { fprintf(stderr, "Short read, samples lost, exiting!\n"); break; } if (iq_frames_to_read) { if (iq_frames_to_read > ((uint32_t)n_read /2)) iq_frames_to_read -= n_read/2; else do_exit = 1; } } } else { fprintf(stderr, "Reading samples in async mode...\n"); r = rtlsdr_read_async(dev, rtlsdr_callback, (void *)file, 0, out_block_size); } if (file != stdout) { if (writeWav) { waveFinalizeHeader(file); fclose(file); remove(filename); /* delete, in case file already exists */ r = rename( tempfilename, filename ); /* #include */ if ( r ) fprintf( stderr, "%s: error %d '%s' renaming'%s' to '%s'\n" , argv[0], errno, strerror(errno), tempfilename, filename ); } else { fclose(file); } } if (do_exit) fprintf(stderr, "\nUser cancel, exiting...\n"); else fprintf(stderr, "\nLibrary error %d, exiting...\n", r); rtlsdr_close(dev); free (buffer); out: return r >= 0 ? r : -r; } ================================================ FILE: src/rtl_tcp.c ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2012 by Steve Markgraf * Copyright (C) 2012-2013 by Hoernchen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #ifndef _WIN32 #include #include #include #include #include #include #include #else #include #include "getopt/getopt.h" #define usleep(x) Sleep(x/1000) #endif #ifdef NEED_PTHREADS_WORKARROUND #define HAVE_STRUCT_TIMESPEC #endif #include #include #include #include #include "convenience/convenience.h" #include "convenience/rtl_convenience.h" #ifdef _WIN32 #pragma comment(lib, "ws2_32.lib") typedef int socklen_t; #else #define closesocket close #define SOCKADDR struct sockaddr #define SOCKET int #define SOCKET_ERROR -1 #endif #include "controlThread.h" static ctrl_thread_data_t ctrldata; static SOCKET s; static pthread_t tcp_worker_thread; static pthread_t command_thread; static pthread_cond_t exit_cond; static pthread_mutex_t exit_cond_lock; static pthread_mutex_t ll_mutex; static pthread_cond_t cond; struct llist { char *data; size_t len; struct llist *next; }; typedef struct { /* structure size must be multiple of 2 bytes */ char magic[4]; uint32_t tuner_type; uint32_t tuner_gain_count; } dongle_info_t; static rtlsdr_dev_t *dev = NULL; static int verbosity = 0; static uint32_t bandwidth = 0; static int enable_biastee = 0; static int global_numq = 0; static struct llist *ll_buffers = 0; static int llbuf_num = 500; static volatile int do_exit = 0; void usage(void) { fprintf(stderr, "rtl_tcp, an I/Q spectrum server for RTL2832 based SDR-receivers\n" "rtl_tcp version %d.%d %s (%s)\n" "rtl-sdr library %d.%d %s\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__, rtlsdr_get_version() >>16, rtlsdr_get_version() & 0xFFFF, rtlsdr_get_ver_id() ); fprintf(stderr, "Usage:\trtl_tcp [-a listen address]\n" "\t[-p control listen port (default: 1234)]\n" "\t[-r response listen port: 0 = off; 1 (=default) for On at control listen port +1; or port]\n" "\t[-I infrared sensor listen port (default: 0=none)]\n" "\t[-W infrared sensor query wait interval usec (default: 10000)]\n" "\t[-f frequency to tune to [Hz]]\n" "\t[-g gain in dB (default: 0 for auto)]\n" "\t[-s samplerate in Hz (default: 2048000 Hz)]\n" "\t[-b number of buffers (default: 15, set by library)]\n" "\t[-l length of single buffer in units of 512 samples (default: 32 was 256)]\n" "\t[-n max number of linked list buffers to keep (default: 500)]\n" "\t[-w rtlsdr tuner bandwidth [Hz] (for R820T/2 and E4000 tuners)]\n" "\t[-d device index or serial (default: 0)]\n" "\t[-P ppm_error (default: 0)]\n" "%s" "\t[-T enable bias-T on GPIO PIN 0 (works for rtl-sdr.com v3 dongles)]\n" "\t[-D direct_sampling_mode (default: 0, 1 = I, 2 = Q, 3 = I below threshold, 4 = Q below threshold)]\n" "\t[-D direct_sampling_threshold_frequency (default: 0 use tuner specific frequency threshold for 3 and 4)]\n" "\t[-v increase verbosity (default: 0)]\n" , rtlsdr_get_opt_help(1) ); exit(1); } #ifdef _WIN32 int gettimeofday(struct timeval *tv, void* ignored) { FILETIME ft; unsigned __int64 tmp = 0; if (NULL != tv) { GetSystemTimeAsFileTime(&ft); tmp |= ft.dwHighDateTime; tmp <<= 32; tmp |= ft.dwLowDateTime; tmp /= 10; #ifdef _MSC_VER tmp -= 11644473600000000Ui64; #else tmp -= 11644473600000000ULL; #endif tv->tv_sec = (long)(tmp / 1000000UL); tv->tv_usec = (long)(tmp % 1000000UL); } return 0; } BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dev); return TRUE; } return FALSE; } #else static void sighandler(int signum) { fprintf(stderr, "Signal caught, exiting!\n"); rtlsdr_cancel_async(dev); do_exit = 1; } #endif void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) { if(!do_exit) { struct llist *rpt = (struct llist*)malloc(sizeof(struct llist)); rpt->data = (char*)malloc(len); memcpy(rpt->data, buf, len); rpt->len = len; rpt->next = NULL; pthread_mutex_lock(&ll_mutex); if (ll_buffers == NULL) { ll_buffers = rpt; } else { struct llist *cur = ll_buffers; int num_queued = 0; while (cur->next != NULL) { cur = cur->next; num_queued++; } if(llbuf_num && llbuf_num == num_queued-2){ struct llist *curelem; free(ll_buffers->data); curelem = ll_buffers->next; free(ll_buffers); ll_buffers = curelem; } cur->next = rpt; if ( verbosity ) { if (num_queued > global_numq) printf("ll+, now %d\n", num_queued); else if (num_queued < global_numq) printf("ll-, now %d\n", num_queued); } global_numq = num_queued; } pthread_cond_signal(&cond); pthread_mutex_unlock(&ll_mutex); } } static void *tcp_worker(void *arg) { struct llist *curelem,*prev; int bytesleft,bytessent, index; struct timeval tv= {1,0}; struct timespec ts; struct timeval tp; fd_set writefds; int r = 0; while(1) { if(do_exit) pthread_exit(0); pthread_mutex_lock(&ll_mutex); gettimeofday(&tp, NULL); ts.tv_sec = tp.tv_sec+1; ts.tv_nsec = tp.tv_usec * 1000; r = pthread_cond_timedwait(&cond, &ll_mutex, &ts); if(r == ETIMEDOUT) { pthread_mutex_unlock(&ll_mutex); printf("worker cond timeout\n"); sighandler(0); pthread_exit(NULL); } curelem = ll_buffers; ll_buffers = 0; pthread_mutex_unlock(&ll_mutex); while(curelem != 0) { bytesleft = curelem->len; index = 0; bytessent = 0; while(bytesleft > 0) { FD_ZERO(&writefds); FD_SET(s, &writefds); tv.tv_sec = 1; tv.tv_usec = 0; r = select(s+1, NULL, &writefds, NULL, &tv); if(r) { bytessent = send(s, &curelem->data[index], bytesleft, 0); bytesleft -= bytessent; index += bytessent; } if(bytessent == SOCKET_ERROR || do_exit) { printf("worker socket bye\n"); sighandler(0); pthread_exit(NULL); } } prev = curelem; curelem = curelem->next; free(prev->data); free(prev); } } } static int set_gain_by_index(rtlsdr_dev_t *_dev, unsigned int index) { int res = 0; int* gains; int count = rtlsdr_get_tuner_gains(_dev, NULL); if (count > 0 && index < (unsigned int)count) { gains = malloc(sizeof(int) * count); count = rtlsdr_get_tuner_gains(_dev, gains); if (verbosity) printf("set tuner gain to %.1f dB\n", gains[index] / 10.0); res = rtlsdr_set_tuner_gain(_dev, gains[index]); if (res < 0) printf(" setting tuner gain index failed\n"); free(gains); } else { printf("set tuner gain index to %u\n", index); printf(" error setting tuner gain index failed: valid range: 0 .. %d\n", count-1); } return res; } static void check_tuner_pll(rtlsdr_dev_t *dev, int *tuner_unsupported, int *last_lock_report) { int r = rtlsdr_is_tuner_PLL_locked(dev); /* printf("performed lock check:\n"); */ if (r == 1) { if (*last_lock_report != r) printf("tuner PLL is unlocked!\n"); *last_lock_report = r; } else if (r == 0) { if (*last_lock_report != r) printf("tuner PLL is locked.\n"); *last_lock_report = r; } else if (r == -2) { printf("error at PLL-locked check: tuner not supported! No further tests.\n"); *tuner_unsupported = 1; } else if (r < 0) printf("error checking tuner PLL!\n"); else printf("unknown error at tuner PLL check!\n"); fflush(stdout); } #ifdef _WIN32 #define __attribute__(x) #pragma pack(push, 1) #endif struct command{ unsigned char cmd; unsigned int param; }__attribute__((packed)); #ifdef _WIN32 #pragma pack(pop) #endif static void *command_worker(void *arg) { int left, received = 0; fd_set readfds; struct command cmd={0, 0}; struct timeval tv= {1, 0}; unsigned tuner_check_timeout = 0; int last_lock_report = -1; int tuner_unsupported = 0; int r = 0; uint32_t freqhi = 0; uint64_t tmp64; uint32_t tmp; int32_t itmp; int32_t if_band_center_freq; int iitmp; while(1) { left=sizeof(cmd); while(left >0) { FD_ZERO(&readfds); FD_SET(s, &readfds); tv.tv_sec = 1; tv.tv_usec = 0; r = select(s+1, &readfds, NULL, NULL, &tv); if(r) { received = recv(s, (char*)&cmd+(sizeof(cmd)-left), left, 0); left -= received; /* printf("received %d bytes\n", received); */ } else if (!tuner_unsupported) { /* timeout: nothing happend */ ++tuner_check_timeout; if (tuner_check_timeout >= 3) { /* automatic check every 3 seconds */ check_tuner_pll(dev, &tuner_unsupported, &last_lock_report); tuner_check_timeout = 0; } fflush(stdout); } if(received == SOCKET_ERROR || do_exit) { printf("comm recv bye\n"); sighandler(0); pthread_exit(NULL); } } switch(cmd.cmd) { case SET_FREQUENCY: tmp = ntohl(cmd.param); if (!freqhi) { printf("set freq %f MHz\n", tmp * 1E-6); r = rtlsdr_set_center_freq(dev, tmp); if (r < 0) { printf(" error setting frequency!\n"); last_lock_report = -1; } } else { tmp64 = ( ((uint64_t)freqhi) << 32 ) | (uint64_t)tmp; printf("set freq64 %f MHz\n", tmp64 * 1E-6); r = rtlsdr_set_center_freq64(dev, tmp64); if (r < 0) { printf(" error setting frequency!\n"); last_lock_report = -1; } } freqhi = 0; break; case SET_FREQ_HI32: freqhi = ntohl(cmd.param); break; case SET_SAMPLE_RATE: tmp = ntohl(cmd.param); printf("set sample rate %u\n", tmp); r = rtlsdr_set_sample_rate(dev, tmp); if (r < 0) printf(" error setting sample rate! sample rate is %u\n", rtlsdr_get_sample_rate(dev)); break; case SET_GAIN_MODE: tmp = ntohl(cmd.param); printf("set gain mode %u (=%s)\n", tmp, tmp?"manual":"automatic"); r = rtlsdr_set_tuner_gain_mode(dev, tmp); if (r < 0) printf(" error setting gain mode!\n"); break; case SET_GAIN: tmp = ntohl(cmd.param); printf("set manual tuner gain %.1f dB\n", tmp/10.0); r = rtlsdr_set_tuner_gain(dev, tmp); if (r < 0) printf(" error setting tuner gain!\n"); break; case SET_FREQUENCY_CORRECTION: itmp = ntohl(cmd.param); printf("set freq correction %d ppm\n", itmp); r = rtlsdr_set_freq_correction(dev, itmp); if (r < 0) { printf(" error setting frequency correction!\n"); last_lock_report = -1; } break; case SET_IF_STAGE: tmp = ntohl(cmd.param); printf("set if stage %d gain %.1f dB\n", tmp >> 16, ((short)(tmp & 0xffff))/10.0); r = rtlsdr_set_tuner_if_gain(dev, tmp >> 16, (short)(tmp & 0xffff)); if (r < 0) printf(" error setting gain for stage!\n"); break; case SET_TEST_MODE: tmp = ntohl(cmd.param); printf("set test mode %d (=%s)\n", tmp, tmp?"active":"inactive"); r = rtlsdr_set_testmode(dev, tmp); if (r < 0) printf(" error setting test mode!\n"); break; case SET_AGC_MODE: tmp = ntohl(cmd.param); printf("set rtl2832's digital agc mode %d (=%s)\n", tmp, tmp?"enabled":"disabled"); r = rtlsdr_set_agc_mode(dev, tmp); if (r < 0) printf(" error setting digital agc mode!\n"); break; case SET_DIRECT_SAMPLING: tmp = ntohl(cmd.param); printf("set direct sampling %u (=%s)\n", tmp, (!tmp) ? "disabled": (tmp==1)?"pin I-ADC": (tmp==2)? "pin Q-ADC":"unknown!"); r = rtlsdr_set_direct_sampling(dev, tmp); if (r < 0) printf(" error setting direct sampling!\n"); break; case SET_OFFSET_TUNING: itmp = ntohl(cmd.param); printf("set offset tuning %d\n", itmp); r = rtlsdr_set_offset_tuning(dev, itmp); if (r < 0) { printf(" error setting offset tuning!\n"); last_lock_report = -1; } break; case SET_RTL_CRYSTAL: printf("set rtl xtal frequency %d\n", ntohl(cmd.param)); r = rtlsdr_set_xtal_freq(dev, ntohl(cmd.param), 0); if (r < 0) printf(" error setting rtl xtal frequency!\n"); break; case SET_TUNER_CRYSTAL: printf("set tuner xtal %d\n", ntohl(cmd.param)); r = rtlsdr_set_xtal_freq(dev, 0, ntohl(cmd.param)); if (r < 0) printf(" error setting tuner xtal frequency!\n"); break; case SET_TUNER_GAIN_BY_INDEX: tmp = ntohl(cmd.param); printf("set tuner gain by index %u\n", tmp); set_gain_by_index(dev, tmp); break; case SET_BIAS_TEE: tmp = ntohl(cmd.param); printf("set bias T %u (%s)\n", tmp, tmp?"on":"off"); r = rtlsdr_set_bias_tee(dev, tmp); if (r < 0) printf(" error setting bias tee!\n"); break; case SET_TUNER_BANDWIDTH: bandwidth = ntohl(cmd.param); printf("set tuner bandwidth to %i Hz\n", bandwidth); verbose_set_bandwidth(dev, bandwidth); break; case SET_I2C_TUNER_REGISTER: tmp = ntohl(cmd.param); printf("set i2c register x%03X to x%03X with mask x%02X\n", (tmp >> 20) & 0xfff, tmp & 0xfff, (tmp >> 12) & 0xff ); r = rtlsdr_set_tuner_i2c_register(dev, (tmp >> 20) & 0xfff, (tmp >> 12) & 0xff, tmp & 0xfff); if (r < 0) printf(" error setting i2c register!\n"); break; case SET_I2C_TUNER_OVERRIDE: tmp = ntohl(cmd.param); printf("set i2c override register x%03X to x%03X with mask x%02X\n", (tmp >> 20) & 0xfff, tmp & 0xfff, (tmp >> 12) & 0xff ); r = rtlsdr_set_tuner_i2c_override(dev, (tmp >> 20) & 0xfff, (tmp >> 12) & 0xff, tmp & 0xfff); if (r < 0) printf(" error setting i2c register!\n"); break; case UDP_TERMINATE: printf("comm recv bye\n"); sighandler(0); pthread_exit(NULL); break; case SET_TUNER_BW_IF_CENTER: if_band_center_freq = ntohl(cmd.param); printf("set tuner band to IF frequency %i Hz from center\n", if_band_center_freq); r = rtlsdr_set_tuner_band_center(dev, if_band_center_freq ); if (r < 0) printf(" error setting tuner band's IF center frequency!\n"); break; case SET_TUNER_IF_MODE: itmp = ntohl(cmd.param); printf("set tuner IF mode to %i: ", itmp); if (!itmp) printf("automatic gain of VGA controlled from RTL2832\n"); else if (-2500 <= itmp && itmp <= 2500) printf("VGA nearest to %.1f dB)\n", itmp/10.0); else if (10000 <=itmp && itmp <= 10015) printf("VGA gain idx %d\n", itmp - 10000); else if (10016 <= itmp && itmp <= 10031) printf("VGA gain idx %d - but with automatic gain of VGA controlled from RTL2832\n", itmp-10016); else printf("unknown!\n"); r = rtlsdr_set_tuner_if_mode(dev, itmp); if (r < 0) printf(" error setting tuner IF mode!\n"); break; case SET_SIDEBAND: tmp = ntohl(cmd.param); if(tmp) tmp = 1; printf("set tuner sideband %d: %s sideband\n", tmp, (tmp ? "upper" : "lower") ); r = rtlsdr_set_tuner_sideband(dev, tmp); if (r < 0) { printf(" error setting tuner sideband!\n"); last_lock_report = -1; } break; case REPORT_I2C_REGS: tmp = ntohl(cmd.param); if(tmp) tmp = 1; ctrldata.report_i2c = tmp; /* (de)activate reporting */ break; case GPIO_SET_OUTPUT_MODE: /* rtlsdr_set_gpio_output() */ itmp = ntohl(cmd.param); if ( 0 <= itmp && itmp < 8 ) { printf("set gpio pin %d to output\n", itmp); r = rtlsdr_set_gpio_output(dev, (uint8_t)itmp); if (r < 0) printf(" error setting gpio pin to output mode!\n"); } else printf("set gpio pin %d to output: error: pin has to be in 0 .. 7\n", itmp); break; case GPIO_SET_INPUT_MODE: /* rtlsdr_set_gpio_input() */ itmp = ntohl(cmd.param); if ( 0 <= itmp && itmp < 8 ) { printf("set gpio pin %d to input\n", itmp); r = rtlsdr_set_gpio_input(dev, (uint8_t)itmp); if (r < 0) printf(" error setting gpio pin to input mode!\n"); } else printf("set gpio pin %d to input: error: pin has to be in 0 .. 7\n", itmp); break; case GPIO_GET_IO_STATUS: /* rtlsdr_set_gpio_status() */ r = rtlsdr_set_gpio_status(dev, &iitmp ); if (r < 0) printf("error at requesting gpio io status!\n"); else printf("request for gpio io status: 0x%02x = %d%d%d%d %d%d%d%d for bits 7 .. 0\n", iitmp & 0xff, (iitmp >>7) & 1, (iitmp >>6) & 1, (iitmp >>5) & 1, (iitmp >>4) & 1, (iitmp >>3) & 1, (iitmp >>2) & 1, (iitmp >>1) & 1, iitmp & 1 ); break; case GPIO_WRITE_PIN: /* rtlsdr_set_gpio_bit() */ itmp = ntohl(cmd.param); if ( 0 <= ((itmp >> 16) & 0xffff) && ((itmp >> 16) & 0xffff) < 8 ) { printf("write %d to gpio %d\n", itmp & 0xffff, (itmp >> 16) & 0xffff); rtlsdr_set_gpio_output(dev, (uint8_t)((itmp >> 16) & 0xffff)); rtlsdr_set_gpio_bit(dev, (uint8_t)((itmp >> 16) & 0xffff), itmp & 0xffff); } else printf("write %d to gpio %d: error: pin has to be in 0 .. 7\n", itmp & 0xffff, (itmp >> 16) & 0xffff); break; case GPIO_READ_PIN: itmp = ntohl(cmd.param); if ( 0 <= itmp && itmp < 8 ) { r = rtlsdr_get_gpio_bit(dev, itmp, &iitmp); if (r < 0) printf(" error reading gpio pin!\n"); else printf("read gpio pin %d: %d\n", itmp, iitmp); } else printf("read gpio pin %d out of range: pin has to be in 0 .. 7\n", itmp); break; case GPIO_GET_BYTE: r = rtlsdr_get_gpio_byte(dev, &iitmp); if (r < 0) printf("error reading gpio byte!\n"); else printf("read gpio byte: 0x%02x = %d%d%d%d %d%d%d%d for bits 7 .. 0\n", iitmp & 0xff, (iitmp >>7) & 1, (iitmp >>6) & 1, (iitmp >>5) & 1, (iitmp >>4) & 1, (iitmp >>3) & 1, (iitmp >>2) & 1, (iitmp >>1) & 1, iitmp & 1 ); break; case IS_TUNER_PLL_LOCKED: itmp = -1; /* always print lock status */ check_tuner_pll(dev, &tuner_unsupported, &itmp); if (itmp != -1) last_lock_report = itmp; tuner_check_timeout = 0; break; default: printf("unknown command 0x%02x\n", cmd.cmd); break; } cmd.cmd = 0xff; } } struct ir_thread_data { rtlsdr_dev_t *dev; int port; int wait; char *addr; }; void *ir_thread_fn(void *arg) { int r = 1; struct linger ling = {1,0}; SOCKET listensocket; SOCKET irsocket; struct sockaddr_in local, remote; socklen_t rlen; uint8_t buf[128]; int ret = 0, len; struct ir_thread_data *data = (struct ir_thread_data *)arg; rtlsdr_dev_t *dev = data->dev; int port = data->port; int wait = data->wait; char *addr = data->addr; memset(&local,0,sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(port); local.sin_addr.s_addr = inet_addr(addr); listensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); r = 1; setsockopt(listensocket, SOL_SOCKET, SO_REUSEADDR, (char *)&r, sizeof(int)); setsockopt(listensocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); bind(listensocket,(struct sockaddr *)&local,sizeof(local)); while(1) { printf("listening on IR port %d...\n", port); listen(listensocket,1); irsocket = accept(listensocket,(struct sockaddr *)&remote, &rlen); setsockopt(irsocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); printf("IR client accepted!\n"); while(1) { ret = rtlsdr_ir_query(dev, buf, sizeof(buf)); if (ret < 0) { printf("rtlsdr_ir_query error %d\n", ret); break; } len = ret; ret = send(irsocket, buf, len, 0); if (ret != len){ printf("incomplete write to ir client: %d != %d\n", ret,len); break; } usleep(wait); } closesocket(irsocket); } return 0; } int main(int argc, char **argv) { int r, opt, i; char* addr = "127.0.0.1"; int port = 1234; int port_ir = 0; int wait_ir = 10000; pthread_t thread_ir; pthread_t thread_ctrl; /* -cs- for periodically reading the register values */ int port_resp = 1; int report_i2c = 0; int do_exit_thrd_ctrl = 0; uint64_t frequency = 100000000; uint32_t samp_rate = 2048000; enum rtlsdr_ds_mode ds_mode = RTLSDR_DS_IQ; uint32_t ds_temp, ds_threshold = 0; struct sockaddr_in local, remote; uint32_t buf_num = 0; /* buf_len: * must be multiple of 512 - else it will be overwritten * in rtlsdr_read_async() in librtlsdr.c with DEFAULT_BUF_LENGTH (= 16*32 *512 = 512 *512) * * -> 512*512 -> 1048 ms @ 250 kS or 81.92 ms @ 3.2 MS (internal default) * -> 32*512 -> 65 ms @ 250 kS or 5.12 ms @ 3.2 MS (new default) * * usual soundcard as reference: * 512 samples @ 48 kHz ~= 10.6 ms * 512 samples @ 8 kHz = 64 ms */ uint32_t buf_len = 32 * 512; const char * rtlOpts = NULL; int dev_index = 0; int dev_given = 0; int gain = 0; int ppm_error = 0; struct llist *curelem,*prev; pthread_attr_t attr; void *status; struct timeval tv = {1,0}; struct linger ling = {1,0}; SOCKET listensocket; socklen_t rlen; fd_set readfds; u_long blockmode = 1; dongle_info_t dongle_info; int gains[100]; const char * opt_str = NULL; #ifdef _WIN32 WSADATA wsd; i = WSAStartup(MAKEWORD(2,2), &wsd); #else struct sigaction sigact, sigign; #endif opt_str = "a:p:f:g:s:b:n:d:P:O:TI:W:l:w:D:vr:"; while ((opt = getopt(argc, argv, opt_str)) != -1) { switch (opt) { case 'd': dev_index = verbose_device_search(optarg); dev_given = 1; break; case 'f': frequency = (uint64_t)( atofs(optarg) + 0.5 ); break; case 'g': gain = (int)(atof(optarg) * 10); /* tenths of a dB */ break; case 's': samp_rate = (uint32_t)atofs(optarg); break; case 'a': addr = optarg; break; case 'p': port = atoi(optarg); break; case 'r': port_resp = atoi(optarg); report_i2c = 0; break; case 'I': port_ir = atoi(optarg); break; case 'W': wait_ir = atoi(optarg); break; case 'b': buf_num = atoi(optarg); break; case 'l': buf_len = 512 * atoi(optarg); break; case 'n': llbuf_num = atoi(optarg); break; case 'P': ppm_error = atoi(optarg); break; case 'O': rtlOpts = optarg; break; case 'T': enable_biastee = 1; break; case 'w': bandwidth = (uint32_t)atofs(optarg); break; case 'v': ++verbosity; break; case 'D': ds_temp = (uint32_t)( atofs(optarg) + 0.5 ); if (ds_temp <= RTLSDR_DS_Q_BELOW) ds_mode = (enum rtlsdr_ds_mode)ds_temp; else ds_threshold = ds_temp; break; default: usage(); break; } } if (argc < optind) usage(); if (verbosity) fprintf(stderr, "verbosity set to %d\n", verbosity); if (!dev_given) { dev_index = verbose_device_search("0"); } if (dev_index < 0) { exit(1); } rtlsdr_open(&dev, (uint32_t)dev_index); if (NULL == dev) { fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); exit(1); } #ifndef _WIN32 sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigign.sa_handler = SIG_IGN; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigign, NULL); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif /* Set the tuner error */ verbose_ppm_set(dev, ppm_error); /* Set the sample rate */ r = rtlsdr_set_sample_rate(dev, samp_rate); if (r < 0) fprintf(stderr, "WARNING: Failed to set sample rate.\n"); if (rtlOpts) { rtlsdr_set_opt_string(dev, rtlOpts, verbosity); } /* Set direct sampling with threshold */ rtlsdr_set_ds_mode(dev, ds_mode, ds_threshold); /* Set the frequency */ r = rtlsdr_set_center_freq64(dev, frequency); if (r < 0) fprintf(stderr, "WARNING: Failed to set center freq.\n"); else fprintf(stderr, "Tuned to %f MHz.\n", frequency * 1E-6); if (0 == gain) { /* Enable automatic gain */ r = rtlsdr_set_tuner_gain_mode(dev, 0); if (r < 0) fprintf(stderr, "WARNING: Failed to enable automatic gain.\n"); } else { /* Enable manual gain */ r = rtlsdr_set_tuner_gain_mode(dev, 1); if (r < 0) fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); /* Set the tuner gain */ r = rtlsdr_set_tuner_gain(dev, gain); if (r < 0) fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); else fprintf(stderr, "Tuner gain set to %f dB.\n", gain/10.0); } verbose_set_bandwidth(dev, bandwidth); rtlsdr_set_bias_tee(dev, enable_biastee); if (enable_biastee) fprintf(stderr, "activated bias-T on GPIO PIN 0\n"); /* Reset endpoint before we start reading from it (mandatory) */ r = rtlsdr_reset_buffer(dev); if (r < 0) fprintf(stderr, "WARNING: Failed to reset buffers.\n"); pthread_mutex_init(&exit_cond_lock, NULL); pthread_mutex_init(&ll_mutex, NULL); pthread_mutex_init(&exit_cond_lock, NULL); pthread_cond_init(&cond, NULL); pthread_cond_init(&exit_cond, NULL); if (port_ir) { struct ir_thread_data data = {.dev = dev, .port = port_ir, .wait = wait_ir, .addr = addr}; pthread_create(&thread_ir, NULL, &ir_thread_fn, (void *)(&data)); } #if 0 fprintf(stderr, "enabling Response channel with I2C reporting\n"); port_resp = 1; report_i2c = 1; #endif if ( port_resp == 1 ) port_resp = port + 1; ctrldata.port = port_resp; ctrldata.dev = dev; ctrldata.addr = addr; ctrldata.wait = 500000; /* = 0.5 sec */ ctrldata.report_i2c = report_i2c; ctrldata.pDoExit = &do_exit_thrd_ctrl; if ( port_resp ) { fprintf(stderr, "activating Response channel on port %d with %s I2C reporting\n" , port_resp, (report_i2c ? "active" : "inactive") ); pthread_create(&thread_ctrl, NULL, &ctrl_thread_fn, &ctrldata); } memset(&local,0,sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(port); local.sin_addr.s_addr = inet_addr(addr); listensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); r = 1; setsockopt(listensocket, SOL_SOCKET, SO_REUSEADDR, (char *)&r, sizeof(int)); setsockopt(listensocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); bind(listensocket,(struct sockaddr *)&local,sizeof(local)); #ifdef _WIN32 ioctlsocket(listensocket, FIONBIO, &blockmode); #else r = fcntl(listensocket, F_GETFL, 0); r = fcntl(listensocket, F_SETFL, r | O_NONBLOCK); #endif while(1) { printf("listening...\n"); printf("Use the device argument 'rtl_tcp=%s:%d' in OsmoSDR " "(gr-osmosdr) source\n" "to receive samples in GRC and control " "rtl_tcp parameters (frequency, gain, ...).\n", addr, port); listen(listensocket,1); while(1) { FD_ZERO(&readfds); FD_SET(listensocket, &readfds); tv.tv_sec = 1; tv.tv_usec = 0; r = select(listensocket+1, &readfds, NULL, NULL, &tv); if(do_exit) { goto out; } else if(r) { rlen = sizeof(remote); s = accept(listensocket,(struct sockaddr *)&remote, &rlen); break; } } setsockopt(s, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); printf("client accepted!\n"); memset(&dongle_info, 0, sizeof(dongle_info)); memcpy(&dongle_info.magic, "RTL0", 4); r = rtlsdr_get_tuner_type(dev); if (r >= 0) dongle_info.tuner_type = htonl(r); r = rtlsdr_get_tuner_gains(dev, gains); if (r >= 0) dongle_info.tuner_gain_count = htonl(r); if (verbosity) { fprintf(stderr, "Supported gain values (%d): ", r); for (i = 0; i < r; i++) fprintf(stderr, "%.1f ", gains[i] / 10.0); fprintf(stderr, "\n"); } r = send(s, (const char *)&dongle_info, sizeof(dongle_info), 0); if (sizeof(dongle_info) != r) printf("failed to send dongle information\n"); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); r = pthread_create(&tcp_worker_thread, &attr, tcp_worker, NULL); r = pthread_create(&command_thread, &attr, command_worker, NULL); pthread_attr_destroy(&attr); r = rtlsdr_read_async(dev, rtlsdr_callback, NULL, buf_num, buf_len); pthread_join(tcp_worker_thread, &status); pthread_join(command_thread, &status); closesocket(s); printf("all threads dead..\n"); curelem = ll_buffers; ll_buffers = 0; while(curelem != 0) { prev = curelem; curelem = curelem->next; free(prev->data); free(prev); } do_exit = 0; global_numq = 0; } out: rtlsdr_close(dev); closesocket(listensocket); /* if (port_ir) pthread_join(thread_ir, &status); */ if ( port_resp ) { do_exit_thrd_ctrl = 1; pthread_join(thread_ctrl, &status); } closesocket(s); #ifdef _WIN32 WSACleanup(); #endif printf("bye!\n"); return r >= 0 ? r : -r; } ================================================ FILE: src/rtl_test.c ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * rtl_test, test and benchmark tool * * Copyright (C) 2012-2014 by Steve Markgraf * Copyright (C) 2012-2014 by Kyle Keen * Copyright (C) 2014 by Michael Tatarinov * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #ifdef __APPLE__ #include #else #include #endif #ifndef _WIN32 #include #else #include #include "getopt/getopt.h" #endif #include #include #include "convenience/convenience.h" #include "convenience/rtl_convenience.h" #define DEFAULT_SAMPLE_RATE 2048000 #define DEFAULT_BUF_LENGTH (16 * 16384) #define MINIMAL_BUF_LENGTH 512 #define MAXIMAL_BUF_LENGTH (256 * 16384) #define MHZ(x) ((x)*1000*1000) #define PPM_DURATION 10 #define PPM_DUMP_TIME 5 #define DETAILED_LOST_MSG 0 struct time_generic /* holds all the platform specific values */ { #ifndef _WIN32 time_t tv_sec; long tv_nsec; #else long tv_sec; long tv_nsec; int init; LARGE_INTEGER frequency; LARGE_INTEGER ticks; #endif }; static enum { NO_BENCHMARK, TUNER_BENCHMARK, PPM_BENCHMARK } test_mode = NO_BENCHMARK; static int do_exit = 0; static rtlsdr_dev_t *dev = NULL; static uint32_t samp_rate = DEFAULT_SAMPLE_RATE; static uint32_t bufferNo = 0; static uint32_t total_samples = 0; static uint32_t dropped_samples = 0; static unsigned int ppm_duration = PPM_DURATION; void usage(void) { fprintf(stderr, "rtl_test, a benchmark tool for RTL2832 based SDR-receivers\n" "rtl_test version %d.%d %s (%s)\n" "rtl-sdr library %d.%d %s\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__, rtlsdr_get_version() >>16, rtlsdr_get_version() & 0xFFFF, rtlsdr_get_ver_id() ); fprintf(stderr, "Usage:\trtl_test [-options]\n" "\t[-s samplerate (default: 2048000 Hz)]\n" "\t[-d device_index or serial (default: 0)]\n" "%s" "\t[-t enable tuner range benchmark]\n" "\t[-f first/begin frequency for tuner range benchmark, default: 0]\n" "\t[-e end frequency for tuner range benchmark, default: 3e9 = 3G ]\n" #ifndef _WIN32 "\t[-p[seconds] enable PPM error measurement (default: 10 seconds)]\n" #endif "\t[-b output_block_size (default: 16 * 16384)]\n" "\t[-S force sync output (default: async)]\n" , rtlsdr_get_opt_help(1) ); exit(1); } #ifdef _WIN32 BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { fprintf(stderr, "\nSignal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dev); return TRUE; } return FALSE; } #else static void sighandler(int signum) { fprintf(stderr, "\nSignal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dev); } #endif static void underrun_test(unsigned char *buf, uint32_t len, int mute) { uint32_t i, lost = 0; static uint8_t bcnt, uninit = 1; #if DETAILED_LOST_MSG uint32_t n_incont = 0, first_incont_sample = 0; uint8_t err_from, err_to; #endif if (uninit) { bcnt = buf[0]; uninit = 0; } for (i = 0; i < len; i++) { if(bcnt != buf[i]) { #if DETAILED_LOST_MSG if (!first_incont_sample) { first_incont_sample = 1 + i; err_from = bcnt; err_to = buf[i]; } ++n_incont; #endif lost += (buf[i] > bcnt) ? (buf[i] - bcnt) : (bcnt - buf[i]); bcnt = buf[i]; } bcnt++; } total_samples += len; dropped_samples += lost; if (mute) return; if (lost) printf("lost at least %3u bytes in buffer %u" #if DETAILED_LOST_MSG " in %u incontinuities at byte %4u switching from %3u to %3u" #endif "\n", (unsigned)lost, (unsigned)bufferNo #if DETAILED_LOST_MSG , (unsigned)n_incont, (unsigned)(first_incont_sample-1) , (unsigned)err_from, (unsigned)err_to #endif ); ++bufferNo; } #ifndef _WIN32 static int ppm_gettime(struct time_generic *tg) { int rv = ENOSYS; struct timespec ts; #ifdef __unix__ rv = clock_gettime(CLOCK_MONOTONIC, &ts); tg->tv_sec = ts.tv_sec; tg->tv_nsec = ts.tv_nsec; #elif __APPLE__ struct timeval tv; rv = gettimeofday(&tv, NULL); tg->tv_sec = tv.tv_sec; tg->tv_nsec = tv.tv_usec * 1000; #endif return rv; } #endif #ifdef _WIN32 static int ppm_gettime(struct time_generic *tg) { int rv; int64_t frac; if (!tg->init) { QueryPerformanceFrequency(&tg->frequency); tg->init = 1; } rv = QueryPerformanceCounter(&tg->ticks); tg->tv_sec = tg->ticks.QuadPart / tg->frequency.QuadPart; frac = (int64_t)(tg->ticks.QuadPart - (tg->tv_sec * tg->frequency.QuadPart)); tg->tv_nsec = (long)(frac * 1000000000L / (int64_t)tg->frequency.QuadPart); return !rv; } #endif static int ppm_report(uint64_t nsamples, uint64_t interval) { double real_rate, ppm; real_rate = nsamples * 1e9 / interval; ppm = 1e6 * (real_rate / (double)samp_rate - 1.); return (int)round(ppm); } static void ppm_test(uint32_t len) { static uint64_t nsamples = 0; static uint64_t interval = 0; static uint64_t nsamples_total = 0; static uint64_t interval_total = 0; struct time_generic ppm_now; static struct time_generic ppm_recent; static enum { PPM_INIT_NO, PPM_INIT_DUMP, PPM_INIT_RUN } ppm_init = PPM_INIT_NO; ppm_gettime(&ppm_now); if (ppm_init != PPM_INIT_RUN) { /* * Kyle Keen wrote: * PPM_DUMP_TIME throws out the first N seconds of data. * The dongle's PPM is usually very bad when first starting up, * typically incorrect by more than twice the final value. * Discarding the first few seconds allows the value to stabilize much faster. */ if (ppm_init == PPM_INIT_NO) { ppm_recent.tv_sec = ppm_now.tv_sec + PPM_DUMP_TIME; ppm_init = PPM_INIT_DUMP; return; } if (ppm_init == PPM_INIT_DUMP && ppm_recent.tv_sec < ppm_now.tv_sec) return; ppm_recent = ppm_now; ppm_init = PPM_INIT_RUN; return; } nsamples += (uint64_t)(len / 2UL); interval = (uint64_t)(ppm_now.tv_sec - ppm_recent.tv_sec); if (interval < ppm_duration) return; interval *= 1000000000UL; interval += (int64_t)(ppm_now.tv_nsec - ppm_recent.tv_nsec); nsamples_total += nsamples; interval_total += interval; printf("real sample rate: %i current PPM: %i cumulative PPM: %i\n", (int)((1000000000UL * nsamples) / interval), ppm_report(nsamples, interval), ppm_report(nsamples_total, interval_total)); ppm_recent = ppm_now; nsamples = 0; } static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) { underrun_test(buf, len, 0); if (test_mode == PPM_BENCHMARK) ppm_test(len); } /* smallest band or band gap that tuner_benchmark() will notice */ static uint32_t max_step(uint32_t freq) { if (freq < 1e6) return 1e4; if (freq > 1e8) return 1e6; return freq / 1e2; } /* precision with which tuner_benchmark() will measure the edges of bands */ static uint32_t min_step(uint32_t freq) { return 100; } static void report_band_start(uint32_t start) { if (start) fprintf(stderr, "Found a new band starting at %u Hz\n", start); } static void report_band(uint32_t low, uint32_t high) { if ( low != high && low ) fprintf(stderr, "Tuning band: %u - %u Hz\n\n", low, high); } static int set_center_freq_wait(rtlsdr_dev_t *dev, uint32_t freq, const char * state_text) { int rcA, rcB; rcA = rtlsdr_set_center_freq(dev, freq); if ( rcA == LIBUSB_ERROR_NO_DEVICE ) { fprintf(stderr, "%s: Got Error -4 for frequency %u .. retry after wait ..\n", state_text, (unsigned)freq); #ifdef _WIN32 Sleep(5); #else usleep(5000); #endif rcB = rtlsdr_set_center_freq(dev, freq); if ( rcB == LIBUSB_ERROR_NO_DEVICE ) { fprintf(stderr, "%s: Got Error -4 .. again. Giving Up!\n", state_text); return rcB; } fprintf(stderr, "%s: Now return code %d at retry.\n", state_text, rcB); return rcB; } return rcA; } void tuner_benchmark(uint32_t beg_freq, uint32_t end_freq) { uint32_t current = beg_freq; /* max_step(0); */ uint32_t band_start = 0; uint32_t low_bound = 0, high_bound = 0; int rc; char buf[20]; enum { FIND_START, REFINE_START, FIND_END, REFINE_END } state; fprintf(stderr, "Testing tuner range. This may take a couple of minutes..\n"); /* deactivate harmonic reception - in case switched on with cli options */ rtlsdr_set_harmonic_rx(dev, 0); /* Scan for tuneable frequencies coarsely. When we find something, * do a binary search to narrow down the exact edge of the band. * * This can potentially miss bands/gaps smaller than max_step(freq) * but it is a lot faster than exhaustively scanning everything. */ /* handle bands starting at 0Hz */ rc = set_center_freq_wait(dev, current, "FIND_START"); if (rc < 0) state = FIND_START; else { band_start = current; report_band_start(band_start); state = FIND_END; } while (current < end_freq && !do_exit) { switch (state) { case FIND_START: /* scanning for the start of a new band */ rc = set_center_freq_wait(dev, current, "FIND_START"); if (rc < 0) { if ( rc == LIBUSB_ERROR_NO_DEVICE ) { do_exit = 1; break; } /* still looking for a band */ low_bound = current; current += max_step(current); } else { /* new band, starting somewhere at or before current */ /* low_bound < start <= current */ high_bound = current; state = REFINE_START; } break; case REFINE_START: /* refining the start of a band */ /* low_bound < bandstart <= high_bound */ rc = set_center_freq_wait(dev, current, "REFINE_START"); if (rc == 0) { /* current is inside the band */ /* low_bound < bandstart <= current */ if (current - low_bound <= min_step(current)) { /* start found at low_bound */ band_start = current; report_band_start(band_start); low_bound = current; current = band_start + max_step(band_start); state = FIND_END; } else { /* binary search */ high_bound = current; current = (current + low_bound) / 2; } } else { if ( rc == LIBUSB_ERROR_NO_DEVICE ) { do_exit = 1; break; } /* current is outside the band */ /* current < bandstart <= high_bound */ if (high_bound - current <= min_step(current)) { /* start found at high_bound */ low_bound = band_start = high_bound; report_band_start(band_start); current = band_start + max_step(band_start); state = FIND_END; } else { /* binary search */ low_bound = current; current = (current + high_bound) / 2; } } break; case FIND_END: /* scanning for the end of the current band */ rc = set_center_freq_wait(dev, current, "FIND_END"); if (rc == 0) { /* still looking for the end of the band */ low_bound = current; current += max_step(current); } else { if ( rc == LIBUSB_ERROR_NO_DEVICE ) { do_exit = 1; break; } /* end found, coarsely */ /* low_bound <= bandend < current, refine it */ high_bound = current; state = REFINE_END; } break; case REFINE_END: /* refining the end of a band */ /* low_bound <= bandend < high_bound */ rc = set_center_freq_wait(dev, current, "REFINE_END"); if (rc < 0) { if ( rc == LIBUSB_ERROR_NO_DEVICE ) { do_exit = 1; break; } /* current is outside the band */ /* low_bound <= bandend < current */ if (current - low_bound <= min_step(current)) { /* band ends at low_bound */ report_band(band_start, low_bound); low_bound = current; current = low_bound + max_step(low_bound); state = FIND_START; } else { /* binary search */ high_bound = current; current = (current + low_bound) / 2; } } else { /* current is inside the band */ /* current <= bandend < high_bound */ if (high_bound - current <= min_step(current)) { /* band ends at high_bound */ report_band(band_start, current); low_bound = high_bound; current = low_bound + max_step(low_bound); state = FIND_START; } else { /* binary search */ low_bound = current; current = (current + high_bound) / 2; } } break; } } if ( rc == LIBUSB_ERROR_NO_DEVICE ) return; if (state == FIND_END) report_band(band_start, current); else if (state == REFINE_END) report_band(band_start, low_bound); } int main(int argc, char **argv) { #ifndef _WIN32 struct sigaction sigact; #endif int n_read, r, opt, i; int sync_mode = 0; const char * rtlOpts = NULL; uint8_t *buffer; int dev_index = 0; int dev_given = 0; uint32_t out_block_size = DEFAULT_BUF_LENGTH; uint32_t tuner_bench_beg_freq = 0; uint32_t tuner_bench_end_freq = 0; int count; int gains[100]; while ((opt = getopt(argc, argv, "d:s:b:O:tf:e:p::Sh")) != -1) { switch (opt) { case 'd': dev_index = verbose_device_search(optarg); dev_given = 1; break; case 's': samp_rate = (uint32_t)atofs(optarg); break; case 'b': out_block_size = (uint32_t)atof(optarg); break; case 'O': rtlOpts = optarg; break; case 't': test_mode = TUNER_BENCHMARK; break; case 'f': tuner_bench_beg_freq = (uint32_t)atofs(optarg); break; case 'e': tuner_bench_end_freq = (uint32_t)atofs(optarg); break; case 'p': test_mode = PPM_BENCHMARK; if (optarg) ppm_duration = atoi(optarg); break; case 'S': sync_mode = 1; break; case 'h': default: usage(); break; } } if(out_block_size < MINIMAL_BUF_LENGTH || out_block_size > MAXIMAL_BUF_LENGTH ){ fprintf(stderr, "Output block size wrong value, falling back to default\n"); fprintf(stderr, "Minimal length: %u\n", MINIMAL_BUF_LENGTH); fprintf(stderr, "Maximal length: %u\n", MAXIMAL_BUF_LENGTH); out_block_size = DEFAULT_BUF_LENGTH; } buffer = malloc(out_block_size * sizeof(uint8_t)); if (!dev_given) { dev_index = verbose_device_search("0"); } if (dev_index < 0) { exit(1); } r = rtlsdr_open(&dev, (uint32_t)dev_index); if (r < 0) { fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); exit(1); } #ifndef _WIN32 sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif count = rtlsdr_get_tuner_gains(dev, NULL); fprintf(stderr, "Supported gain values (%d): ", count); count = rtlsdr_get_tuner_gains(dev, gains); for (i = 0; i < count; i++) fprintf(stderr, "%.1f ", gains[i] / 10.0); fprintf(stderr, "\n"); /* Set the sample rate */ verbose_set_sample_rate(dev, samp_rate); /* set - especially sideband - before testing tuning range */ if (rtlOpts) { rtlsdr_set_opt_string(dev, rtlOpts, 1); } if (test_mode == TUNER_BENCHMARK) { tuner_benchmark(tuner_bench_beg_freq, tuner_bench_end_freq); goto exit; } /* Enable test mode */ r = rtlsdr_set_testmode(dev, 1); /* Reset endpoint before we start reading from it (mandatory) */ verbose_reset_buffer(dev); if ((test_mode == PPM_BENCHMARK) && !sync_mode) { fprintf(stderr, "Reporting PPM error measurement every %u seconds...\n", ppm_duration); fprintf(stderr, "Press ^C after a few minutes.\n"); } if (test_mode == NO_BENCHMARK) { fprintf(stderr, "\nInfo: This tool will continuously" " read from the device, and report if\n" "samples get lost. If you observe no " "further output, everything is fine.\n\n"); } if (sync_mode) { fprintf(stderr, "Reading samples in sync mode...\n"); fprintf(stderr, "(Samples are being lost but not reported.)\n"); while (!do_exit) { r = rtlsdr_read_sync(dev, buffer, out_block_size, &n_read); if (r < 0) { fprintf(stderr, "WARNING: sync read failed.\n"); break; } if ((uint32_t)n_read < out_block_size) { fprintf(stderr, "Short read, samples lost, exiting!\n"); break; } underrun_test(buffer, n_read, 1); } } else { fprintf(stderr, "Reading samples in async mode...\n"); r = rtlsdr_read_async(dev, rtlsdr_callback, NULL, 0, out_block_size); } if (do_exit) { fprintf(stderr, "\nUser cancel after %u buffers, exiting...\n", (unsigned)bufferNo ); fprintf(stderr, "Samples per million lost (minimum): %i\n", (int)(1000000L * dropped_samples / total_samples)); } else fprintf(stderr, "\nLibrary error %d after %u buffers, exiting...\n", r, (unsigned)bufferNo); exit: rtlsdr_close(dev); free (buffer); return r >= 0 ? r : -r; } ================================================ FILE: src/rtl_udp.c ================================================ /* * rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver * Copyright (C) 2012 by Steve Markgraf * Copyright (C) 2012-2013 by Hoernchen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #ifndef _WIN32 #include #include #include #include #include #include #include #else #include #include "getopt/getopt.h" #define usleep(x) Sleep(x/1000) #endif #ifdef NEED_PTHREADS_WORKARROUND #define HAVE_STRUCT_TIMESPEC #endif #include #include #include #include #include "convenience/convenience.h" #include "convenience/rtl_convenience.h" #ifdef _WIN32 #pragma comment(lib, "ws2_32.lib") typedef int socklen_t; #else #define closesocket close #define SOCKADDR struct sockaddr #define SOCKET int #define SOCKET_ERROR -1 #endif static SOCKET s; struct sockaddr_in remote; static pthread_t tcp_worker_thread; static pthread_t command_thread; static pthread_cond_t exit_cond; static pthread_mutex_t exit_cond_lock; static pthread_mutex_t ll_mutex; static pthread_cond_t cond; struct llist { char *data; size_t len; struct llist *next; }; typedef struct { /* structure size must be multiple of 2 bytes */ char magic[4]; uint32_t tuner_type; uint32_t tuner_gain_count; } dongle_info_t; static rtlsdr_dev_t *dev = NULL; static int verbosity = 0; static uint32_t bandwidth = 0; static int enable_biastee = 0; static int global_numq = 0; static struct llist *ll_buffers = 0; static int llbuf_num = 500; static volatile int do_exit = 0; void usage(void) { fprintf(stderr, "rtl_udp, an I/Q spectrum server for RTL2832 based SDR-receivers\n" "rtl_udp version %d.%d %s (%s)\n" "rtl-sdr library %d.%d %s\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__, rtlsdr_get_version() >>16, rtlsdr_get_version() & 0xFFFF, rtlsdr_get_ver_id() ); fprintf(stderr, "Usage:\trtl_udp [-a listen address]\n" "\t[-p listen port (default: 1234)]\n" "\t[-I infrared sensor listen port (default: 0=none)]\n" "\t[-W infrared sensor query wait interval usec (default: 10000)]\n" "\t[-f frequency to tune to [Hz]]\n" "\t[-g gain in dB (default: 0 for auto)]\n" "\t[-s samplerate in Hz (default: 2048000 Hz)]\n" "\t[-b number of buffers (default: 15, set by library)]\n" "\t[-l length of single buffer in units of 512 samples (default: 32 was 256)]\n" "\t[-n max number of linked list buffers to keep (default: 500)]\n" "\t[-w rtlsdr tuner bandwidth [Hz] (for R820T/2 and E4000 tuners)]\n" "\t[-d device index or serial (default: 0)]\n" "\t[-P ppm_error (default: 0)]\n" "%s" "\t[-T enable bias-T on GPIO PIN 0 (works for rtl-sdr.com v3 dongles)]\n" "\t[-D direct_sampling_mode (default: 0, 1 = I, 2 = Q, 3 = I below threshold, 4 = Q below threshold)]\n" "\t[-D direct_sampling_threshold_frequency (default: 0 use tuner specific frequency threshold for 3 and 4)]\n" "\t[-v increase verbosity (default: 0)]\n" , rtlsdr_get_opt_help(1) ); exit(1); } #ifdef _WIN32 int gettimeofday(struct timeval *tv, void* ignored) { FILETIME ft; unsigned __int64 tmp = 0; if (NULL != tv) { GetSystemTimeAsFileTime(&ft); tmp |= ft.dwHighDateTime; tmp <<= 32; tmp |= ft.dwLowDateTime; tmp /= 10; #ifdef _MSC_VER tmp -= 11644473600000000Ui64; #else tmp -= 11644473600000000ULL; #endif tv->tv_sec = (long)(tmp / 1000000UL); tv->tv_usec = (long)(tmp % 1000000UL); } return 0; } BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; rtlsdr_cancel_async(dev); return TRUE; } return FALSE; } #else static void sighandler(int signum) { fprintf(stderr, "Signal caught, exiting!\n"); rtlsdr_cancel_async(dev); do_exit = 1; } #endif void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx) { if(!do_exit) { struct llist *rpt = (struct llist*)malloc(sizeof(struct llist)); rpt->data = (char*)malloc(len); memcpy(rpt->data, buf, len); rpt->len = len; rpt->next = NULL; pthread_mutex_lock(&ll_mutex); if (ll_buffers == NULL) { ll_buffers = rpt; } else { struct llist *cur = ll_buffers; int num_queued = 0; while (cur->next != NULL) { cur = cur->next; num_queued++; } if(llbuf_num && llbuf_num == num_queued-2){ struct llist *curelem; free(ll_buffers->data); curelem = ll_buffers->next; free(ll_buffers); ll_buffers = curelem; } cur->next = rpt; if ( verbosity ) { if (num_queued > global_numq) printf("ll+, now %d\n", num_queued); else if (num_queued < global_numq) printf("ll-, now %d\n", num_queued); } global_numq = num_queued; } pthread_cond_signal(&cond); pthread_mutex_unlock(&ll_mutex); } } static void *udp_worker(void *arg) { struct llist *curelem,*prev; int bytesleft,bytessent, index; struct timeval tv= {1,0}; struct timespec ts; struct timeval tp; fd_set writefds; int r = 0; while(1) { if(do_exit) pthread_exit(0); pthread_mutex_lock(&ll_mutex); gettimeofday(&tp, NULL); ts.tv_sec = tp.tv_sec+1; ts.tv_nsec = tp.tv_usec * 1000; r = pthread_cond_timedwait(&cond, &ll_mutex, &ts); if(r == ETIMEDOUT) { pthread_mutex_unlock(&ll_mutex); printf("worker cond timeout\n"); sighandler(0); pthread_exit(NULL); } curelem = ll_buffers; ll_buffers = 0; pthread_mutex_unlock(&ll_mutex); while(curelem != 0) { bytesleft = curelem->len; index = 0; bytessent = 0; while(bytesleft > 0) { FD_ZERO(&writefds); FD_SET(s, &writefds); tv.tv_sec = 1; tv.tv_usec = 0; r = select(s+1, NULL, &writefds, NULL, &tv); if(r) { bytessent = sendto(s, &curelem->data[index], bytesleft, 0, (struct sockaddr *)&remote, sizeof(remote)); bytesleft -= bytessent; index += bytessent; } if(bytessent == SOCKET_ERROR || do_exit) { printf("worker socket bye\n"); sighandler(0); pthread_exit(NULL); } } prev = curelem; curelem = curelem->next; free(prev->data); free(prev); } } } static int set_gain_by_index(rtlsdr_dev_t *_dev, unsigned int index) { int res = 0; int* gains; int count = rtlsdr_get_tuner_gains(_dev, NULL); if (count > 0 && index < (unsigned int)count) { gains = malloc(sizeof(int) * count); count = rtlsdr_get_tuner_gains(_dev, gains); if (verbosity) printf("set tuner gain to %.1f dB\n", gains[index] / 10.0); res = rtlsdr_set_tuner_gain(_dev, gains[index]); if (res < 0) printf(" setting tuner gain index failed\n"); free(gains); } else { printf("set tuner gain index to %u\n", index); printf(" error setting tuner gain index failed: valid range: 0 .. %d\n", count-1); } return res; } static void check_tuner_pll(rtlsdr_dev_t *dev, int *tuner_unsupported, int *last_lock_report) { int r = rtlsdr_is_tuner_PLL_locked(dev); /* printf("performed lock check:\n"); */ if (r == 1) { if (*last_lock_report != r) printf("tuner PLL is unlocked!\n"); *last_lock_report = r; } else if (r == 0) { if (*last_lock_report != r) printf("tuner PLL is locked.\n"); *last_lock_report = r; } else if (r == -2) { printf("error at PLL-locked check: tuner not supported! No further tests.\n"); *tuner_unsupported = 1; } else if (r < 0) printf("error checking tuner PLL!\n"); else printf("unknown error at tuner PLL check!\n"); fflush(stdout); } #ifdef _WIN32 #define __attribute__(x) #pragma pack(push, 1) #endif struct command{ unsigned char cmd; unsigned int param; }__attribute__((packed)); #ifdef _WIN32 #pragma pack(pop) #endif static void *command_worker(void *arg) { int left, received = 0; fd_set readfds; struct command cmd={0, 0}; struct timeval tv= {1, 0}; unsigned tuner_check_timeout = 0; int last_lock_report = -1; int tuner_unsupported = 0; int r = 0; uint32_t freqhi = 0; uint64_t tmp64; uint32_t tmp; int32_t itmp; int32_t if_band_center_freq; int iitmp; while(1) { left=sizeof(cmd); while(left >0) { FD_ZERO(&readfds); FD_SET(s, &readfds); tv.tv_sec = 1; tv.tv_usec = 0; r = select(s+1, &readfds, NULL, NULL, &tv); if(r) { received = recv(s, (char*)&cmd+(sizeof(cmd)-left), left, 0); left -= received; /* printf("received %d bytes\n", received); */ } else if (!tuner_unsupported) { /* timeout: nothing happend */ ++tuner_check_timeout; if (tuner_check_timeout >= 3) { /* automatic check every 3 seconds */ check_tuner_pll(dev, &tuner_unsupported, &last_lock_report); tuner_check_timeout = 0; } fflush(stdout); } if(received == SOCKET_ERROR || do_exit) { printf("comm recv bye\n"); sighandler(0); pthread_exit(NULL); } } switch(cmd.cmd) { case SET_FREQUENCY: tmp = ntohl(cmd.param); if (!freqhi) { printf("set freq %f MHz\n", tmp * 1E-6); r = rtlsdr_set_center_freq(dev, tmp); if (r < 0) { printf(" error setting frequency!\n"); last_lock_report = -1; } } else { tmp64 = ( ((uint64_t)freqhi) << 32 ) | (uint64_t)tmp; printf("set freq64 %f MHz\n", tmp64 * 1E-6); r = rtlsdr_set_center_freq64(dev, tmp64); if (r < 0) { printf(" error setting frequency!\n"); last_lock_report = -1; } } freqhi = 0; break; case SET_FREQ_HI32: freqhi = ntohl(cmd.param); break; case SET_SAMPLE_RATE: tmp = ntohl(cmd.param); printf("set sample rate %u\n", tmp); r = rtlsdr_set_sample_rate(dev, tmp); if (r < 0) printf(" error setting sample rate! sample rate is %u\n", rtlsdr_get_sample_rate(dev)); break; case SET_GAIN_MODE: tmp = ntohl(cmd.param); printf("set gain mode %u (=%s)\n", tmp, tmp?"manual":"automatic"); r = rtlsdr_set_tuner_gain_mode(dev, tmp); if (r < 0) printf(" error setting gain mode!\n"); break; case SET_GAIN: tmp = ntohl(cmd.param); printf("set manual tuner gain %.1f dB\n", tmp/10.0); r = rtlsdr_set_tuner_gain(dev, tmp); if (r < 0) printf(" error setting tuner gain!\n"); break; case SET_FREQUENCY_CORRECTION: itmp = ntohl(cmd.param); printf("set freq correction %d ppm\n", itmp); r = rtlsdr_set_freq_correction(dev, itmp); if (r < 0) { printf(" error setting frequency correction!\n"); last_lock_report = -1; } break; case SET_IF_STAGE: tmp = ntohl(cmd.param); printf("set if stage %d gain %.1f dB\n", tmp >> 16, ((short)(tmp & 0xffff))/10.0); r = rtlsdr_set_tuner_if_gain(dev, tmp >> 16, (short)(tmp & 0xffff)); if (r < 0) printf(" error setting gain for stage!\n"); break; case SET_TEST_MODE: tmp = ntohl(cmd.param); printf("set test mode %d (=%s)\n", tmp, tmp?"active":"inactive"); r = rtlsdr_set_testmode(dev, tmp); if (r < 0) printf(" error setting test mode!\n"); break; case SET_AGC_MODE: tmp = ntohl(cmd.param); printf("set rtl2832's digital agc mode %d (=%s)\n", tmp, tmp?"enabled":"disabled"); r = rtlsdr_set_agc_mode(dev, tmp); if (r < 0) printf(" error setting digital agc mode!\n"); break; case SET_DIRECT_SAMPLING: tmp = ntohl(cmd.param); printf("set direct sampling %u (=%s)\n", tmp, (!tmp) ? "disabled": (tmp==1)?"pin I-ADC": (tmp==2)? "pin Q-ADC":"unknown!"); r = rtlsdr_set_direct_sampling(dev, tmp); if (r < 0) printf(" error setting direct sampling!\n"); break; case SET_OFFSET_TUNING: itmp = ntohl(cmd.param); printf("set offset tuning %d\n", itmp); r = rtlsdr_set_offset_tuning(dev, itmp); if (r < 0) { printf(" error setting offset tuning!\n"); last_lock_report = -1; } break; case SET_RTL_CRYSTAL: printf("set rtl xtal frequency %d\n", ntohl(cmd.param)); r = rtlsdr_set_xtal_freq(dev, ntohl(cmd.param), 0); if (r < 0) printf(" error setting rtl xtal frequency!\n"); break; case SET_TUNER_CRYSTAL: printf("set tuner xtal %d\n", ntohl(cmd.param)); r = rtlsdr_set_xtal_freq(dev, 0, ntohl(cmd.param)); if (r < 0) printf(" error setting tuner xtal frequency!\n"); break; case SET_TUNER_GAIN_BY_INDEX: tmp = ntohl(cmd.param); printf("set tuner gain by index %u\n", tmp); set_gain_by_index(dev, tmp); break; case SET_BIAS_TEE: tmp = ntohl(cmd.param); printf("set bias T %u (%s)\n", tmp, tmp?"on":"off"); r = rtlsdr_set_bias_tee(dev, tmp); if (r < 0) printf(" error setting bias tee!\n"); break; case SET_TUNER_BANDWIDTH: bandwidth = ntohl(cmd.param); printf("set tuner bandwidth to %i Hz\n", bandwidth); verbose_set_bandwidth(dev, bandwidth); break; case SET_I2C_TUNER_REGISTER: tmp = ntohl(cmd.param); printf("set i2c register x%03X to x%03X with mask x%02X\n", (tmp >> 20) & 0xfff, tmp & 0xfff, (tmp >> 12) & 0xff ); r = rtlsdr_set_tuner_i2c_register(dev, (tmp >> 20) & 0xfff, (tmp >> 12) & 0xff, tmp & 0xfff); if (r < 0) printf(" error setting i2c register!\n"); break; case SET_I2C_TUNER_OVERRIDE: tmp = ntohl(cmd.param); printf("set i2c override register x%03X to x%03X with mask x%02X\n", (tmp >> 20) & 0xfff, tmp & 0xfff, (tmp >> 12) & 0xff ); r = rtlsdr_set_tuner_i2c_override(dev, (tmp >> 20) & 0xfff, (tmp >> 12) & 0xff, tmp & 0xfff); if (r < 0) printf(" error setting i2c register!\n"); break; case UDP_TERMINATE: printf("comm recv bye\n"); sighandler(0); pthread_exit(NULL); break; case SET_TUNER_BW_IF_CENTER: if_band_center_freq = ntohl(cmd.param); printf("set tuner band to IF frequency %i Hz from center\n", if_band_center_freq); r = rtlsdr_set_tuner_band_center(dev, if_band_center_freq ); if (r < 0) printf(" error setting tuner band's IF center frequency!\n"); break; case SET_TUNER_IF_MODE: itmp = ntohl(cmd.param); printf("set tuner IF mode to %i: ", itmp); if (!itmp) printf("automatic gain of VGA controlled from RTL2832\n"); else if (-2500 <= itmp && itmp <= 2500) printf("VGA nearest to %.1f dB)\n", itmp/10.0); else if (10000 <=itmp && itmp <= 10015) printf("VGA gain idx %d\n", itmp - 10000); else if (10016 <= itmp && itmp <= 10031) printf("VGA gain idx %d - but with automatic gain of VGA controlled from RTL2832\n", itmp-10016); else printf("unknown!\n"); r = rtlsdr_set_tuner_if_mode(dev, itmp); if (r < 0) printf(" error setting tuner IF mode!\n"); break; case SET_SIDEBAND: tmp = ntohl(cmd.param); if(tmp) tmp = 1; printf("set tuner sideband %d: %s sideband\n", tmp, (tmp ? "upper" : "lower") ); r = rtlsdr_set_tuner_sideband(dev, tmp); if (r < 0) { printf(" error setting tuner sideband!\n"); last_lock_report = -1; } break; case REPORT_I2C_REGS: printf("unsupported command REPORT_I2C_REGS (0x%02x)\n", cmd.cmd); break; case GPIO_SET_OUTPUT_MODE: /* rtlsdr_set_gpio_output() */ itmp = ntohl(cmd.param); if ( 0 <= itmp && itmp < 8 ) { printf("set gpio pin %d to output\n", itmp); r = rtlsdr_set_gpio_output(dev, (uint8_t)itmp); if (r < 0) printf(" error setting gpio pin to output mode!\n"); } else printf("set gpio pin %d to output: error: pin has to be in 0 .. 7\n", itmp); break; case GPIO_SET_INPUT_MODE: /* rtlsdr_set_gpio_input() */ itmp = ntohl(cmd.param); if ( 0 <= itmp && itmp < 8 ) { printf("set gpio pin %d to input\n", itmp); r = rtlsdr_set_gpio_input(dev, (uint8_t)itmp); if (r < 0) printf(" error setting gpio pin to input mode!\n"); } else printf("set gpio pin %d to input: error: pin has to be in 0 .. 7\n", itmp); break; case GPIO_GET_IO_STATUS: /* rtlsdr_set_gpio_status() */ r = rtlsdr_set_gpio_status(dev, &iitmp ); if (r < 0) printf("error at requesting gpio io status!\n"); else printf("request for gpio io status: 0x%02x = %d%d%d%d %d%d%d%d for bits 7 .. 0\n", iitmp & 0xff, (iitmp >>7) & 1, (iitmp >>6) & 1, (iitmp >>5) & 1, (iitmp >>4) & 1, (iitmp >>3) & 1, (iitmp >>2) & 1, (iitmp >>1) & 1, iitmp & 1 ); break; case GPIO_WRITE_PIN: /* rtlsdr_set_gpio_bit() */ itmp = ntohl(cmd.param); if ( 0 <= ((itmp >> 16) & 0xffff) && ((itmp >> 16) & 0xffff) < 8 ) { printf("write %d to gpio %d\n", itmp & 0xffff, (itmp >> 16) & 0xffff); rtlsdr_set_gpio_output(dev, (uint8_t)((itmp >> 16) & 0xffff)); rtlsdr_set_gpio_bit(dev, (uint8_t)((itmp >> 16) & 0xffff), itmp & 0xffff); } else printf("write %d to gpio %d: error: pin has to be in 0 .. 7\n", itmp & 0xffff, (itmp >> 16) & 0xffff); break; case GPIO_READ_PIN: itmp = ntohl(cmd.param); if ( 0 <= itmp && itmp < 8 ) { r = rtlsdr_get_gpio_bit(dev, itmp, &iitmp); if (r < 0) printf(" error reading gpio pin!\n"); else printf("read gpio pin %d: %d\n", itmp, iitmp); } else printf("read gpio pin %d out of range: pin has to be in 0 .. 7\n", itmp); break; case GPIO_GET_BYTE: r = rtlsdr_get_gpio_byte(dev, &iitmp); if (r < 0) printf("error reading gpio byte!\n"); else printf("read gpio byte: 0x%02x = %d%d%d%d %d%d%d%d for bits 7 .. 0\n", iitmp & 0xff, (iitmp >>7) & 1, (iitmp >>6) & 1, (iitmp >>5) & 1, (iitmp >>4) & 1, (iitmp >>3) & 1, (iitmp >>2) & 1, (iitmp >>1) & 1, iitmp & 1 ); break; case IS_TUNER_PLL_LOCKED: itmp = -1; /* always print lock status */ check_tuner_pll(dev, &tuner_unsupported, &itmp); if (itmp != -1) last_lock_report = itmp; tuner_check_timeout = 0; break; default: printf("unknown command 0x%02x\n", cmd.cmd); break; } cmd.cmd = 0xff; } } struct ir_thread_data { rtlsdr_dev_t *dev; SOCKET port; int wait; char *addr; }; void *ir_thread_fn(void *arg) { int r = 1; struct linger ling = {1,0}; SOCKET listensocket; SOCKET irsocket; struct sockaddr_in local, remote; socklen_t rlen; uint8_t buf[128]; int ret = 0, len; struct ir_thread_data *data = (struct ir_thread_data *)arg; rtlsdr_dev_t *dev = data->dev; int port = data->port; int wait = data->wait; char *addr = data->addr; memset(&local,0,sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(port); local.sin_addr.s_addr = inet_addr(addr); listensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); r = 1; setsockopt(listensocket, SOL_SOCKET, SO_REUSEADDR, (char *)&r, sizeof(int)); setsockopt(listensocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); bind(listensocket,(struct sockaddr *)&local,sizeof(local)); while(1) { printf("listening on IR port %d...\n", port); listen(listensocket,1); irsocket = accept(listensocket,(struct sockaddr *)&remote, &rlen); setsockopt(irsocket, SOL_SOCKET, SO_LINGER, (char *)&ling, sizeof(ling)); printf("IR client accepted!\n"); while(1) { ret = rtlsdr_ir_query(dev, buf, sizeof(buf)); if (ret < 0) { printf("rtlsdr_ir_query error %d\n", ret); break; } len = ret; ret = send(irsocket, buf, len, 0); if (ret != len){ printf("incomplete write to ir client: %d != %d\n", ret,len); break; } usleep(wait); } closesocket(irsocket); } return 0; } int main(int argc, char **argv) { int r, opt, i; char* addr = "127.0.0.1"; int port = 1234; int port_ir = 0; int wait_ir = 10000; pthread_t thread_ir; uint32_t frequency = 100000000, samp_rate = 2048000; enum rtlsdr_ds_mode ds_mode = RTLSDR_DS_IQ; uint32_t ds_temp, ds_threshold = 0; struct sockaddr_in local; uint32_t buf_num = 0; /* buf_len: * must be multiple of 512 - else it will be overwritten * in rtlsdr_read_async() in librtlsdr.c with DEFAULT_BUF_LENGTH (= 16*32 *512 = 512 *512) * * -> 512*512 -> 1048 ms @ 250 kS or 81.92 ms @ 3.2 MS (internal default) * -> 32*512 -> 65 ms @ 250 kS or 5.12 ms @ 3.2 MS (new default) * * usual soundcard as reference: * 512 samples @ 48 kHz ~= 10.6 ms * 512 samples @ 8 kHz = 64 ms */ uint32_t buf_len = 32 * 512; const char * rtlOpts = NULL; int dev_index = 0; int dev_given = 0; int gain = 0; int ppm_error = 0; struct llist *curelem,*prev; pthread_attr_t attr; void *status; struct timeval tv = {1,0}; // struct linger ling = {1,0}; // SOCKET listensocket; socklen_t rlen; fd_set readfds; u_long blockmode = 1; dongle_info_t dongle_info; int gains[100]; const char * opt_str = NULL; #ifdef _WIN32 WSADATA wsd; i = WSAStartup(MAKEWORD(2,2), &wsd); #else struct sigaction sigact, sigign; #endif opt_str = "a:p:f:g:s:b:n:d:P:O:TI:W:l:w:D:v"; while ((opt = getopt(argc, argv, opt_str)) != -1) { switch (opt) { case 'd': dev_index = verbose_device_search(optarg); dev_given = 1; break; case 'f': frequency = (uint32_t)atofs(optarg); break; case 'g': gain = (int)(atof(optarg) * 10); /* tenths of a dB */ break; case 's': samp_rate = (uint32_t)atofs(optarg); break; case 'a': addr = optarg; break; case 'p': port = atoi(optarg); break; case 'I': port_ir = atoi(optarg); break; case 'W': wait_ir = atoi(optarg); break; case 'b': buf_num = atoi(optarg); break; case 'l': buf_len = 512 * atoi(optarg); break; case 'n': llbuf_num = atoi(optarg); break; case 'P': ppm_error = atoi(optarg); break; case 'O': rtlOpts = optarg; break; case 'T': enable_biastee = 1; break; case 'w': bandwidth = (uint32_t)atofs(optarg); break; case 'v': ++verbosity; break; case 'D': ds_temp = (uint32_t)( atofs(optarg) + 0.5 ); if (ds_temp <= RTLSDR_DS_Q_BELOW) ds_mode = (enum rtlsdr_ds_mode)ds_temp; else ds_threshold = ds_temp; break; default: usage(); break; } } if (argc < optind) usage(); if (verbosity) fprintf(stderr, "verbosity set to %d\n", verbosity); if (!dev_given) { dev_index = verbose_device_search("0"); } if (dev_index < 0) { exit(1); } rtlsdr_open(&dev, (uint32_t)dev_index); if (NULL == dev) { fprintf(stderr, "Failed to open rtlsdr device #%d.\n", dev_index); exit(1); } #ifndef _WIN32 sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigign.sa_handler = SIG_IGN; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigign, NULL); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif /* Set the tuner error */ verbose_ppm_set(dev, ppm_error); /* Set the sample rate */ r = rtlsdr_set_sample_rate(dev, samp_rate); if (r < 0) fprintf(stderr, "WARNING: Failed to set sample rate.\n"); if (rtlOpts) { rtlsdr_set_opt_string(dev, rtlOpts, verbosity); } /* Set direct sampling with threshold */ rtlsdr_set_ds_mode(dev, ds_mode, ds_threshold); /* Set the frequency */ r = rtlsdr_set_center_freq(dev, frequency); if (r < 0) fprintf(stderr, "WARNING: Failed to set center freq.\n"); else fprintf(stderr, "Tuned to %i Hz.\n", frequency); if (0 == gain) { /* Enable automatic gain */ r = rtlsdr_set_tuner_gain_mode(dev, 0); if (r < 0) fprintf(stderr, "WARNING: Failed to enable automatic gain.\n"); } else { /* Enable manual gain */ r = rtlsdr_set_tuner_gain_mode(dev, 1); if (r < 0) fprintf(stderr, "WARNING: Failed to enable manual gain.\n"); /* Set the tuner gain */ r = rtlsdr_set_tuner_gain(dev, gain); if (r < 0) fprintf(stderr, "WARNING: Failed to set tuner gain.\n"); else fprintf(stderr, "Tuner gain set to %f dB.\n", gain/10.0); } verbose_set_bandwidth(dev, bandwidth); rtlsdr_set_bias_tee(dev, enable_biastee); if (enable_biastee) fprintf(stderr, "activated bias-T on GPIO PIN 0\n"); /* Reset endpoint before we start reading from it (mandatory) */ r = rtlsdr_reset_buffer(dev); if (r < 0) fprintf(stderr, "WARNING: Failed to reset buffers.\n"); pthread_mutex_init(&exit_cond_lock, NULL); pthread_mutex_init(&ll_mutex, NULL); pthread_mutex_init(&exit_cond_lock, NULL); pthread_cond_init(&cond, NULL); pthread_cond_init(&exit_cond, NULL); if (port_ir) { struct ir_thread_data data = {.dev = dev, .port = port_ir, .wait = wait_ir, .addr = addr}; pthread_create(&thread_ir, NULL, &ir_thread_fn, (void *)(&data)); } memset(&local,0,sizeof(local)); local.sin_family = AF_INET; local.sin_port = htons(port); local.sin_addr.s_addr = inet_addr(addr); s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); r = 1; bind(s,(struct sockaddr *)&local,sizeof(local)); #ifdef _WIN32 ioctlsocket(s, FIONBIO, &blockmode); #else r = fcntl(s, F_GETFL, 0); r = fcntl(s, F_SETFL, r | O_NONBLOCK); #endif while(1) { printf("listening...\n"); printf("Use the device argument 'rtl_tcp=%s:%d' in OsmoSDR " "(gr-osmosdr) source\n" "to receive samples in GRC and control " "rtl_udp parameters (frequency, gain, ...).\n", addr, port); while(1) { FD_ZERO(&readfds); FD_SET(s, &readfds); tv.tv_sec = 1; tv.tv_usec = 0; r = select(s+1, &readfds, NULL, NULL, &tv); if(do_exit) { goto out; } else if(r) { char buff = 0; socklen_t blen = sizeof(buff); rlen = sizeof(remote); r = recvfrom(s, &buff, blen, 0, (struct sockaddr *)&remote, &rlen); if(buff == UDP_ESTABLISH) break; } } printf("client accepted!\n"); memset(&dongle_info, 0, sizeof(dongle_info)); memcpy(&dongle_info.magic, "RTL0", 4); r = rtlsdr_get_tuner_type(dev); if (r >= 0) dongle_info.tuner_type = htonl(r); r = rtlsdr_get_tuner_gains(dev, gains); if (r >= 0) dongle_info.tuner_gain_count = htonl(r); if (verbosity) { fprintf(stderr, "Supported gain values (%d): ", r); for (i = 0; i < r; i++) fprintf(stderr, "%.1f ", gains[i] / 10.0); fprintf(stderr, "\n"); } r = sendto(s, (const char *)&dongle_info, sizeof(dongle_info), 0, (struct sockaddr *)&remote, sizeof(remote)); if (sizeof(dongle_info) != r) printf("failed to send dongle information\n"); pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); r = pthread_create(&tcp_worker_thread, &attr, udp_worker, NULL); r = pthread_create(&command_thread, &attr, command_worker, NULL); pthread_attr_destroy(&attr); r = rtlsdr_read_async(dev, rtlsdr_callback, NULL, buf_num, buf_len); pthread_join(tcp_worker_thread, &status); pthread_join(command_thread, &status); printf("all threads dead..\n"); curelem = ll_buffers; ll_buffers = 0; while(curelem != 0) { prev = curelem; curelem = curelem->next; free(prev->data); free(prev); } do_exit = 0; global_numq = 0; } out: rtlsdr_close(dev); /* if (port_ir) pthread_join(thread_ir, &status); */ closesocket(s); #ifdef _WIN32 WSACleanup(); #endif printf("bye!\n"); return r >= 0 ? r : -r; } ================================================ FILE: src/rtl_wavestat.c ================================================ /* * rtl-wavestat, display wave file meta information * Copyright (C) 2019 by Hayati Ayguen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #else #include #include #include #include #include #include "getopt/getopt.h" #if defined(_MSC_VER) && (_MSC_VER < 1900) #define snprintf _snprintf #endif #endif #include #include "convenience/convenience.h" #include "convenience/waveread.h" static volatile int do_exit = 0; static int verbosity = 0; void usage(void) { fprintf(stderr, "rtl_wavestat, display wave file meta information\n" "rtl_wavestat version %d.%d %s (%s)\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__ ); fprintf(stderr, "Use:\trtl_wavestat [-options] \n" "\t-a print all information\n" "\t-r print without field name\n" "\t-f print center frequency in Hz\n" "\t-s print samplerate in Hz\n" "\t-c print number of channels\n" "\t-b print number of bits per sample\n" "\t-F print sample Format\n" "\t-u print start time in UTC: 'yyy-mm-ddThh:mm:dd.zzz'\n" "\t-t print start time in localtime: 'yyy-mm-ddThh:mm:dd.zzz'\n" "\t-z print start time in seconds since 1970-01-01T00:00:00.000\n" "\t-d print file duration in frames (= num samples per channel)\n" "\t-D print file duration in seconds\n" "\t-w input file\n" "\t-v verbose output\n" ); } #ifdef _WIN32 BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; return TRUE; } return FALSE; } #else static void sighandler(int signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; } #endif int main(int argc, char **argv) { #ifndef _WIN32 struct sigaction sigact; #endif int r, opt; const char * wavfilename = NULL; FILE * inpfile = stdin; uint32_t freq = 0; uint32_t srate = 0; int nChan = 0; int nBits = 0; int16_t formatTag; uint32_t numFrames; time_t tim = 0; double fraction = 0.0; struct tm *ptm = NULL; int printAll = 0; int printFieldName = 1; int printFreq = 0; int printSRate = 0; int printNchan = 0; int printNbits = 0; int printSmpFmt = 0; int printStartZ = 0; int printStartL = 0; int printStartUnix = 0; int printDurationSmp = 0; int printDurationTim = 0; while ((opt = getopt(argc, argv, "arfscbFutzdDvhw:")) != -1) { switch (opt) { case 'a': printAll = 1; break; case 'r': printFieldName = 0; break; case 'f': printFreq = 1; break; case 's': printSRate = 1; break; case 'c': printNchan = 1; break; case 'b': printNbits = 1; break; case 'F': printSmpFmt = 1; break; case 'u': printStartZ = 1; break; case 't': printStartL = 1; break; case 'z': printStartUnix = 1; break; case 'd': printDurationSmp = 1; break; case 'D': printDurationTim = 1; break; case 'w': wavfilename = optarg; break; case 'v': ++verbosity; break; case 'h': case '?': default: usage(); exit(1); break; } } if (verbosity) fprintf(stderr, "verbosity set to %d\n", verbosity); if (optind < argc) { wavfilename = argv[optind]; } #ifndef _WIN32 sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif if (!wavfilename || !strcmp(wavfilename, "-")) { #ifdef _WIN32 _setmode(_fileno(stdin), _O_BINARY); #endif inpfile = stdin; if (verbosity) fprintf(stderr, "Using stdin as input\n"); } else { if (verbosity >= 2) fprintf(stderr, "Opening input '%s'\n", wavfilename); inpfile = fopen(wavfilename, "rb"); if (!inpfile) { fprintf(stderr, "Error: Failed to open input %s\n", wavfilename); exit(1); } if (verbosity) fprintf(stderr, "Opened '%s' for input\n", wavfilename); } r = waveReadHeader(inpfile, &srate, &freq, &nBits, &nChan, &numFrames, &formatTag, verbosity); if ( r >= 10 ) { fprintf(stderr, "Error %d reading/evaluating wave file header\n", r); exit(1); } else if ( verbosity >= 2 ) { fprintf(stderr, "Success reading/evaluating wave file header\n"); } if ( !printFreq && !printSRate && !printNchan && !printNbits && !printSmpFmt && !printStartZ && !printStartL && !printDurationSmp && !printDurationTim ) { printAll = 1; } if ( printAll || printFreq ) { if ( printFieldName ) fprintf(stdout, "frequency/Hz:\t"); fprintf(stdout, "%lu\n", (unsigned long)freq); } if ( printAll || printSRate ) { if ( printFieldName ) fprintf(stdout, "samplerate/Hz:\t"); fprintf(stdout, "%lu\n", (unsigned long)srate); } if ( printAll || printNchan ) { if ( printFieldName ) fprintf(stdout, "num_channels:\t"); fprintf(stdout, "%d\n", nChan); } if ( printAll || printNbits ) { if ( printFieldName ) fprintf(stdout, "bits_per_sample:\t"); fprintf(stdout, "%d\n", nBits); } if ( printAll || printSmpFmt ) { if ( printFieldName ) fprintf(stdout, "sample_format:\t"); if (formatTag == 0x0001) fprintf(stdout, "0x%04X\tPCM\n", (unsigned)formatTag); else if (formatTag == 0x0003) fprintf(stdout, "0x%04X\tIEEE_FLOAT\n", (unsigned)formatTag); else fprintf(stdout, "0x%04X\n", (unsigned)formatTag); } if ( printAll || printStartZ ) { waveGetStartTime(&tim, &fraction); ptm = gmtime(&tim); if (ptm) { if ( printFieldName ) fprintf(stdout, "time_start/Z:\t"); fprintf(stdout, "%04d-%02d-%02dT%02d:%02d:%02d.%03d\n" , ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday , ptm->tm_hour, ptm->tm_min, ptm->tm_sec, (int)(fraction*1000.0) ); } } if ( printAll || printStartL ) { waveGetStartTime(&tim, &fraction); ptm = localtime(&tim); if (ptm) { if ( printFieldName ) fprintf(stdout, "time_start/L:\t"); fprintf(stdout, "%04d-%02d-%02dT%02d:%02d:%02d.%03d\n" , ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday , ptm->tm_hour, ptm->tm_min, ptm->tm_sec, (int)(fraction*1000.0) ); } } if ( printAll || printStartUnix ) { tim = 0; fraction = 0; waveGetStartTime(&tim, &fraction); if ( printFieldName ) fprintf(stdout, "time_start/secs (unix):\t"); fraction += (double)tim; fprintf(stdout, "%f\n", fraction ); } if ( printAll || printDurationSmp ) { if ( printFieldName ) fprintf(stdout, "duration/frames:\t"); fprintf(stdout, "%lu\n", (unsigned long)numFrames); } if ( printAll || printDurationTim ) { if ( printFieldName ) fprintf(stdout, "duration/secs:\t"); fprintf(stdout, "%f\n", (double)numFrames/(double)srate); } if (inpfile != stdin) fclose(inpfile); return 0; } /* vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab */ ================================================ FILE: src/rtl_wavestream.c ================================================ /* * rtl-wavestream, stream raw data (in specified sample format) * Copyright (C) 2019 by Hayati Ayguen * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #ifndef _WIN32 #include #include #include #else #include #include #include #include #include #include "getopt/getopt.h" #if defined(_MSC_VER) && (_MSC_VER < 1900) #define snprintf _snprintf #endif #endif #include #include "convenience/convenience.h" #include "convenience/waveread.h" static volatile int do_exit = 0; static int verbosity = 0; #define BLOCKLEN 65536 /* read up to 64K samples */ static uint8_t inpBuffer[BLOCKLEN * sizeof(int32_t)]; /* output is max 4 times bigger: uint8_t -> int32_t */ static uint8_t outBuffer[BLOCKLEN * sizeof(int32_t) * sizeof(int32_t)]; void usage(void) { fprintf(stderr, "rtl_wavestream, stream raw data (in specified format)\n" "rtl_wavestream version %d.%d %s (%s)\n\n", APP_VER_MAJOR, APP_VER_MINOR, APP_VER_ID, __DATE__ ); fprintf(stderr, "Usage:\trtl_wavestream [-options] \n" "\t-f sample format for output. default = input format\n" "\t supported formats: 'PCM16'/'PCM' or 'FLOAT32'/'FLOAT'\n" "\t-w input file\n" "\t-v verbose output\n" ); } #ifdef _WIN32 BOOL WINAPI sighandler(int signum) { if (CTRL_C_EVENT == signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; return TRUE; } return FALSE; } #else static void sighandler(int signum) { fprintf(stderr, "Signal caught, exiting!\n"); do_exit = 1; } #endif int main(int argc, char **argv) { #ifndef _WIN32 struct sigaction sigact; #endif int r, opt; const char * wavfilename = NULL; const char * targetFmtStr = "pcm16"; FILE * inpfile = NULL; uint32_t freq = 0; uint32_t srate = 0; int nChan = 0; int nBits = 0; int targetFmt = 0; /* PCM16 = 0, FLOAT32 = 1 */ int inputFmt = 0; /* PCM16 = 0, FLOAT32 = 1 */ int16_t formatTag; uint32_t numFrames; while ((opt = getopt(argc, argv, "f:w:vh")) != -1) { switch (opt) { case 'f': targetFmtStr = optarg; break; case 'w': wavfilename = optarg; break; case 'v': ++verbosity; break; case 'h': case '?': default: usage(); exit(1); break; } } if (verbosity) fprintf(stderr, "verbosity set to %d\n", verbosity); if (optind < argc) { wavfilename = argv[optind]; } if ( !strcmp(targetFmtStr, "pcm") || !strcmp(targetFmtStr, "pcm16") || !strcmp(targetFmtStr, "PCM") || !strcmp(targetFmtStr, "PCM16") ) { targetFmt = 0; /* PCM16 = 0, FLOAT32 = 1 */ if (verbosity) fprintf(stderr, "target sample format: PCM16\n"); } else if ( !strcmp(targetFmtStr, "flt32") || !strcmp(targetFmtStr, "float32") || !strcmp(targetFmtStr, "float") || !strcmp(targetFmtStr, "FLT32") || !strcmp(targetFmtStr, "FLOAT32") || !strcmp(targetFmtStr, "FLOAT") ) { targetFmt = 1; /* PCM16 = 0, FLOAT32 = 1 */ if (verbosity) fprintf(stderr, "target sample format: FLOAT32\n"); } else { fprintf(stderr, "Error: unsupported target format. accepting 'PCM16'/'PCM' or 'FLOAT32'/'FLOAT'\n"); exit(1); } #ifndef _WIN32 sigact.sa_handler = sighandler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; sigaction(SIGINT, &sigact, NULL); sigaction(SIGTERM, &sigact, NULL); sigaction(SIGQUIT, &sigact, NULL); sigaction(SIGPIPE, &sigact, NULL); #else SetConsoleCtrlHandler( (PHANDLER_ROUTINE) sighandler, TRUE ); #endif #ifdef _WIN32 _setmode(_fileno(stdout), _O_BINARY); #endif if (!wavfilename) { fprintf(stderr, "Error: No input file specified!\n"); exit(1); } else { if (verbosity >= 2) fprintf(stderr, "Opening input '%s'\n", wavfilename); inpfile = fopen(wavfilename, "rb"); if (!inpfile) { fprintf(stderr, "Error: Failed to open input %s\n", wavfilename); exit(1); } if (verbosity) fprintf(stderr, "Opened '%s' for input\n", wavfilename); } r = waveReadHeader(inpfile, &srate, &freq, &nBits, &nChan, &numFrames, &formatTag, verbosity); if ( r >= 10 ) { fprintf(stderr, "Error %d reading/evaluating wave file header\n", r); } else if ( verbosity >= 2 ) { fprintf(stderr, "Success reading/evaluating wave file header\n"); } if ( verbosity ) { fprintf(stderr, "frequency/Hz:\t%lu\n", (unsigned long)freq); fprintf(stderr, "samplerate/Hz:\t%lu\n", (unsigned long)srate); fprintf(stderr, "num_channels:\t%d\n", nChan); fprintf(stderr, "bits_per_sample:\t%d\n", nBits); if (formatTag == 0x0001) fprintf(stderr, "sample_format:\t0x%04X\tPCM\n", (unsigned)formatTag); else if (formatTag == 0x0003) fprintf(stderr, "sample_format:\t0x%04X\tIEEE_FLOAT\n", (unsigned)formatTag); else fprintf(stderr, "sample_format:\t0x%04X\n", (unsigned)formatTag); fprintf(stderr, "duration/frames:\t%lu\t0x%lX\n", (unsigned long)numFrames, (unsigned long)numFrames); fprintf(stderr, "duration/secs:\t%f\n", (double)numFrames/(double)srate); } if ( formatTag == 0x0001 && nBits == 16 ) { inputFmt = 0; if (verbosity) fprintf(stderr, "input sample format: PCM16\n"); } else if ( formatTag == 0x0003 && nBits == 32 ) { inputFmt = 1; if (verbosity) fprintf(stderr, "input sample format: FLOAT32\n"); } else { fprintf(stderr, "Error: unsupported input format. only 'PCM16' and 'FLOAT32' supported.\n"); exit(1); } { void * pvInp = &inpBuffer[0]; void * pvOut = &outBuffer[0]; const size_t numFramesPerRead = BLOCKLEN / nChan; const size_t numSmpPerRead = numFramesPerRead * nChan; const size_t inpSmpSize = (inputFmt == 0) ? sizeof(int16_t) : sizeof(float); const size_t outSmpSize = (targetFmt == 0) ? sizeof(int16_t) : sizeof(float); size_t numSamples = numFrames * nChan; size_t readTotal = 0; size_t numRead; if ( verbosity ) { fprintf(stderr, "input sample size = %u\n", (unsigned)inpSmpSize); fprintf(stderr, "output sample size = %u\n", (unsigned)outSmpSize); fprintf(stderr, "samples per read = %u smp\n", (unsigned)numSmpPerRead); } while ( !do_exit ) { const size_t numToRead = numSmpPerRead; const size_t readErr = waveReadSamples(inpfile, pvInp, numToRead, 0, &numRead); if ( numRead != numToRead ) fprintf(stderr, "Error: reading %lu delivered %lu smp after %lu smp - left %lu frames\n" , (unsigned long)numToRead, (unsigned long)numRead , (unsigned long)readTotal, (unsigned long)(numSamples / nChan) ); if ( readErr ) { fprintf(stderr, "Error reading samples after %lu smp - left %lu frames\n" , (unsigned long)readTotal, (unsigned long)(numSamples / nChan) ); } else if ( verbosity >= 2 ) fprintf(stderr, "read %lu samples: left frames: %lu\n" , (unsigned long)numToRead, (unsigned long)numSamples); if ( !numRead ) break; if ( inputFmt == targetFmt ) { size_t w = fwrite(pvInp, inpSmpSize, numRead, stdout); if ( w != numRead ) { fprintf(stderr, "Error writing read samples after %lu smp - left %lu frames\n" , (unsigned long)readTotal, (unsigned long)(numSamples / nChan) ); break; } } else if ( inputFmt == 0 && targetFmt == 1 ) { const int16_t *ai = (const int16_t*)pvInp; float * ao = (float*)pvOut; size_t w, k; for ( k = 0; k < numRead; ++k ) ao[k] = ai[k] * (1.0F / 32768.0F); w = fwrite(pvOut, outSmpSize, numRead, stdout); if ( w != numRead ) { fprintf(stderr, "Error writing converted samples after %lu smp - left %lu frames\n" , (unsigned long)readTotal, (unsigned long)(numSamples / nChan) ); break; } } else if ( inputFmt == 1 && targetFmt == 0 ) { const float *ai = (const float*)pvInp; int16_t * ao = (int16_t*)pvOut; size_t w, k; for ( k = 0; k < numRead; ++k ) ao[k] = (int16_t)( ai[k] * 32768.0F ); w = fwrite(pvOut, outSmpSize, numRead, stdout); if ( w != numRead ) { fprintf(stderr, "Error writing converted samples after %lu smp - left %lu frames\n" , (unsigned long)readTotal, (unsigned long)(numSamples / nChan) ); break; } } numSamples -= numRead; readTotal += numRead; } if ( verbosity ) { fprintf(stderr, "Written %lu samples in total - left %lu of %lu frames\n" , (unsigned long)readTotal, (unsigned long)(numSamples / nChan), (unsigned long)(numFrames * nChan) ); } } fclose(inpfile); return 0; } /* vim: tabstop=8:softtabstop=8:shiftwidth=8:noexpandtab */ ================================================ FILE: src/rtlsdr.rc.in ================================================ #include VS_VERSION_INFO VERSIONINFO FILEVERSION 0,0,0,0 PRODUCTVERSION 0,0,0,0 FILEFLAGSMASK 0x3fL #ifndef NDEBUG FILEFLAGS 0x0L #else FILEFLAGS 0x1L #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_DLL FILESUBTYPE VFT2_DRV_INSTALLABLE BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "osmocom rtl-sdr" VALUE "FileVersion", "@VERSION@" VALUE "InternalName", "rtl-sdr.dll" VALUE "LegalCopyright", "Licensed under GPLv2" VALUE "OriginalFilename", "rtl-sdr.dll" VALUE "ProductName", "github.com/librtlsdr/librtlsdr" VALUE "ProductVersion", "@VERSION@" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END ================================================ FILE: src/rtlsdr_rpc.c ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtlsdr_rpc_msg.h" #if 1 #include #define PRINTF(__s, ...) fprintf(stderr, __s, ##__VA_ARGS__) #define TRACE() PRINTF("[t] %s,%u\n", __FILE__, __LINE__) #define ERROR() PRINTF("[e] %s,%u\n", __FILE__, __LINE__) #define UNIMPL() PRINTF("[u] %s,%u\n", __FILE__, __LINE__) #else #define PRINTF(...) #define TRACE() #define ERROR() #define UNIMPL() #endif typedef struct { volatile unsigned int is_locked; volatile unsigned int is_init; int sock; #define QR_COUNT 32 pthread_mutex_t qr_lock; volatile uint32_t qr_mask; volatile uint32_t qr_box; rtlsdr_rpc_msg_t query[QR_COUNT]; rtlsdr_rpc_msg_t reply[QR_COUNT]; pthread_mutex_t send_lock; pthread_mutex_t recv_lock; volatile unsigned int is_async_cancel; } rtlsdr_rpc_cli_t; static rtlsdr_rpc_cli_t rtlsdr_rpc_cli = { 0, }; typedef void (*rtlsdr_rpc_read_async_cb_t) (unsigned char*, uint32_t, void*); typedef struct rtlsdr_rpc_dev { uint32_t index; size_t gain_count; rtlsdr_rpc_cli_t* cli; } rtlsdr_rpc_dev_t; static int resolve_ip_addr ( struct sockaddr_storage saddr_both[2], size_t size_both[2], const char* addr, const char* port ) { struct addrinfo ai; struct addrinfo* aip = NULL; int err = -1; size_t i; memset(&ai, 0, sizeof(ai)); ai.ai_family = AF_UNSPEC; ai.ai_socktype = SOCK_STREAM; ai.ai_flags = AI_PASSIVE; if (getaddrinfo(addr, port, &ai, &aip)) goto on_error; size_both[0] = 0; size_both[1] = 0; i = 0; for (; (i != 2) && (aip != NULL); aip = aip->ai_next) { if ((aip->ai_family != AF_INET) && (aip->ai_family != AF_INET6)) continue ; if (aip->ai_addrlen == 0) continue ; memcpy(&saddr_both[i], aip->ai_addr, aip->ai_addrlen); size_both[i] = aip->ai_addrlen; ++i; } if (i == 0) goto on_error; err = 0; on_error: if (aip != NULL) freeaddrinfo(aip); return err; } static int open_socket ( struct sockaddr_storage saddr_both[2], size_t size_both[2], int type, int proto, struct sockaddr_storage** saddr_used, size_t* size_used ) { size_t i; int fd; for (i = 0; (i != 2) && (size_both[i]); ++i) { const struct sockaddr* const sa = (const struct sockaddr*)&saddr_both[i]; fd = socket(sa->sa_family, type, proto); if (fd != -1) break ; } if ((i == 2) || (size_both[i] == 0)) return -1; *saddr_used = &saddr_both[i]; *size_used = size_both[i]; return fd; } static int init_cli(rtlsdr_rpc_cli_t* cli) { struct sockaddr_storage saddrs[2]; struct sockaddr_storage* saddr; size_t sizes[2]; size_t size; const char* addr; const char* port; size_t i; size_t j; int err = -1; /* no better way in this case ... */ while (cli->is_locked) usleep(10000); cli->is_locked = 1; if (cli->is_init) goto on_success; addr = getenv("RTLSDR_RPC_SERV_ADDR"); if (addr == NULL) addr = "127.0.0.1"; port = getenv("RTLSDR_RPC_SERV_PORT"); if (port == NULL) port = "40000"; if (resolve_ip_addr(saddrs, sizes, addr, port)) { ERROR(); goto on_error_0; } cli->sock = open_socket (saddrs, sizes, SOCK_STREAM, IPPROTO_TCP, &saddr, &size); if (cli->sock == -1) { ERROR(); goto on_error_0; } if (connect(cli->sock, (const struct sockaddr*)saddr, (socklen_t)size)) { ERROR(); goto on_error_1; } if (fcntl(cli->sock, F_SETFL, O_NONBLOCK)) { ERROR(); goto on_error_1; } for (i = 0; i != QR_COUNT; ++i) { rtlsdr_rpc_msg_t* const q = &cli->query[i]; rtlsdr_rpc_msg_t* const r = &cli->reply[i]; if (rtlsdr_rpc_msg_init(q, 0)) goto on_error_2; if (rtlsdr_rpc_msg_init(r, 0)) { rtlsdr_rpc_msg_fini(q); goto on_error_2; } } pthread_mutex_init(&cli->qr_lock, NULL); cli->qr_mask = 0; cli->qr_box = 0; pthread_mutex_init(&cli->send_lock, NULL); pthread_mutex_init(&cli->recv_lock, NULL); cli->is_init = 1; on_success: err = 0; goto on_error_0; on_error_2: for (j = 0; j != i; ++j) { rtlsdr_rpc_msg_fini(&cli->query[j]); rtlsdr_rpc_msg_fini(&cli->reply[j]); } on_error_1: shutdown(cli->sock, SHUT_RDWR); close(cli->sock); on_error_0: cli->is_locked = 0; return err; } __attribute__((unused)) static int fini_cli(rtlsdr_rpc_cli_t* cli) { size_t i; for (i = 0; i != QR_COUNT; ++i) { rtlsdr_rpc_msg_fini(&cli->query[i]); rtlsdr_rpc_msg_fini(&cli->reply[i]); } pthread_mutex_destroy(&cli->qr_lock); pthread_mutex_destroy(&cli->send_lock); pthread_mutex_destroy(&cli->recv_lock); shutdown(cli->sock, SHUT_RDWR); close(cli->sock); return 0; } static int alloc_qr (rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t** q, rtlsdr_rpc_msg_t** r) { size_t i; pthread_mutex_lock(&cli->qr_lock); for (i = 0; i != QR_COUNT; ++i) { const uint32_t m = 1 << i; if ((cli->qr_mask & m) == 0) { cli->qr_mask |= m; break ; } } pthread_mutex_unlock(&cli->qr_lock); if (i == QR_COUNT) return -1; *q = &cli->query[i]; *r = &cli->reply[i]; /* set the query id */ rtlsdr_rpc_msg_reset(*q); rtlsdr_rpc_msg_set_id(*q, (uint8_t)i); return 0; } static void free_qr (rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* q, rtlsdr_rpc_msg_t* r) { const uint32_t m = 1 << (uint32_t)rtlsdr_rpc_msg_get_id(q); pthread_mutex_lock(&cli->qr_lock); cli->qr_mask &= ~m; pthread_mutex_unlock(&cli->qr_lock); } static int recv_all(int fd, uint8_t* buf, size_t size) { ssize_t n; fd_set rset; while (1) { errno = 0; n = recv(fd, buf, size, 0); if (n <= 0) { if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) return -1; } else { size -= (size_t)n; buf += (size_t)n; if (size == 0) break ; } FD_ZERO(&rset); FD_SET(fd, &rset); if (select(fd + 1, &rset, NULL, NULL, NULL) <= 0) { return -1; } } return 0; } static int send_all(int fd, const uint8_t* buf, size_t size) { ssize_t n; fd_set wset; while (1) { errno = 0; n = send(fd, buf, size, 0); if (n <= 0) { if ((errno != EWOULDBLOCK) || (errno != EAGAIN)) return -1; } else { size -= (size_t)n; buf += (size_t)n; if (size == 0) break ; } FD_ZERO(&wset); FD_SET(fd, &wset); if (select(fd + 1, NULL, &wset, NULL, NULL) <= 0) { return -1; } } return 0; } static int recv_msg (rtlsdr_rpc_cli_t* cli, uint8_t id, rtlsdr_rpc_msg_t* m) { static const size_t fmt_size = offsetof(rtlsdr_rpc_fmt_t, data); const uint32_t mask = 1 << (uint32_t)id; const int fd = cli->sock; uint32_t size; uint8_t to_id; int err = -1; rtlsdr_rpc_msg_t* to_m; pthread_mutex_lock(&cli->recv_lock); if (cli->qr_box & mask) { cli->qr_box &= ~mask; goto on_success; } while (1) { /* receive next message */ if (recv_all(fd, m->fmt, fmt_size)) goto on_error; /* get destination message by id */ to_id = rtlsdr_rpc_msg_get_id(m); if (to_id >= QR_COUNT) goto on_error; to_m = &cli->reply[to_id]; if (to_id != id) memcpy(to_m->fmt, m->fmt, fmt_size); size = rtlsdr_rpc_msg_get_size(to_m); if (size < fmt_size) goto on_error; if (rtlsdr_rpc_msg_realloc(to_m, size)) goto on_error; size -= fmt_size; if (size) { if (recv_all(fd, to_m->fmt + fmt_size, size)) goto on_error; } if (to_id == id) goto on_success; /* message not for this query, forward */ cli->qr_box |= 1 << (uint32_t)to_id; } on_success: err = 0; on_error: pthread_mutex_unlock(&cli->recv_lock); return err; } static int send_msg(rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* m) { int err; rtlsdr_rpc_msg_set_size(m, (uint32_t)m->off); pthread_mutex_lock(&cli->send_lock); err = send_all(cli->sock, m->fmt, m->off); pthread_mutex_unlock(&cli->send_lock); return err; } static int send_recv_msg (rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* q, rtlsdr_rpc_msg_t* r) { const uint8_t id = rtlsdr_rpc_msg_get_id(q); if (send_msg(cli, q)) return -1; if (recv_msg(cli, id, r)) return -1; rtlsdr_rpc_msg_reset(r); return 0; } static int send_flush_msgs (rtlsdr_rpc_cli_t* cli, rtlsdr_rpc_msg_t* q) { struct timeval tm; ssize_t n; fd_set rset; uint8_t buf[256]; if (send_msg(cli, q)) return -1; pthread_mutex_lock(&cli->recv_lock); while (1) { FD_ZERO(&rset); FD_SET(cli->sock, &rset); tm.tv_sec = 0; tm.tv_usec = 200000; if (select(cli->sock + 1, &rset, NULL, NULL, &tm) < 0) break ; if (recv(cli->sock, buf, sizeof(buf), 0) <= 0) break ; } pthread_mutex_unlock(&cli->recv_lock); return 0; } uint32_t rtlsdr_rpc_get_device_count(void) { rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; uint32_t n = 0; if (init_cli(cli)) goto on_error_0; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DEVICE_COUNT); if (send_recv_msg(cli, q, r)) goto on_error_1; if (rtlsdr_rpc_msg_pop_uint32(r, &n)) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return n; } const char* rtlsdr_rpc_get_device_name ( uint32_t index ) { rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; const char* s = NULL; if (init_cli(cli)) goto on_error_0; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DEVICE_NAME); if (rtlsdr_rpc_msg_push_uint32(q, index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; /* TODO: memory leak here */ s = strdup(s); on_error_1: free_qr(cli, q, r); on_error_0: return s; } int rtlsdr_rpc_get_device_usb_strings (uint32_t index, char* manufact, char* product, char* serial) { rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; const char* s; int err = -1; if (init_cli(cli)) goto on_error_0; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DEVICE_USB_STRINGS); if (rtlsdr_rpc_msg_push_uint32(q, index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; strcpy(manufact, s); if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; strcpy(product, s); if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; strcpy(serial, s); on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_get_index_by_serial(const char* serial) { rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (init_cli(cli)) goto on_error_0; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_INDEX_BY_SERIAL); if (rtlsdr_rpc_msg_push_str(q, serial)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_open(void** devp, uint32_t index) { rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; rtlsdr_rpc_dev_t* dev; int err = -1; index = (uint32_t) atoi(getenv("RTLSDR_RPC_DEVICE_INDEX")); *devp = NULL; if (init_cli(cli)) goto on_error_0; dev = malloc(sizeof(rtlsdr_rpc_dev_t)); if (dev == NULL) goto on_error_0; if (alloc_qr(cli, &q, &r)) goto on_error_1; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_OPEN); if (rtlsdr_rpc_msg_push_uint32(q, index)) goto on_error_2; if (send_recv_msg(cli, q, r)) goto on_error_2; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_2; dev->index = index; dev->gain_count = 32; dev->cli = cli; *devp = dev; on_error_2: free_qr(cli, q, r); on_error_1: if (err) free(dev); on_error_0: return err; } int rtlsdr_rpc_close(void* devp) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_CLOSE); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: free(dev); return err; } int rtlsdr_rpc_set_xtal_freq (void* devp, uint32_t rtl_freq, uint32_t tuner_freq) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_XTAL_FREQ); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, rtl_freq)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, tuner_freq)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_get_xtal_freq (void* devp, uint32_t* rtl_freq, uint32_t* tuner_freq) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_XTAL_FREQ); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; if (rtlsdr_rpc_msg_pop_uint32(r, rtl_freq)) { err = -1; goto on_error_1; } if (rtlsdr_rpc_msg_pop_uint32(r, tuner_freq)) { err = -1; goto on_error_1; } on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_get_usb_strings (void* devp, char* manufact, char* product, char* serial) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = &rtlsdr_rpc_cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; const char* s; int err = -1; if (init_cli(cli)) goto on_error_0; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_USB_STRINGS); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; strcpy(manufact, s); if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; strcpy(product, s); if (rtlsdr_rpc_msg_pop_str(r, &s)) goto on_error_1; strcpy(serial, s); on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_write_eeprom (void* dev, uint8_t* data, uint8_t offset, uint16_t len) { UNIMPL(); return -1; } int rtlsdr_rpc_read_eeprom (void* dev, uint8_t* data, uint8_t offset, uint16_t len) { UNIMPL(); return -1; } int rtlsdr_rpc_set_center_freq(void* devp, uint32_t freq) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_CENTER_FREQ); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, freq)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } uint32_t rtlsdr_rpc_get_center_freq(void* devp) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; uint32_t freq = 0; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_CENTER_FREQ); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; if (rtlsdr_rpc_msg_get_err(r)) goto on_error_1; if (rtlsdr_rpc_msg_pop_uint32(r, &freq)) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return freq; } int rtlsdr_rpc_set_freq_correction(void* devp, int ppm) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_FREQ_CORRECTION); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)ppm)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_get_freq_correction(void* devp) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_FREQ_CORRECTION); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_get_tuner_type(void* devp) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_TUNER_TYPE); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_get_tuner_gains(void* devp, int* gainsp) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; const uint32_t is_null = (gainsp == NULL); const uint32_t gain_size = (uint32_t)dev->gain_count * sizeof(int); const uint8_t* tmp; size_t size; int err = 0; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_TUNER_GAINS); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, is_null)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, gain_size)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err <= 0) goto on_error_1; dev->gain_count = (size_t)err; if (is_null == 0) { if (rtlsdr_rpc_msg_pop_buf(r, &tmp, &size)) { err = 0; goto on_error_1; } /* TODO: endianess */ memcpy(gainsp, tmp, size); } on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_set_tuner_gain(void* devp, int gain) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TUNER_GAIN); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)gain)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_set_and_get_tuner_bandwidth(void* devp, uint32_t bw, uint32_t *applied_bw, int apply_bw ) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_GET_TUNER_BW); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)bw)) goto on_error_1; if (rtlsdr_rpc_msg_push_int32(q, (int32_t)apply_bw)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; if (rtlsdr_rpc_msg_get_err(r)) goto on_error_1; if (rtlsdr_rpc_msg_pop_uint32(r, applied_bw)) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_get_tuner_gain(void* devp) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_TUNER_GAIN); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_set_tuner_if_gain(void* devp, int stage, int gain) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TUNER_IF_GAIN); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)stage)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)gain)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_set_tuner_gain_mode(void* devp, int manual) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TUNER_GAIN_MODE); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, manual)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_set_sample_rate(void* devp, uint32_t rate) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_SAMPLE_RATE); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, rate)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } uint32_t rtlsdr_rpc_get_sample_rate(void* devp) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; uint32_t rate = 0; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_SAMPLE_RATE); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; if (rtlsdr_rpc_msg_get_err(r)) goto on_error_1; if (rtlsdr_rpc_msg_pop_uint32(r, &rate)) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return rate; } int rtlsdr_rpc_set_testmode(void* devp, int on) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_TESTMODE); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_set_agc_mode(void* devp, int on) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_AGC_MODE); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_set_direct_sampling(void* devp, int on) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_DIRECT_SAMPLING); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_get_direct_sampling(void* devp) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_DIRECT_SAMPLING); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_set_offset_tuning(void* devp, int on) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_SET_OFFSET_TUNING); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)on)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_get_offset_tuning(void* devp) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_GET_OFFSET_TUNING); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_reset_buffer(void* devp) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_RESET_BUFFER); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_read_sync (void* devp, void* buf, int len, int* n_read) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; const uint8_t* tmp; size_t size; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_READ_SYNC); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, (uint32_t)len)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; if (rtlsdr_rpc_msg_pop_buf(r, &tmp, &size)) { err = -1; goto on_error_1; } if (size > (size_t)len) size = len; memcpy(buf, tmp, size); *n_read = (int)size; on_error_1: free_qr(cli, q, r); on_error_0: return err; } static volatile unsigned int is_cancel; int rtlsdr_rpc_read_async ( void* devp, rtlsdr_rpc_read_async_cb_t cb, void* ctx, uint32_t buf_num, uint32_t buf_len ) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; rtlsdr_rpc_msg_t* q; rtlsdr_rpc_msg_t* r; uint8_t id; size_t size; int err = -1; if (alloc_qr(cli, &q, &r)) goto on_error_0; id = rtlsdr_rpc_msg_get_id(q); rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_READ_ASYNC); if (rtlsdr_rpc_msg_push_uint32(q, dev->index)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, buf_num)) goto on_error_1; if (rtlsdr_rpc_msg_push_uint32(q, buf_len)) goto on_error_1; if (send_recv_msg(cli, q, r)) goto on_error_1; err = rtlsdr_rpc_msg_get_err(r); if (err) goto on_error_1; cli->is_async_cancel = 0; while (cli->is_async_cancel == 0) { static const size_t off = offsetof(rtlsdr_rpc_fmt_t, data); if (recv_msg(cli, id, r)) { err = -1; goto on_error_1; } size = rtlsdr_rpc_msg_get_size(r); cb(r->fmt + off, size - off, ctx); } rtlsdr_rpc_msg_reset(q); rtlsdr_rpc_msg_set_id(q, id); rtlsdr_rpc_msg_set_op(q, RTLSDR_RPC_OP_CANCEL_ASYNC); rtlsdr_rpc_msg_push_uint32(q, dev->index); send_flush_msgs(cli, q); on_error_1: free_qr(cli, q, r); on_error_0: return err; } int rtlsdr_rpc_wait_async ( void* dev, rtlsdr_rpc_read_async_cb_t cb, void* ctx ) { return rtlsdr_rpc_read_async(dev, cb, ctx, 0, 0); } int rtlsdr_rpc_cancel_async(void* devp) { rtlsdr_rpc_dev_t* const dev = devp; rtlsdr_rpc_cli_t* const cli = dev->cli; cli->is_async_cancel = 1; return 0; } unsigned int rtlsdr_rpc_is_enabled(void) { static unsigned int is_enabled = (unsigned int)-1; if (is_enabled == (unsigned int)-1) is_enabled = (getenv("RTLSDR_RPC_IS_ENABLED") != NULL); return is_enabled; } ================================================ FILE: src/rtlsdr_rpc_msg.c ================================================ #include #include #include #include #include "rtlsdr_rpc_msg.h" #if 1 #include #define PRINTF(__s, ...) fprintf(stderr, __s, ##__VA_ARGS__) #define TRACE() PRINTF("[t] %s,%u\n", __FILE__, __LINE__) #define ERROR() PRINTF("[e] %s,%u\n", __FILE__, __LINE__) #else #define TRACE() #define ERROR() #define PRINTF(...) #endif int rtlsdr_rpc_msg_init(rtlsdr_rpc_msg_t* msg, size_t data_size) { size_t fmt_size; if (data_size == 0) data_size = 64; fmt_size = offsetof(rtlsdr_rpc_fmt_t, data) + data_size; msg->fmt = malloc(fmt_size); if (msg->fmt == NULL) return -1; msg->off = offsetof(rtlsdr_rpc_fmt_t, data); msg->size = fmt_size; return 0; } int rtlsdr_rpc_msg_fini(rtlsdr_rpc_msg_t* msg) { free(msg->fmt); return 0; } void rtlsdr_rpc_msg_reset(rtlsdr_rpc_msg_t* msg) { msg->off = offsetof(rtlsdr_rpc_fmt_t, data); } int rtlsdr_rpc_msg_realloc(rtlsdr_rpc_msg_t* msg, size_t size) { uint8_t* new_fmt; if (msg->size >= size) return 0; new_fmt = malloc(size); if (new_fmt == NULL) return -1; memcpy(new_fmt, msg->fmt, msg->off); free(msg->fmt); msg->fmt = new_fmt; msg->size = size; return 0; } static int check_size(const rtlsdr_rpc_msg_t* msg, size_t size) { if ((msg->off + size) > msg->size) return -1; return 0; } static int check_size_or_realloc(rtlsdr_rpc_msg_t* msg, size_t size) { uint8_t* new_fmt; size_t new_size; if (check_size(msg, size) == 0) return 0; new_size = (msg->off + size + 256) & ~(256 - 1); new_fmt = malloc(new_size); if (new_fmt == NULL) return -1; memcpy(new_fmt, msg->fmt, msg->off); free(msg->fmt); msg->fmt = new_fmt; msg->size = new_size; return 0; } static int pop_uint32(rtlsdr_rpc_msg_t* msg, uint32_t* x) { #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #error "unsupported endianness" #endif if (check_size(msg, sizeof(uint32_t))) return -1; *x = *(const uint32_t*)(msg->fmt + msg->off); msg->off += sizeof(uint32_t); return 0; } static void push_mem_safe(rtlsdr_rpc_msg_t* msg, const uint8_t* x, size_t n) { memcpy(msg->fmt + msg->off, x, n); msg->off += n; } static void push_uint32_safe(rtlsdr_rpc_msg_t* msg, uint32_t x) { #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #error "unsupported endianness" #endif push_mem_safe(msg, (const uint8_t*)&x, sizeof(uint32_t)); } int rtlsdr_rpc_msg_push_int32(rtlsdr_rpc_msg_t* msg, int x) { if (check_size_or_realloc(msg, sizeof(x))) return -1; push_uint32_safe(msg, (uint32_t)x); return 0; } int rtlsdr_rpc_msg_push_uint32(rtlsdr_rpc_msg_t* msg, uint32_t x) { if (check_size_or_realloc(msg, sizeof(x))) return -1; push_uint32_safe(msg, x); return 0; } void rtlsdr_rpc_msg_push_uint32_safe(rtlsdr_rpc_msg_t* msg, uint32_t x) { push_uint32_safe(msg, x); } int rtlsdr_rpc_msg_push_str(rtlsdr_rpc_msg_t* msg, const char* s) { if (check_size_or_realloc(msg, strlen(s) + 1)) return -1; push_mem_safe(msg, (const uint8_t*)s, strlen(s) + 1); return 0; } int rtlsdr_rpc_msg_push_buf(rtlsdr_rpc_msg_t* msg, const uint8_t* buf, size_t size) { size_t total_size = sizeof(uint32_t) + size; if (check_size_or_realloc(msg, total_size)) return -1; push_uint32_safe(msg, (uint32_t)size); push_mem_safe(msg, buf, size); return 0; } void rtlsdr_rpc_msg_skip_safe(rtlsdr_rpc_msg_t* msg, size_t size) { msg->off += size; } int rtlsdr_rpc_msg_pop_int32(rtlsdr_rpc_msg_t* msg, int32_t* x) { return pop_uint32(msg, (uint32_t*)x); } int rtlsdr_rpc_msg_pop_uint32(rtlsdr_rpc_msg_t* msg, uint32_t* x) { return pop_uint32(msg, x); } int rtlsdr_rpc_msg_pop_str(rtlsdr_rpc_msg_t* msg, const char** s) { size_t i; *s = (const char*)(msg->fmt + msg->off); for (i = msg->off; i != msg->size; ++i) { if (msg->fmt[i] == 0) { msg->off = i + 1; return 0; } } return -1; } int rtlsdr_rpc_msg_pop_buf (rtlsdr_rpc_msg_t* msg, const uint8_t** buf, size_t* size) { uint32_t x; if (pop_uint32(msg, &x)) return -1; if ((msg->off + x) > msg->size) return -1; *buf = (const uint8_t*)(msg->fmt + msg->off); msg->off += x; *size = (size_t)x; return 0; } static void put_uint8(void* p, uint8_t x) { memcpy(p, (const void*)&x, sizeof(x)); } static void put_uint16(void* p, uint16_t x) { #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #error "unsupported endianness" #endif memcpy(p, (const void*)&x, sizeof(x)); } static void put_uint32(void* p, uint32_t x) { #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #error "unsupported endianness" #endif memcpy(p, (const void*)&x, sizeof(x)); } static uint8_t get_uint8(const void* p) { uint8_t x; memcpy((void*)&x, p, sizeof(x)); return x; } static uint16_t get_uint16(const void* p) { #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #error "unsupported endianness" #endif uint16_t x; memcpy((void*)&x, p, sizeof(x)); return x; } static uint32_t get_uint32(const void* p) { #if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #error "unsupported endianness" #endif uint32_t x; memcpy((void*)&x, p, sizeof(x)); return x; } void rtlsdr_rpc_msg_set_size(rtlsdr_rpc_msg_t* msg, size_t size) { rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; put_uint32(&fmt->size, (uint32_t)size); } size_t rtlsdr_rpc_msg_get_size(const rtlsdr_rpc_msg_t* msg) { const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; return (size_t)get_uint32(&fmt->size); } void rtlsdr_rpc_msg_set_op(rtlsdr_rpc_msg_t* msg, rtlsdr_rpc_op_t op) { rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; put_uint8(&fmt->op, (uint8_t)op); } rtlsdr_rpc_op_t rtlsdr_rpc_msg_get_op(const rtlsdr_rpc_msg_t* msg) { const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; return (rtlsdr_rpc_op_t)get_uint8(&fmt->op); } void rtlsdr_rpc_msg_set_id(rtlsdr_rpc_msg_t* msg, uint8_t id) { rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; put_uint16(&fmt->id, id); } uint8_t rtlsdr_rpc_msg_get_id(const rtlsdr_rpc_msg_t* msg) { const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; return get_uint8(&fmt->id); } void rtlsdr_rpc_msg_set_err(rtlsdr_rpc_msg_t* msg, int err) { rtlsdr_rpc_fmt_t* const fmt = (rtlsdr_rpc_fmt_t*)msg->fmt; put_uint32(&fmt->err, (uint32_t)err); } int rtlsdr_rpc_msg_get_err(const rtlsdr_rpc_msg_t* msg) { const rtlsdr_rpc_fmt_t* const fmt = (const rtlsdr_rpc_fmt_t*)msg->fmt; return (int)get_uint32(&fmt->err); } ================================================ FILE: src/tuner_e4k.c ================================================ /* * Elonics E4000 tuner driver * * (C) 2011-2012 by Harald Welte * (C) 2012 by Sylvain Munaut * (C) 2012 by Hoernchen * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #define PRINT_PLL_ERRORS 0 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) /* If this is defined, the limits are somewhat relaxed compared to what the * vendor claims is possible */ #define OUT_OF_SPEC #define MHZ(x) ((x)*1000*1000) #define KHZ(x) ((x)*1000) uint32_t unsigned_delta(uint32_t a, uint32_t b) { if (a > b) return a - b; else return b - a; } /* look-up table bit-width -> mask */ static const uint8_t width2mask[] = { 0, 1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff }; /*********************************************************************** * Register Access */ /*! \brief Write a register of the tuner chip * \param[in] e4k reference to the tuner * \param[in] reg number of the register * \param[in] val value to be written * \returns 0 on success, negative in case of error */ static int e4k_reg_write(struct e4k_state *e4k, uint8_t reg, uint8_t val) { int r; uint8_t data[2]; data[0] = reg; data[1] = val; r = rtlsdr_i2c_write_fn(e4k->rtl_dev, e4k->i2c_addr, data, 2); return r == 2 ? 0 : -1; } /*! \brief Read a register of the tuner chip * \param[in] e4k reference to the tuner * \param[in] reg number of the register * \returns positive 8bit register contents on success, negative in case of error */ static int e4k_reg_read(struct e4k_state *e4k, uint8_t reg) { uint8_t data = reg; if (rtlsdr_i2c_write_fn(e4k->rtl_dev, e4k->i2c_addr, &data, 1) < 1) return -1; if (rtlsdr_i2c_read_fn(e4k->rtl_dev, e4k->i2c_addr, &data, 1) < 1) return -1; return data; } /*! \brief Set or clear some (masked) bits inside a register * \param[in] e4k reference to the tuner * \param[in] reg number of the register * \param[in] mask bit-mask of the value * \param[in] val data value to be written to register * \returns 0 on success, negative in case of error */ static int e4k_reg_set_mask(struct e4k_state *e4k, uint8_t reg, uint8_t mask, uint8_t val) { uint8_t tmp = e4k_reg_read(e4k, reg); if ((tmp & mask) == val) return 0; return e4k_reg_write(e4k, reg, (tmp & ~mask) | (val & mask)); } /*! \brief Write a given field inside a register * \param[in] e4k reference to the tuner * \param[in] field structure describing the field * \param[in] val value to be written * \returns 0 on success, negative in case of error */ static int e4k_field_write(struct e4k_state *e4k, const struct reg_field *field, uint8_t val) { int rc; uint8_t mask; rc = e4k_reg_read(e4k, field->reg); if (rc < 0) return rc; mask = width2mask[field->width] << field->shift; return e4k_reg_set_mask(e4k, field->reg, mask, val << field->shift); } /*! \brief Read a given field inside a register * \param[in] e4k reference to the tuner * \param[in] field structure describing the field * \returns positive value of the field, negative in case of error */ static int e4k_field_read(struct e4k_state *e4k, const struct reg_field *field) { int rc; rc = e4k_reg_read(e4k, field->reg); if (rc < 0) return rc; rc = (rc >> field->shift) & width2mask[field->width]; return rc; } /*********************************************************************** * Filter Control */ static const uint32_t rf_filt_center_uhf[] = { MHZ(360), MHZ(380), MHZ(405), MHZ(425), MHZ(450), MHZ(475), MHZ(505), MHZ(540), MHZ(575), MHZ(615), MHZ(670), MHZ(720), MHZ(760), MHZ(840), MHZ(890), MHZ(970) }; static const uint32_t rf_filt_center_l[] = { MHZ(1300), MHZ(1320), MHZ(1360), MHZ(1410), MHZ(1445), MHZ(1460), MHZ(1490), MHZ(1530), MHZ(1560), MHZ(1590), MHZ(1640), MHZ(1660), MHZ(1680), MHZ(1700), MHZ(1720), MHZ(1750) }; static int closest_arr_idx(const uint32_t *arr, unsigned int arr_size, uint32_t freq) { unsigned int i, bi = 0; uint32_t best_delta = 0xffffffff; /* iterate over the array containing a list of the center * frequencies, selecting the closest one */ for (i = 0; i < arr_size; i++) { uint32_t delta = unsigned_delta(freq, arr[i]); if (delta < best_delta) { best_delta = delta; bi = i; } } return bi; } /* return 4-bit index as to which RF filter to select */ static int choose_rf_filter(enum e4k_band band, uint32_t freq) { int rc; switch (band) { case E4K_BAND_VHF2: case E4K_BAND_VHF3: rc = 0; break; case E4K_BAND_UHF: rc = closest_arr_idx(rf_filt_center_uhf, ARRAY_SIZE(rf_filt_center_uhf), freq); break; case E4K_BAND_L: rc = closest_arr_idx(rf_filt_center_l, ARRAY_SIZE(rf_filt_center_l), freq); break; default: rc = -EINVAL; break; } return rc; } /* \brief Automatically select apropriate RF filter based on e4k state */ int e4k_rf_filter_set(struct e4k_state *e4k) { int rc; rc = choose_rf_filter(e4k->band, e4k->vco.flo); if (rc < 0) return rc; return e4k_reg_set_mask(e4k, E4K_REG_FILT1, 0xF, rc); } /* Mixer Filter */ static const uint32_t mix_filter_bw[] = { KHZ(27000), KHZ(27000), KHZ(27000), KHZ(27000), KHZ(27000), KHZ(27000), KHZ(27000), KHZ(27000), KHZ(4600), KHZ(4200), KHZ(3800), KHZ(3400), KHZ(3300), KHZ(2700), KHZ(2300), KHZ(1900) }; /* IF RC Filter */ static const uint32_t ifrc_filter_bw[] = { KHZ(21400), KHZ(21000), KHZ(17600), KHZ(14700), KHZ(12400), KHZ(10600), KHZ(9000), KHZ(7700), KHZ(6400), KHZ(5300), KHZ(4400), KHZ(3400), KHZ(2600), KHZ(1800), KHZ(1200), KHZ(1000) }; /* IF Channel Filter */ static const uint32_t ifch_filter_bw[] = { KHZ(5500), KHZ(5300), KHZ(5000), KHZ(4800), KHZ(4600), KHZ(4400), KHZ(4300), KHZ(4100), KHZ(3900), KHZ(3800), KHZ(3700), KHZ(3600), KHZ(3400), KHZ(3300), KHZ(3200), KHZ(3100), KHZ(3000), KHZ(2950), KHZ(2900), KHZ(2800), KHZ(2750), KHZ(2700), KHZ(2600), KHZ(2550), KHZ(2500), KHZ(2450), KHZ(2400), KHZ(2300), KHZ(2280), KHZ(2240), KHZ(2200), KHZ(2150) }; static const uint32_t *if_filter_bw[] = { mix_filter_bw, ifch_filter_bw, ifrc_filter_bw, }; static const uint32_t if_filter_bw_len[] = { ARRAY_SIZE(mix_filter_bw), ARRAY_SIZE(ifch_filter_bw), ARRAY_SIZE(ifrc_filter_bw), }; static const struct reg_field if_filter_fields[] = { { E4K_REG_FILT2, 4, 4, }, { E4K_REG_FILT3, 0, 5, }, { E4K_REG_FILT2, 0, 4, } }; static int find_if_bw(enum e4k_if_filter filter, uint32_t bw) { if (filter >= ARRAY_SIZE(if_filter_bw)) return -EINVAL; return closest_arr_idx(if_filter_bw[filter], if_filter_bw_len[filter], bw); } /*! \brief Set the filter band-width of any of the IF filters * \param[in] e4k reference to the tuner chip * \param[in] filter filter to be configured * \param[in] bandwidth bandwidth to be configured * \returns positive actual filter band-width, negative in case of error */ int e4k_if_filter_bw_set(struct e4k_state *e4k, enum e4k_if_filter filter, uint32_t bandwidth) { int bw_idx; const struct reg_field *field; if (filter >= ARRAY_SIZE(if_filter_bw)) return -EINVAL; bw_idx = find_if_bw(filter, bandwidth); field = &if_filter_fields[filter]; return e4k_field_write(e4k, field, bw_idx); } /*! \brief Enables / Disables the channel filter * \param[in] e4k reference to the tuner chip * \param[in] on 1=filter enabled, 0=filter disabled * \returns 0 success, negative errors */ int e4k_if_filter_chan_enable(struct e4k_state *e4k, int on) { return e4k_reg_set_mask(e4k, E4K_REG_FILT3, E4K_FILT3_DISABLE, on ? 0 : E4K_FILT3_DISABLE); } int e4k_if_filter_bw_get(struct e4k_state *e4k, enum e4k_if_filter filter) { const uint32_t *arr; int rc; const struct reg_field *field; if (filter >= ARRAY_SIZE(if_filter_bw)) return -EINVAL; field = &if_filter_fields[filter]; rc = e4k_field_read(e4k, field); if (rc < 0) return rc; arr = if_filter_bw[filter]; return arr[rc]; } /*********************************************************************** * Frequency Control */ #define E4K_FVCO_MIN_KHZ 2600000 /* 2.6 GHz */ #define E4K_FVCO_MAX_KHZ 3900000 /* 3.9 GHz */ #define E4K_PLL_Y 65536 #ifdef OUT_OF_SPEC #define E4K_FLO_MIN_MHZ 50 #define E4K_FLO_MAX_MHZ 2200UL #else #define E4K_FLO_MIN_MHZ 64 #define E4K_FLO_MAX_MHZ 1700 #endif struct pll_settings { uint32_t freq; uint8_t reg_synth7; uint8_t mult; }; static const struct pll_settings pll_vars[] = { {KHZ(72400), (1 << 3) | 7, 48}, {KHZ(81200), (1 << 3) | 6, 40}, {KHZ(108300), (1 << 3) | 5, 32}, {KHZ(162500), (1 << 3) | 4, 24}, {KHZ(216600), (1 << 3) | 3, 16}, {KHZ(325000), (1 << 3) | 2, 12}, {KHZ(350000), (1 << 3) | 1, 8}, {KHZ(432000), (0 << 3) | 3, 8}, {KHZ(667000), (0 << 3) | 2, 6}, {KHZ(1200000), (0 << 3) | 1, 4} }; static int is_fvco_valid(uint32_t fvco_z) { /* check if the resulting fosc is valid */ if (fvco_z/1000 < E4K_FVCO_MIN_KHZ || fvco_z/1000 > E4K_FVCO_MAX_KHZ) { fprintf(stderr, "[E4K] Fvco %u invalid\n", fvco_z); return 0; } return 1; } static int is_fosc_valid(uint32_t fosc) { if (fosc < MHZ(16) || fosc > MHZ(30)) { fprintf(stderr, "[E4K] Fosc %u invalid\n", fosc); return 0; } return 1; } static int is_z_valid(uint32_t z) { if (z > 255) { fprintf(stderr, "[E4K] Z %u invalid\n", z); return 0; } return 1; } /*! \brief Determine if 3-phase mixing shall be used or not */ static int use_3ph_mixing(uint32_t flo) { /* this is a magic number somewhre between VHF and UHF */ if (flo < MHZ(350)) return 1; return 0; } /* \brief compute Fvco based on Fosc, Z and X * \returns positive value (Fvco in Hz), 0 in case of error */ static uint64_t compute_fvco(uint32_t f_osc, uint8_t z, uint16_t x) { uint64_t fvco_z, fvco_x, fvco; /* We use the following transformation in order to * handle the fractional part with integer arithmetic: * Fvco = Fosc * (Z + X/Y) <=> Fvco = Fosc * Z + (Fosc * X)/Y * This avoids X/Y = 0. However, then we would overflow a 32bit * integer, as we cannot hold e.g. 26 MHz * 65536 either. */ fvco_z = (uint64_t)f_osc * z; #if 0 if (!is_fvco_valid(fvco_z)) return 0; #endif fvco_x = ((uint64_t)f_osc * x) / E4K_PLL_Y; fvco = fvco_z + fvco_x; return fvco; } static uint32_t compute_flo(uint32_t f_osc, uint8_t z, uint16_t x, uint8_t r) { uint64_t fvco = compute_fvco(f_osc, z, x); if (fvco == 0) return -EINVAL; return fvco / r; } static int e4k_band_set(struct e4k_state *e4k, enum e4k_band band) { int rc; switch (band) { case E4K_BAND_VHF2: case E4K_BAND_VHF3: case E4K_BAND_UHF: e4k_reg_write(e4k, E4K_REG_BIAS, 3); break; case E4K_BAND_L: e4k_reg_write(e4k, E4K_REG_BIAS, 0); break; } /* workaround: if we don't reset this register before writing to it, * we get a gap between 325-350 MHz */ rc = e4k_reg_set_mask(e4k, E4K_REG_SYNTH1, 0x06, 0); rc = e4k_reg_set_mask(e4k, E4K_REG_SYNTH1, 0x06, band << 1); if (rc >= 0) e4k->band = band; return rc; } /*! \brief Compute PLL parameters for givent target frequency * \param[out] oscp Oscillator parameters, if computation successful * \param[in] fosc Clock input frequency applied to the chip (Hz) * \param[in] intended_flo target tuning frequency (Hz) * \returns actual PLL frequency, as close as possible to intended_flo, * 0 in case of error */ uint32_t e4k_compute_pll_params(struct e4k_pll_params *oscp, uint32_t fosc, uint32_t intended_flo) { uint32_t i; uint8_t r = 2; uint64_t intended_fvco, remainder; uint64_t z = 0; uint32_t x; int flo; int three_phase_mixing = 0; oscp->r_idx = 0; if (!is_fosc_valid(fosc)) return 0; for(i = 0; i < ARRAY_SIZE(pll_vars); ++i) { if(intended_flo < pll_vars[i].freq) { three_phase_mixing = (pll_vars[i].reg_synth7 & 0x08) ? 1 : 0; oscp->r_idx = pll_vars[i].reg_synth7; r = pll_vars[i].mult; break; } } //fprintf(stderr, "[E4K] Fint=%u, R=%u\n", intended_flo, r); /* flo(max) = 1700MHz, R(max) = 48, we need 64bit! */ intended_fvco = (uint64_t)intended_flo * r; /* compute integral component of multiplier */ z = intended_fvco / fosc; /* compute fractional part. this will not overflow, * as fosc(max) = 30MHz and z(max) = 255 */ remainder = intended_fvco - (fosc * z); /* remainder(max) = 30MHz, E4K_PLL_Y = 65536 -> 64bit! */ x = (remainder * E4K_PLL_Y) / fosc; /* x(max) as result of this computation is 65536 */ flo = compute_flo(fosc, z, x, r); oscp->fosc = fosc; oscp->flo = flo; oscp->intended_flo = intended_flo; oscp->r = r; // oscp->r_idx = pll_vars[i].reg_synth7 & 0x0; oscp->threephase = three_phase_mixing; oscp->x = x; oscp->z = z; return flo; } int e4k_tune_params(struct e4k_state *e4k, struct e4k_pll_params *p) { /* program R + 3phase/2phase */ e4k_reg_write(e4k, E4K_REG_SYNTH7, p->r_idx); /* program Z */ e4k_reg_write(e4k, E4K_REG_SYNTH3, p->z); /* program X */ e4k_reg_write(e4k, E4K_REG_SYNTH4, p->x & 0xff); e4k_reg_write(e4k, E4K_REG_SYNTH5, p->x >> 8); /* we're in auto calibration mode, so there's no need to trigger it */ memcpy(&e4k->vco, p, sizeof(e4k->vco)); /* set the band */ if (e4k->vco.flo < MHZ(140)) e4k_band_set(e4k, E4K_BAND_VHF2); else if (e4k->vco.flo < MHZ(350)) e4k_band_set(e4k, E4K_BAND_VHF3); else if (e4k->vco.flo < MHZ(1135)) e4k_band_set(e4k, E4K_BAND_UHF); else e4k_band_set(e4k, E4K_BAND_L); /* select and set proper RF filter */ e4k_rf_filter_set(e4k); return e4k->vco.flo; } /*! \brief High-level tuning API, just specify frquency * * This function will compute matching PLL parameters, program them into the * hardware and set the band as well as RF filter. * * \param[in] e4k reference to tuner * \param[in] freq frequency in Hz * \returns actual tuned frequency, negative in case of error */ int e4k_tune_freq(struct e4k_state *e4k, uint32_t freq) { uint32_t rc; struct e4k_pll_params p; /* determine PLL parameters */ rc = e4k_compute_pll_params(&p, e4k->vco.fosc, freq); if (!rc) return -EINVAL; /* actually tune to those parameters */ rc = e4k_tune_params(e4k, &p); /* check PLL lock */ rc = e4k_reg_read(e4k, E4K_REG_SYNTH1); if (!(rc & 0x01)) { #if PRINT_PLL_ERRORS fprintf(stderr, "[E4K] PLL not locked for %u Hz!\n", freq); #endif return -1; } return 0; } /*********************************************************************** * Gain Control */ static const int8_t if_stage1_gain[] = { -3, 6 }; static const int8_t if_stage23_gain[] = { 0, 3, 6, 9 }; static const int8_t if_stage4_gain[] = { 0, 1, 2, 2 }; static const int8_t if_stage56_gain[] = { 3, 6, 9, 12, 15, 15, 15, 15 }; static const int8_t *if_stage_gain[] = { 0, if_stage1_gain, if_stage23_gain, if_stage23_gain, if_stage4_gain, if_stage56_gain, if_stage56_gain }; static const uint8_t if_stage_gain_len[] = { 0, ARRAY_SIZE(if_stage1_gain), ARRAY_SIZE(if_stage23_gain), ARRAY_SIZE(if_stage23_gain), ARRAY_SIZE(if_stage4_gain), ARRAY_SIZE(if_stage56_gain), ARRAY_SIZE(if_stage56_gain) }; static const struct reg_field if_stage_gain_regs[] = { { 0, 0, 0 }, { E4K_REG_GAIN3, 0, 1 }, { E4K_REG_GAIN3, 1, 2 }, { E4K_REG_GAIN3, 3, 2 }, { E4K_REG_GAIN3, 5, 2 }, { E4K_REG_GAIN4, 0, 3 }, { E4K_REG_GAIN4, 3, 3 } }; static const int32_t lnagain[] = { -50, 0, -25, 1, 0, 4, 25, 5, 50, 6, 75, 7, 100, 8, 125, 9, 150, 10, 175, 11, 200, 12, 250, 13, 300, 14, }; static const int32_t enhgain[] = { 10, 30, 50, 70 }; int e4k_set_lna_gain(struct e4k_state *e4k, int32_t gain) { uint32_t i; for(i = 0; i < ARRAY_SIZE(lnagain)/2; ++i) { if(lnagain[i*2] == gain) { e4k_reg_set_mask(e4k, E4K_REG_GAIN1, 0xf, lnagain[i*2+1]); return gain; } } return -EINVAL; } int e4k_set_enh_gain(struct e4k_state *e4k, int32_t gain) { uint32_t i; for(i = 0; i < ARRAY_SIZE(enhgain); ++i) { if(enhgain[i] == gain) { e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, E4K_AGC11_LNA_GAIN_ENH | (i << 1)); return gain; } } e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, 0); /* special case: 0 = off*/ if(0 == gain) return 0; else return -EINVAL; } int e4k_enable_manual_gain(struct e4k_state *e4k, uint8_t manual) { if (manual) { /* Set LNA mode to manual */ e4k_reg_set_mask(e4k, E4K_REG_AGC1, E4K_AGC1_MOD_MASK, E4K_AGC_MOD_SERIAL); /* Set Mixer Gain Control to manual */ e4k_reg_set_mask(e4k, E4K_REG_AGC7, E4K_AGC7_MIX_GAIN_AUTO, 0); } else { /* Set LNA mode to auto */ e4k_reg_set_mask(e4k, E4K_REG_AGC1, E4K_AGC1_MOD_MASK, E4K_AGC_MOD_IF_SERIAL_LNA_AUTON); /* Set Mixer Gain Control to auto */ e4k_reg_set_mask(e4k, E4K_REG_AGC7, E4K_AGC7_MIX_GAIN_AUTO, 1); e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, 0); } return 0; } static int find_stage_gain(uint8_t stage, int8_t val) { const int8_t *arr; int i; if (stage >= ARRAY_SIZE(if_stage_gain)) return -EINVAL; arr = if_stage_gain[stage]; for (i = 0; i < if_stage_gain_len[stage]; i++) { if (arr[i] == val) return i; } return -EINVAL; } /*! \brief Set the gain of one of the IF gain stages * \param [e4k] handle to the tuner chip * \param [stage] number of the stage (1..6) * \param [value] gain value in dB * \returns 0 on success, negative in case of error */ int e4k_if_gain_set(struct e4k_state *e4k, uint8_t stage, int8_t value) { int rc; uint8_t mask; const struct reg_field *field; rc = find_stage_gain(stage, value); if (rc < 0) return rc; /* compute the bit-mask for the given gain field */ field = &if_stage_gain_regs[stage]; mask = width2mask[field->width] << field->shift; return e4k_reg_set_mask(e4k, field->reg, mask, rc << field->shift); } int e4k_mixer_gain_set(struct e4k_state *e4k, int8_t value) { uint8_t bit; switch (value) { case 4: bit = 0; break; case 12: bit = 1; break; default: return -EINVAL; } return e4k_reg_set_mask(e4k, E4K_REG_GAIN2, 1, bit); } int e4k_commonmode_set(struct e4k_state *e4k, int8_t value) { if(value < 0) return -EINVAL; else if(value > 7) return -EINVAL; return e4k_reg_set_mask(e4k, E4K_REG_DC7, 7, value); } /*********************************************************************** * DC Offset */ int e4k_manual_dc_offset(struct e4k_state *e4k, int8_t iofs, int8_t irange, int8_t qofs, int8_t qrange) { int res; if((iofs < 0x00) || (iofs > 0x3f)) return -EINVAL; if((irange < 0x00) || (irange > 0x03)) return -EINVAL; if((qofs < 0x00) || (qofs > 0x3f)) return -EINVAL; if((qrange < 0x00) || (qrange > 0x03)) return -EINVAL; res = e4k_reg_set_mask(e4k, E4K_REG_DC2, 0x3f, iofs); if(res < 0) return res; res = e4k_reg_set_mask(e4k, E4K_REG_DC3, 0x3f, qofs); if(res < 0) return res; res = e4k_reg_set_mask(e4k, E4K_REG_DC4, 0x33, (qrange << 4) | irange); return res; } /*! \brief Perform a DC offset calibration right now * \param [e4k] handle to the tuner chip */ int e4k_dc_offset_calibrate(struct e4k_state *e4k) { /* make sure the DC range detector is enabled */ e4k_reg_set_mask(e4k, E4K_REG_DC5, E4K_DC5_RANGE_DET_EN, E4K_DC5_RANGE_DET_EN); return e4k_reg_write(e4k, E4K_REG_DC1, 0x01); } static const int8_t if_gains_max[] = { 0, 6, 9, 9, 2, 15, 15 }; struct gain_comb { int8_t mixer_gain; int8_t if1_gain; uint8_t reg; }; static const struct gain_comb dc_gain_comb[] = { { 4, -3, 0x50 }, { 4, 6, 0x51 }, { 12, -3, 0x52 }, { 12, 6, 0x53 }, }; #define TO_LUT(offset, range) (offset | (range << 6)) int e4k_dc_offset_gen_table(struct e4k_state *e4k) { uint32_t i; /* FIXME: read ont current gain values and write them back * before returning to the caller */ /* disable auto mixer gain */ e4k_reg_set_mask(e4k, E4K_REG_AGC7, E4K_AGC7_MIX_GAIN_AUTO, 0); /* set LNA/IF gain to full manual */ e4k_reg_set_mask(e4k, E4K_REG_AGC1, E4K_AGC1_MOD_MASK, E4K_AGC_MOD_SERIAL); /* set all 'other' gains to maximum */ for (i = 2; i <= 6; i++) e4k_if_gain_set(e4k, i, if_gains_max[i]); /* iterate over all mixer + if_stage_1 gain combinations */ for (i = 0; i < ARRAY_SIZE(dc_gain_comb); i++) { uint8_t offs_i, offs_q, range, range_i, range_q; /* set the combination of mixer / if1 gain */ e4k_mixer_gain_set(e4k, dc_gain_comb[i].mixer_gain); e4k_if_gain_set(e4k, 1, dc_gain_comb[i].if1_gain); /* perform actual calibration */ e4k_dc_offset_calibrate(e4k); /* extract I/Q offset and range values */ offs_i = e4k_reg_read(e4k, E4K_REG_DC2) & 0x3f; offs_q = e4k_reg_read(e4k, E4K_REG_DC3) & 0x3f; range = e4k_reg_read(e4k, E4K_REG_DC4); range_i = range & 0x3; range_q = (range >> 4) & 0x3; fprintf(stderr, "[E4K] Table %u I=%u/%u, Q=%u/%u\n", i, range_i, offs_i, range_q, offs_q); /* write into the table */ e4k_reg_write(e4k, dc_gain_comb[i].reg, TO_LUT(offs_q, range_q)); e4k_reg_write(e4k, dc_gain_comb[i].reg + 0x10, TO_LUT(offs_i, range_i)); } return 0; } /*********************************************************************** * Standby */ /*! \brief Enable/disable standby mode */ int e4k_standby(struct e4k_state *e4k, int enable) { e4k_reg_set_mask(e4k, E4K_REG_MASTER1, E4K_MASTER1_NORM_STBY, enable ? 0 : E4K_MASTER1_NORM_STBY); return 0; } /*********************************************************************** * Initialization */ static int magic_init(struct e4k_state *e4k) { e4k_reg_write(e4k, 0x7e, 0x01); e4k_reg_write(e4k, 0x7f, 0xfe); e4k_reg_write(e4k, 0x82, 0x00); e4k_reg_write(e4k, 0x86, 0x50); /* polarity A */ e4k_reg_write(e4k, 0x87, 0x20); e4k_reg_write(e4k, 0x88, 0x01); e4k_reg_write(e4k, 0x9f, 0x7f); e4k_reg_write(e4k, 0xa0, 0x07); return 0; } /*! \brief Initialize the E4K tuner */ int e4k_init(struct e4k_state *e4k) { /* make a dummy i2c read or write command, will not be ACKed! */ e4k_reg_read(e4k, 0); /* Make sure we reset everything and clear POR indicator */ e4k_reg_write(e4k, E4K_REG_MASTER1, E4K_MASTER1_RESET | E4K_MASTER1_NORM_STBY | E4K_MASTER1_POR_DET ); /* Configure clock input */ e4k_reg_write(e4k, E4K_REG_CLK_INP, 0x00); /* Disable clock output */ e4k_reg_write(e4k, E4K_REG_REF_CLK, 0x00); e4k_reg_write(e4k, E4K_REG_CLKOUT_PWDN, 0x96); /* Write some magic values into registers */ magic_init(e4k); #if 0 /* Set common mode voltage a bit higher for more margin 850 mv */ e4k_commonmode_set(e4k, 4); /* Initialize DC offset lookup tables */ e4k_dc_offset_gen_table(e4k); /* Enable time variant DC correction */ e4k_reg_write(e4k, E4K_REG_DCTIME1, 0x01); e4k_reg_write(e4k, E4K_REG_DCTIME2, 0x01); #endif /* Set LNA mode to manual */ e4k_reg_write(e4k, E4K_REG_AGC4, 0x10); /* High threshold */ e4k_reg_write(e4k, E4K_REG_AGC5, 0x04); /* Low threshold */ e4k_reg_write(e4k, E4K_REG_AGC6, 0x1a); /* LNA calib + loop rate */ e4k_reg_set_mask(e4k, E4K_REG_AGC1, E4K_AGC1_MOD_MASK, E4K_AGC_MOD_SERIAL); /* Set Mixer Gain Control to manual */ e4k_reg_set_mask(e4k, E4K_REG_AGC7, E4K_AGC7_MIX_GAIN_AUTO, 0); #if 0 /* Enable LNA Gain enhancement */ e4k_reg_set_mask(e4k, E4K_REG_AGC11, 0x7, E4K_AGC11_LNA_GAIN_ENH | (2 << 1)); /* Enable automatic IF gain mode switching */ e4k_reg_set_mask(e4k, E4K_REG_AGC8, 0x1, E4K_AGC8_SENS_LIN_AUTO); #endif /* Use auto-gain as default */ e4k_enable_manual_gain(e4k, 0); /* Select moderate gain levels */ e4k_if_gain_set(e4k, 1, 6); e4k_if_gain_set(e4k, 2, 0); e4k_if_gain_set(e4k, 3, 0); e4k_if_gain_set(e4k, 4, 0); e4k_if_gain_set(e4k, 5, 9); e4k_if_gain_set(e4k, 6, 9); /* Set the most narrow filter we can possibly use */ e4k_if_filter_bw_set(e4k, E4K_IF_FILTER_MIX, KHZ(1900)); e4k_if_filter_bw_set(e4k, E4K_IF_FILTER_RC, KHZ(1000)); e4k_if_filter_bw_set(e4k, E4K_IF_FILTER_CHAN, KHZ(2150)); e4k_if_filter_chan_enable(e4k, 1); /* Disable time variant DC correction and LUT */ e4k_reg_set_mask(e4k, E4K_REG_DC5, 0x03, 0); e4k_reg_set_mask(e4k, E4K_REG_DCTIME1, 0x03, 0); e4k_reg_set_mask(e4k, E4K_REG_DCTIME2, 0x03, 0); return 0; } ================================================ FILE: src/tuner_fc0012.c ================================================ /* * Fitipower FC0012 tuner driver * * Copyright (C) 2012 Hans-Frieder Vogt * * modified for use in librtlsdr * Copyright (C) 2012 Steve Markgraf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include "rtlsdr_i2c.h" #include "tuner_fc0012.h" #define PRINT_PLL_ERRORS 0 static int fc0012_writereg(void *dev, uint8_t reg, uint8_t val) { uint8_t data[2]; data[0] = reg; data[1] = val; if (rtlsdr_i2c_write_fn(dev, FC0012_I2C_ADDR, data, 2) < 0) return -1; return 0; } static int fc0012_readreg(void *dev, uint8_t reg, uint8_t *val) { uint8_t data = reg; if (rtlsdr_i2c_write_fn(dev, FC0012_I2C_ADDR, &data, 1) < 0) return -1; if (rtlsdr_i2c_read_fn(dev, FC0012_I2C_ADDR, &data, 1) < 0) return -1; *val = data; return 0; } /* expose/permit tuner specific i2c register hacking! */ int fc0012_set_i2c_register(void *dev, unsigned i2c_register, unsigned data) { uint8_t reg = i2c_register & 0xFF; uint8_t reg_val = data & 0xFF; return fc0012_writereg(dev, reg, reg_val); } int fc0012_get_i2c_register(void *dev, unsigned char* data, int len) { int len1; data[0] = 0; /* The lower 16 I2C registers can be read with the normal read fct, * the upper ones are read from the cache */ if(len < 16) len1 = len; else len1 = 16; if (rtlsdr_i2c_write_fn(dev, FC0012_I2C_ADDR, data, 1) < 0) return -1; if (rtlsdr_i2c_read_fn(dev, FC0012_I2C_ADDR, data, len1) < 0) return -1; if(len > 16) { len1 = len - 16; data[16] = 16; if (rtlsdr_i2c_write_fn(dev, FC0012_I2C_ADDR, data+16, 1) < 0) return -1; if (rtlsdr_i2c_read_fn(dev, FC0012_I2C_ADDR, data+16, len1) < 0) return -1; } return 0; } static int print_registers(void *dev) { uint8_t data[32]; unsigned int i; if (fc0012_get_i2c_register(dev, data, 32) < 0) return -1; for(i=0; i<16; i++) printf("%02x ", data[i]); printf("\n"); for(i=16; i<32; i++) printf("%02x ", data[i]); printf("\n"); return 0; } /* Incomplete list of register settings: * * Name Reg BitsDesc * CHIP_ID 0x00 0-7 Chip ID (constant 0xA1) * RF_A 0x01 0-3 Number of count-to-9 cycles in RF * divider (suggested: 2..9) * RF_M 0x02 0-7 Total number of cycles (to-8 and to-9) * in RF divider * RF_K_HIGH 0x03 0-6 Bits 8..14 of fractional divider * RF_K_LOW 0x04 0-7 Bits 0..7 of fractional RF divider * RF_OUTDIV_A 0x05 3-7 Power of two required? * LNA_POWER_DOWN 0x06 0 Set to 1 to switch off low noise amp * RF_OUTDIV_B 0x06 1 Set to select 3 instead of 2 for the * RF output divider * VCO_SPEED 0x06 3 Select tuning range of VCO: * 0 = Low range, (ca. 1.1 - 1.5GHz) * 1 = High range (ca. 1.4 - 1.8GHz) * BANDWIDTH 0x06 6-7 Set bandwidth. 6MHz = 0x80, 7MHz=0x40 * 8MHz=0x00 * XTAL_SPEED 0x07 5 Set to 1 for 28.8MHz Crystal input * or 0 for 36MHz * 0x08 0-7 * EN_CAL_RSSI 0x09 4 Enable calibrate RSSI * (Receive Signal Strength Indicator) * LNA_FORCE 0x0d 0 * AGC_FORCE 0x0d ? * VCO_CALIB 0x0e 7 Set high then low to calibrate VCO * (fast lock?) * VCO_VOLTAGE 0x0e 0-6 Read Control voltage of VCO * (big value -> low freq) * LNA_GAIN 0x13 3-4 Low noise amp gain * LNA_COMPS 0x15 3 ? */ int fc0012_init(void *dev) { int ret = 0; unsigned int i; uint8_t reg[] = { 0x00, /* dummy reg. 0 */ 0x05, /* reg. 0x01 */ 0x10, /* reg. 0x02 */ 0x00, /* reg. 0x03 */ 0x00, /* reg. 0x04 */ 0x0f, /* reg. 0x05: may also be 0x0a */ 0x00, /* reg. 0x06: divider 2, VCO slow */ 0x00, /* reg. 0x07: may also be 0x0f */ 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, Loop Bw 1/8 */ 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */ 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ 0x82, /* reg. 0x0b: Output Clock is same as clock frequency, may also be 0x83 */ 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ 0x02, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, 0x02 for DVB-T */ 0x00, /* reg. 0x0e */ 0x00, /* reg. 0x0f */ 0x00, /* reg. 0x10: may also be 0x0d */ 0x0a, /* reg. 0x11 */ 0x51, /* reg. 0x12: Set to maximum gain */ 0x08, /* reg. 0x13: Set to Middle Gain: 0x08, Low Gain: 0x00, High Gain: 0x10, enable IX2: 0x80 */ 0x00, /* reg. 0x14 */ 0x04, /* reg. 0x15: Enable LNA COMPS */ }; #if 0 switch (rtlsdr_get_tuner_clock(dev)) { case FC_XTAL_27_MHZ: case FC_XTAL_28_8_MHZ: reg[0x07] |= 0x20; break; case FC_XTAL_36_MHZ: default: break; } #endif reg[0x07] |= 0x20; /* if (priv->dual_master) */ reg[0x0c] |= 0x02; for (i = 1; i < sizeof(reg); i++) { ret = fc0012_writereg(dev, i, reg[i]); if (ret) break; } return ret; } int fc0012_set_params(void *dev, uint32_t freq, uint32_t bandwidth) { int i, ret = 0; uint8_t reg[7], am, pm, multi, tmp; uint64_t f_vco; uint32_t xtal_freq_div_2; uint16_t xin, xdiv; int vco_select = 0; xtal_freq_div_2 = rtlsdr_get_tuner_clock(dev) / 2; /* select frequency divider and the frequency of VCO */ if (freq < 37084000) { /* freq * 96 < 3560000000 */ multi = 96; reg[5] = 0x82; reg[6] = 0x00; } else if (freq < 55625000) { /* freq * 64 < 3560000000 */ multi = 64; reg[5] = 0x82; reg[6] = 0x02; } else if (freq < 74167000) { /* freq * 48 < 3560000000 */ multi = 48; reg[5] = 0x42; reg[6] = 0x00; } else if (freq < 111250000) { /* freq * 32 < 3560000000 */ multi = 32; reg[5] = 0x42; reg[6] = 0x02; } else if (freq < 148334000) { /* freq * 24 < 3560000000 */ multi = 24; reg[5] = 0x22; reg[6] = 0x00; } else if (freq < 222500000) { /* freq * 16 < 3560000000 */ multi = 16; reg[5] = 0x22; reg[6] = 0x02; } else if (freq < 296667000) { /* freq * 12 < 3560000000 */ multi = 12; reg[5] = 0x12; reg[6] = 0x00; } else if (freq < 445000000) { /* freq * 8 < 3560000000 */ multi = 8; reg[5] = 0x12; reg[6] = 0x02; } else if (freq < 593334000) { /* freq * 6 < 3560000000 */ multi = 6; reg[5] = 0x0a; reg[6] = 0x00; } else { multi = 4; reg[5] = 0x0a; reg[6] = 0x02; } f_vco = freq * multi; if (f_vco >= 3060000000U) { reg[6] |= 0x08; vco_select = 1; } /* From divided value (XDIV) determined the FA and FP value */ xdiv = (uint16_t)(f_vco / xtal_freq_div_2); if ((f_vco - xdiv * xtal_freq_div_2) >= (xtal_freq_div_2 / 2)) xdiv++; pm = (uint8_t)(xdiv / 8); am = (uint8_t)(xdiv - (8 * pm)); if (am < 2) { am += 8; pm--; } if (pm > 31) { reg[1] = am + (8 * (pm - 31)); reg[2] = 31; } else { reg[1] = am; reg[2] = pm; } if ((reg[1] > 15) || (reg[2] < 0x0b)) { #if PRINT_PLL_ERRORS fprintf(stderr, "[FC0012] no valid PLL combination " "found for %u Hz!\n", freq); #endif return -1; } /* fix clock out */ reg[6] |= 0x20; /* From VCO frequency determines the XIN ( fractional part of Delta Sigma PLL) and divided value (XDIV) */ xin = (uint16_t)((f_vco - (f_vco / xtal_freq_div_2) * xtal_freq_div_2) / 1000); xin = (xin << 15) / (xtal_freq_div_2 / 1000); if (xin >= 16384) xin += 32768; reg[3] = xin >> 8; /* xin with 9 bit resolution */ reg[4] = xin & 0xff; reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ switch (bandwidth) { case 6000000: reg[6] |= 0x80; break; case 7000000: reg[6] |= 0x40; break; case 8000000: default: break; } /* modified for Realtek demod */ reg[5] |= 0x07; for (i = 1; i <= 6; i++) { ret = fc0012_writereg(dev, i, reg[i]); if (ret) goto exit; } /* VCO Calibration */ ret = fc0012_writereg(dev, 0x0e, 0x80); if (!ret) ret = fc0012_writereg(dev, 0x0e, 0x00); /* VCO Re-Calibration if needed */ if (!ret) ret = fc0012_writereg(dev, 0x0e, 0x00); if (!ret) { /* msleep(10); */ ret = fc0012_readreg(dev, 0x0e, &tmp); } if (ret) goto exit; /* vco selection */ tmp &= 0x3f; if (vco_select) { if (tmp > 0x3c) { reg[6] &= ~0x08; ret = fc0012_writereg(dev, 0x06, reg[6]); if (!ret) ret = fc0012_writereg(dev, 0x0e, 0x80); if (!ret) ret = fc0012_writereg(dev, 0x0e, 0x00); } } else { if (tmp < 0x02) { reg[6] |= 0x08; ret = fc0012_writereg(dev, 0x06, reg[6]); if (!ret) ret = fc0012_writereg(dev, 0x0e, 0x80); if (!ret) ret = fc0012_writereg(dev, 0x0e, 0x00); } } exit: return ret; } int fc0012_set_gain(void *dev, int gain) { int ret; uint8_t tmp = 0; ret = fc0012_readreg(dev, 0x13, &tmp); /* mask bits off */ tmp &= 0xe0; switch (gain) { case -99: /* -9.9 dB */ tmp |= 0x02; break; case -40: /* -4 dB */ break; case 71: tmp |= 0x08; /* 7.1 dB */ break; case 179: tmp |= 0x17; /* 17.9 dB */ break; case 192: default: tmp |= 0x10; /* 19.2 dB */ break; } ret = fc0012_writereg(dev, 0x13, tmp); /* print_registers(dev); */ return ret; } ================================================ FILE: src/tuner_fc0013.c ================================================ /* * Fitipower FC0013 tuner driver * * Copyright (C) 2012 Hans-Frieder Vogt * partially based on driver code from Fitipower * Copyright (C) 2010 Fitipower Integrated Technology Inc * * modified for use in librtlsdr * Copyright (C) 2012 Steve Markgraf * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include #include #include "rtlsdr_i2c.h" #include "tuner_fc0013.h" #define PRINT_PLL_ERRORS 0 static int fc0013_writereg(void *dev, uint8_t reg, uint8_t val) { uint8_t data[2]; data[0] = reg; data[1] = val; if (rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, data, 2) < 0) return -1; return 0; } static int fc0013_readreg(void *dev, uint8_t reg, uint8_t *val) { uint8_t data = reg; if (rtlsdr_i2c_write_fn(dev, FC0013_I2C_ADDR, &data, 1) < 0) return -1; if (rtlsdr_i2c_read_fn(dev, FC0013_I2C_ADDR, &data, 1) < 0) return -1; *val = data; return 0; } int fc0013_init(void *dev) { int ret = 0; unsigned int i; uint8_t reg[] = { 0x00, /* reg. 0x00: dummy */ 0x09, /* reg. 0x01 */ 0x16, /* reg. 0x02 */ 0x00, /* reg. 0x03 */ 0x00, /* reg. 0x04 */ 0x17, /* reg. 0x05 */ 0x02, /* reg. 0x06: LPF bandwidth */ 0x0a, /* reg. 0x07: CHECK */ 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, Loop Bw 1/8 */ 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */ 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ 0x82, /* reg. 0x0b: CHECK */ 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ 0x01, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */ 0x00, /* reg. 0x0e */ 0x00, /* reg. 0x0f */ 0x00, /* reg. 0x10 */ 0x00, /* reg. 0x11 */ 0x00, /* reg. 0x12 */ 0x00, /* reg. 0x13 */ 0x50, /* reg. 0x14: DVB-t High Gain, UHF. Middle Gain: 0x48, Low Gain: 0x40 */ 0x01, /* reg. 0x15 */ }; #if 0 switch (rtlsdr_get_tuner_clock(dev)) { case FC_XTAL_27_MHZ: case FC_XTAL_28_8_MHZ: reg[0x07] |= 0x20; break; case FC_XTAL_36_MHZ: default: break; } #endif reg[0x07] |= 0x20; // if (dev->dual_master) reg[0x0c] |= 0x02; for (i = 1; i < sizeof(reg); i++) { ret = fc0013_writereg(dev, i, reg[i]); if (ret < 0) break; } return ret; } int fc0013_rc_cal_add(void *dev, int rc_val) { int ret; uint8_t rc_cal; int val; /* push rc_cal value, get rc_cal value */ ret = fc0013_writereg(dev, 0x10, 0x00); if (ret) goto error_out; /* get rc_cal value */ ret = fc0013_readreg(dev, 0x10, &rc_cal); if (ret) goto error_out; rc_cal &= 0x0f; val = (int)rc_cal + rc_val; /* forcing rc_cal */ ret = fc0013_writereg(dev, 0x0d, 0x11); if (ret) goto error_out; /* modify rc_cal value */ if (val > 15) ret = fc0013_writereg(dev, 0x10, 0x0f); else if (val < 0) ret = fc0013_writereg(dev, 0x10, 0x00); else ret = fc0013_writereg(dev, 0x10, (uint8_t)val); error_out: return ret; } int fc0013_rc_cal_reset(void *dev) { int ret; ret = fc0013_writereg(dev, 0x0d, 0x01); if (!ret) ret = fc0013_writereg(dev, 0x10, 0x00); return ret; } static int fc0013_set_vhf_track(void *dev, uint32_t freq) { int ret; uint8_t tmp; ret = fc0013_readreg(dev, 0x1d, &tmp); if (ret) goto error_out; tmp &= 0xe3; if (freq <= 177500000) { /* VHF Track: 7 */ ret = fc0013_writereg(dev, 0x1d, tmp | 0x1c); } else if (freq <= 184500000) { /* VHF Track: 6 */ ret = fc0013_writereg(dev, 0x1d, tmp | 0x18); } else if (freq <= 191500000) { /* VHF Track: 5 */ ret = fc0013_writereg(dev, 0x1d, tmp | 0x14); } else if (freq <= 198500000) { /* VHF Track: 4 */ ret = fc0013_writereg(dev, 0x1d, tmp | 0x10); } else if (freq <= 205500000) { /* VHF Track: 3 */ ret = fc0013_writereg(dev, 0x1d, tmp | 0x0c); } else if (freq <= 219500000) { /* VHF Track: 2 */ ret = fc0013_writereg(dev, 0x1d, tmp | 0x08); } else if (freq < 300000000) { /* VHF Track: 1 */ ret = fc0013_writereg(dev, 0x1d, tmp | 0x04); } else { /* UHF and GPS */ ret = fc0013_writereg(dev, 0x1d, tmp | 0x1c); } error_out: return ret; } int fc0013_set_params(void *dev, uint32_t freq, uint32_t bandwidth) { int i, ret = 0; uint8_t reg[7], am, pm, multi, tmp; uint64_t f_vco; uint32_t xtal_freq_div_2; uint16_t xin, xdiv; int vco_select = 0; xtal_freq_div_2 = rtlsdr_get_tuner_clock(dev) / 2; /* set VHF track */ ret = fc0013_set_vhf_track(dev, freq); if (ret) goto exit; if (freq < 300000000) { /* enable VHF filter */ ret = fc0013_readreg(dev, 0x07, &tmp); if (ret) goto exit; ret = fc0013_writereg(dev, 0x07, tmp | 0x10); if (ret) goto exit; /* disable UHF & disable GPS */ ret = fc0013_readreg(dev, 0x14, &tmp); if (ret) goto exit; ret = fc0013_writereg(dev, 0x14, tmp & 0x1f); if (ret) goto exit; } else if (freq <= 862000000) { /* disable VHF filter */ ret = fc0013_readreg(dev, 0x07, &tmp); if (ret) goto exit; ret = fc0013_writereg(dev, 0x07, tmp & 0xef); if (ret) goto exit; /* enable UHF & disable GPS */ ret = fc0013_readreg(dev, 0x14, &tmp); if (ret) goto exit; ret = fc0013_writereg(dev, 0x14, (tmp & 0x1f) | 0x40); if (ret) goto exit; } else { /* disable VHF filter */ ret = fc0013_readreg(dev, 0x07, &tmp); if (ret) goto exit; ret = fc0013_writereg(dev, 0x07, tmp & 0xef); if (ret) goto exit; /* disable UHF & enable GPS */ ret = fc0013_readreg(dev, 0x14, &tmp); if (ret) goto exit; ret = fc0013_writereg(dev, 0x14, (tmp & 0x1f) | 0x20); if (ret) goto exit; } /* select frequency divider and the frequency of VCO */ if (freq < 37084000) { /* freq * 96 < 3560000000 */ multi = 96; reg[5] = 0x82; reg[6] = 0x00; } else if (freq < 55625000) { /* freq * 64 < 3560000000 */ multi = 64; reg[5] = 0x02; reg[6] = 0x02; } else if (freq < 74167000) { /* freq * 48 < 3560000000 */ multi = 48; reg[5] = 0x42; reg[6] = 0x00; } else if (freq < 111250000) { /* freq * 32 < 3560000000 */ multi = 32; reg[5] = 0x82; reg[6] = 0x02; } else if (freq < 148334000) { /* freq * 24 < 3560000000 */ multi = 24; reg[5] = 0x22; reg[6] = 0x00; } else if (freq < 222500000) { /* freq * 16 < 3560000000 */ multi = 16; reg[5] = 0x42; reg[6] = 0x02; } else if (freq < 296667000) { /* freq * 12 < 3560000000 */ multi = 12; reg[5] = 0x12; reg[6] = 0x00; } else if (freq < 445000000) { /* freq * 8 < 3560000000 */ multi = 8; reg[5] = 0x22; reg[6] = 0x02; } else if (freq < 593334000) { /* freq * 6 < 3560000000 */ multi = 6; reg[5] = 0x0a; reg[6] = 0x00; } else if (freq < 950000000) { /* freq * 4 < 3800000000 */ multi = 4; reg[5] = 0x12; reg[6] = 0x02; } else { multi = 2; reg[5] = 0x0a; reg[6] = 0x02; } f_vco = freq * multi; if (f_vco >= 3060000000U) { reg[6] |= 0x08; vco_select = 1; } /* From divided value (XDIV) determined the FA and FP value */ xdiv = (uint16_t)(f_vco / xtal_freq_div_2); if ((f_vco - xdiv * xtal_freq_div_2) >= (xtal_freq_div_2 / 2)) xdiv++; pm = (uint8_t)(xdiv / 8); am = (uint8_t)(xdiv - (8 * pm)); if (am < 2) { am += 8; pm--; } if (pm > 31) { reg[1] = am + (8 * (pm - 31)); reg[2] = 31; } else { reg[1] = am; reg[2] = pm; } if ((reg[1] > 15) || (reg[2] < 0x0b)) { #if PRINT_PLL_ERRORS fprintf(stderr, "[FC0013] no valid PLL combination " "found for %u Hz!\n", freq); #endif return -1; } /* fix clock out */ reg[6] |= 0x20; /* From VCO frequency determines the XIN ( fractional part of Delta Sigma PLL) and divided value (XDIV) */ xin = (uint16_t)((f_vco - (f_vco / xtal_freq_div_2) * xtal_freq_div_2) / 1000); xin = (xin << 15) / (xtal_freq_div_2 / 1000); if (xin >= 16384) xin += 32768; reg[3] = xin >> 8; reg[4] = xin & 0xff; reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ switch (bandwidth) { case 6000000: reg[6] |= 0x80; break; case 7000000: reg[6] |= 0x40; break; case 8000000: default: break; } /* modified for Realtek demod */ reg[5] |= 0x07; for (i = 1; i <= 6; i++) { ret = fc0013_writereg(dev, i, reg[i]); if (ret) goto exit; } ret = fc0013_readreg(dev, 0x11, &tmp); if (ret) goto exit; if (multi == 64) ret = fc0013_writereg(dev, 0x11, tmp | 0x04); else ret = fc0013_writereg(dev, 0x11, tmp & 0xfb); if (ret) goto exit; /* VCO Calibration */ ret = fc0013_writereg(dev, 0x0e, 0x80); if (!ret) ret = fc0013_writereg(dev, 0x0e, 0x00); /* VCO Re-Calibration if needed */ if (!ret) ret = fc0013_writereg(dev, 0x0e, 0x00); if (!ret) { // msleep(10); ret = fc0013_readreg(dev, 0x0e, &tmp); } if (ret) goto exit; /* vco selection */ tmp &= 0x3f; if (vco_select) { if (tmp > 0x3c) { reg[6] &= ~0x08; ret = fc0013_writereg(dev, 0x06, reg[6]); if (!ret) ret = fc0013_writereg(dev, 0x0e, 0x80); if (!ret) ret = fc0013_writereg(dev, 0x0e, 0x00); } } else { if (tmp < 0x02) { reg[6] |= 0x08; ret = fc0013_writereg(dev, 0x06, reg[6]); if (!ret) ret = fc0013_writereg(dev, 0x0e, 0x80); if (!ret) ret = fc0013_writereg(dev, 0x0e, 0x00); } } exit: return ret; } int fc0013_set_gain_mode(void *dev, int manual) { int ret = 0; uint8_t tmp = 0; ret |= fc0013_readreg(dev, 0x0d, &tmp); if (manual) tmp |= (1 << 3); else tmp &= ~(1 << 3); ret |= fc0013_writereg(dev, 0x0d, tmp); /* set a fixed IF-gain for now */ ret |= fc0013_writereg(dev, 0x13, 0x0a); return ret; } int fc0013_lna_gains[] ={ -99, 0x02, -73, 0x03, -65, 0x05, -63, 0x04, -63, 0x00, -60, 0x07, -58, 0x01, -54, 0x06, 58, 0x0f, 61, 0x0e, 63, 0x0d, 65, 0x0c, 67, 0x0b, 68, 0x0a, 70, 0x09, 71, 0x08, 179, 0x17, 181, 0x16, 182, 0x15, 184, 0x14, 186, 0x13, 188, 0x12, 191, 0x11, 197, 0x10 }; #define GAIN_CNT (sizeof(fc0013_lna_gains) / sizeof(int) / 2) int fc0013_set_lna_gain(void *dev, int gain) { int ret = 0; unsigned int i; uint8_t tmp = 0; ret |= fc0013_readreg(dev, 0x14, &tmp); /* mask bits off */ tmp &= 0xe0; for (i = 0; i < GAIN_CNT; i++) { if ((fc0013_lna_gains[i*2] >= gain) || (i+1 == GAIN_CNT)) { tmp |= fc0013_lna_gains[i*2 + 1]; break; } } /* set gain */ ret |= fc0013_writereg(dev, 0x14, tmp); return ret; } ================================================ FILE: src/tuner_fc2580.c ================================================ /* * FCI FC2580 tuner driver, taken from the kernel driver that can be found * on http://linux.terratec.de/tv_en.html * * This driver is a mess, and should be cleaned up/rewritten. * */ #include #include "rtlsdr_i2c.h" #include "tuner_fc2580.h" /* 16.384 MHz (at least on the Logilink VG0002A) */ #define CRYSTAL_FREQ 16384000 /* glue functions to rtl-sdr code */ fc2580_fci_result_type fc2580_i2c_write(void *pTuner, unsigned char reg, unsigned char val) { uint8_t data[2]; data[0] = reg; data[1] = val; if (rtlsdr_i2c_write_fn(pTuner, FC2580_I2C_ADDR, data, 2) < 0) return FC2580_FCI_FAIL; return FC2580_FCI_SUCCESS; } fc2580_fci_result_type fc2580_i2c_read(void *pTuner, unsigned char reg, unsigned char *read_data) { uint8_t data = reg; if (rtlsdr_i2c_write_fn(pTuner, FC2580_I2C_ADDR, &data, 1) < 0) return FC2580_FCI_FAIL; if (rtlsdr_i2c_read_fn(pTuner, FC2580_I2C_ADDR, &data, 1) < 0) return FC2580_FCI_FAIL; *read_data = data; return FC2580_FCI_SUCCESS; } int fc2580_Initialize(void *pTuner) { int AgcMode; unsigned int CrystalFreqKhz; //TODO set AGC mode AgcMode = FC2580_AGC_EXTERNAL; // Initialize tuner with AGC mode. // Note: CrystalFreqKhz = round(CrystalFreqHz / 1000) CrystalFreqKhz = (unsigned int)((CRYSTAL_FREQ + 500) / 1000); if(fc2580_set_init(pTuner, AgcMode, CrystalFreqKhz) != FC2580_FCI_SUCCESS) goto error_status_initialize_tuner; return FUNCTION_SUCCESS; error_status_initialize_tuner: return FUNCTION_ERROR; } int fc2580_SetRfFreqHz(void *pTuner, unsigned long RfFreqHz) { unsigned int RfFreqKhz; unsigned int CrystalFreqKhz; // Set tuner RF frequency in KHz. // Note: RfFreqKhz = round(RfFreqHz / 1000) // CrystalFreqKhz = round(CrystalFreqHz / 1000) RfFreqKhz = (unsigned int)((RfFreqHz + 500) / 1000); CrystalFreqKhz = (unsigned int)((CRYSTAL_FREQ + 500) / 1000); if(fc2580_set_freq(pTuner, RfFreqKhz, CrystalFreqKhz) != FC2580_FCI_SUCCESS) goto error_status_set_tuner_rf_frequency; return FUNCTION_SUCCESS; error_status_set_tuner_rf_frequency: return FUNCTION_ERROR; } /** @brief Set FC2580 tuner bandwidth mode. */ int fc2580_SetBandwidthMode(void *pTuner, int BandwidthMode) { unsigned int CrystalFreqKhz; // Set tuner bandwidth mode. // Note: CrystalFreqKhz = round(CrystalFreqHz / 1000) CrystalFreqKhz = (unsigned int)((CRYSTAL_FREQ + 500) / 1000); if(fc2580_set_filter(pTuner, (unsigned char)BandwidthMode, CrystalFreqKhz) != FC2580_FCI_SUCCESS) goto error_status_set_tuner_bandwidth_mode; return FUNCTION_SUCCESS; error_status_set_tuner_bandwidth_mode: return FUNCTION_ERROR; } void fc2580_wait_msec(void *pTuner, int a) { /* USB latency is enough for now ;) */ // usleep(a * 1000); return; } /*============================================================================== fc2580 initial setting This function is a generic function which gets called to initialize fc2580 in DVB-H mode or L-Band TDMB mode ifagc_mode type : integer 1 : Internal AGC 2 : Voltage Control Mode ==============================================================================*/ fc2580_fci_result_type fc2580_set_init(void *pTuner, int ifagc_mode, unsigned int freq_xtal) { fc2580_fci_result_type result = FC2580_FCI_SUCCESS; result &= fc2580_i2c_write(pTuner, 0x00, 0x00); /*** Confidential ***/ result &= fc2580_i2c_write(pTuner, 0x12, 0x86); result &= fc2580_i2c_write(pTuner, 0x14, 0x5C); result &= fc2580_i2c_write(pTuner, 0x16, 0x3C); result &= fc2580_i2c_write(pTuner, 0x1F, 0xD2); result &= fc2580_i2c_write(pTuner, 0x09, 0xD7); result &= fc2580_i2c_write(pTuner, 0x0B, 0xD5); result &= fc2580_i2c_write(pTuner, 0x0C, 0x32); result &= fc2580_i2c_write(pTuner, 0x0E, 0x43); result &= fc2580_i2c_write(pTuner, 0x21, 0x0A); result &= fc2580_i2c_write(pTuner, 0x22, 0x82); if( ifagc_mode == 1 ) { result &= fc2580_i2c_write(pTuner, 0x45, 0x10); //internal AGC result &= fc2580_i2c_write(pTuner, 0x4C, 0x00); //HOLD_AGC polarity } else if( ifagc_mode == 2 ) { result &= fc2580_i2c_write(pTuner, 0x45, 0x20); //Voltage Control Mode result &= fc2580_i2c_write(pTuner, 0x4C, 0x02); //HOLD_AGC polarity } result &= fc2580_i2c_write(pTuner, 0x3F, 0x88); result &= fc2580_i2c_write(pTuner, 0x02, 0x0E); result &= fc2580_i2c_write(pTuner, 0x58, 0x14); result &= fc2580_set_filter(pTuner, 8, freq_xtal); //BW = 7.8MHz return result; } /*============================================================================== fc2580 frequency setting This function is a generic function which gets called to change LO Frequency of fc2580 in DVB-H mode or L-Band TDMB mode freq_xtal: kHz f_lo Value of target LO Frequency in 'kHz' unit ex) 2.6GHz = 2600000 ==============================================================================*/ fc2580_fci_result_type fc2580_set_freq(void *pTuner, unsigned int f_lo, unsigned int freq_xtal) { unsigned int f_diff, f_diff_shifted, n_val, k_val; unsigned int f_vco, r_val, f_comp; unsigned char pre_shift_bits = 4;// number of preshift to prevent overflow in shifting f_diff to f_diff_shifted unsigned char data_0x18; unsigned char data_0x02 = (USE_EXT_CLK<<5)|0x0E; fc2580_band_type band = ( f_lo > 1000000 )? FC2580_L_BAND : ( f_lo > 400000 )? FC2580_UHF_BAND : FC2580_VHF_BAND; fc2580_fci_result_type result = FC2580_FCI_SUCCESS; f_vco = ( band == FC2580_UHF_BAND )? f_lo * 4 : (( band == FC2580_L_BAND )? f_lo * 2 : f_lo * 12); r_val = ( f_vco >= 2*76*freq_xtal )? 1 : ( f_vco >= 76*freq_xtal )? 2 : 4; f_comp = freq_xtal/r_val; n_val = ( f_vco / 2 ) / f_comp; f_diff = f_vco - 2* f_comp * n_val; f_diff_shifted = f_diff << ( 20 - pre_shift_bits ); k_val = f_diff_shifted / ( ( 2* f_comp ) >> pre_shift_bits ); if( f_diff_shifted - k_val * ( ( 2* f_comp ) >> pre_shift_bits ) >= ( f_comp >> pre_shift_bits ) ) k_val = k_val + 1; if( f_vco >= BORDER_FREQ ) //Select VCO Band data_0x02 = data_0x02 | 0x08; //0x02[3] = 1; else data_0x02 = data_0x02 & 0xF7; //0x02[3] = 0; // if( band != curr_band ) { switch(band) { case FC2580_UHF_BAND: data_0x02 = (data_0x02 & 0x3F); result &= fc2580_i2c_write(pTuner, 0x25, 0xF0); result &= fc2580_i2c_write(pTuner, 0x27, 0x77); result &= fc2580_i2c_write(pTuner, 0x28, 0x53); result &= fc2580_i2c_write(pTuner, 0x29, 0x60); result &= fc2580_i2c_write(pTuner, 0x30, 0x09); result &= fc2580_i2c_write(pTuner, 0x50, 0x8C); result &= fc2580_i2c_write(pTuner, 0x53, 0x50); if( f_lo < 538000 ) result &= fc2580_i2c_write(pTuner, 0x5F, 0x13); else result &= fc2580_i2c_write(pTuner, 0x5F, 0x15); if( f_lo < 538000 ) { result &= fc2580_i2c_write(pTuner, 0x61, 0x07); result &= fc2580_i2c_write(pTuner, 0x62, 0x06); result &= fc2580_i2c_write(pTuner, 0x67, 0x06); result &= fc2580_i2c_write(pTuner, 0x68, 0x08); result &= fc2580_i2c_write(pTuner, 0x69, 0x10); result &= fc2580_i2c_write(pTuner, 0x6A, 0x12); } else if( f_lo < 794000 ) { result &= fc2580_i2c_write(pTuner, 0x61, 0x03); result &= fc2580_i2c_write(pTuner, 0x62, 0x03); result &= fc2580_i2c_write(pTuner, 0x67, 0x03); //ACI improve result &= fc2580_i2c_write(pTuner, 0x68, 0x05); //ACI improve result &= fc2580_i2c_write(pTuner, 0x69, 0x0C); result &= fc2580_i2c_write(pTuner, 0x6A, 0x0E); } else { result &= fc2580_i2c_write(pTuner, 0x61, 0x07); result &= fc2580_i2c_write(pTuner, 0x62, 0x06); result &= fc2580_i2c_write(pTuner, 0x67, 0x07); result &= fc2580_i2c_write(pTuner, 0x68, 0x09); result &= fc2580_i2c_write(pTuner, 0x69, 0x10); result &= fc2580_i2c_write(pTuner, 0x6A, 0x12); } result &= fc2580_i2c_write(pTuner, 0x63, 0x15); result &= fc2580_i2c_write(pTuner, 0x6B, 0x0B); result &= fc2580_i2c_write(pTuner, 0x6C, 0x0C); result &= fc2580_i2c_write(pTuner, 0x6D, 0x78); result &= fc2580_i2c_write(pTuner, 0x6E, 0x32); result &= fc2580_i2c_write(pTuner, 0x6F, 0x14); result &= fc2580_set_filter(pTuner, 8, freq_xtal); //BW = 7.8MHz break; case FC2580_VHF_BAND: data_0x02 = (data_0x02 & 0x3F) | 0x80; result &= fc2580_i2c_write(pTuner, 0x27, 0x77); result &= fc2580_i2c_write(pTuner, 0x28, 0x33); result &= fc2580_i2c_write(pTuner, 0x29, 0x40); result &= fc2580_i2c_write(pTuner, 0x30, 0x09); result &= fc2580_i2c_write(pTuner, 0x50, 0x8C); result &= fc2580_i2c_write(pTuner, 0x53, 0x50); result &= fc2580_i2c_write(pTuner, 0x5F, 0x0F); result &= fc2580_i2c_write(pTuner, 0x61, 0x07); result &= fc2580_i2c_write(pTuner, 0x62, 0x00); result &= fc2580_i2c_write(pTuner, 0x63, 0x15); result &= fc2580_i2c_write(pTuner, 0x67, 0x03); result &= fc2580_i2c_write(pTuner, 0x68, 0x05); result &= fc2580_i2c_write(pTuner, 0x69, 0x10); result &= fc2580_i2c_write(pTuner, 0x6A, 0x12); result &= fc2580_i2c_write(pTuner, 0x6B, 0x08); result &= fc2580_i2c_write(pTuner, 0x6C, 0x0A); result &= fc2580_i2c_write(pTuner, 0x6D, 0x78); result &= fc2580_i2c_write(pTuner, 0x6E, 0x32); result &= fc2580_i2c_write(pTuner, 0x6F, 0x54); result &= fc2580_set_filter(pTuner, 7, freq_xtal); //BW = 6.8MHz break; case FC2580_L_BAND: data_0x02 = (data_0x02 & 0x3F) | 0x40; result &= fc2580_i2c_write(pTuner, 0x2B, 0x70); result &= fc2580_i2c_write(pTuner, 0x2C, 0x37); result &= fc2580_i2c_write(pTuner, 0x2D, 0xE7); result &= fc2580_i2c_write(pTuner, 0x30, 0x09); result &= fc2580_i2c_write(pTuner, 0x44, 0x20); result &= fc2580_i2c_write(pTuner, 0x50, 0x8C); result &= fc2580_i2c_write(pTuner, 0x53, 0x50); result &= fc2580_i2c_write(pTuner, 0x5F, 0x0F); result &= fc2580_i2c_write(pTuner, 0x61, 0x0F); result &= fc2580_i2c_write(pTuner, 0x62, 0x00); result &= fc2580_i2c_write(pTuner, 0x63, 0x13); result &= fc2580_i2c_write(pTuner, 0x67, 0x00); result &= fc2580_i2c_write(pTuner, 0x68, 0x02); result &= fc2580_i2c_write(pTuner, 0x69, 0x0C); result &= fc2580_i2c_write(pTuner, 0x6A, 0x0E); result &= fc2580_i2c_write(pTuner, 0x6B, 0x08); result &= fc2580_i2c_write(pTuner, 0x6C, 0x0A); result &= fc2580_i2c_write(pTuner, 0x6D, 0xA0); result &= fc2580_i2c_write(pTuner, 0x6E, 0x50); result &= fc2580_i2c_write(pTuner, 0x6F, 0x14); result &= fc2580_set_filter(pTuner, 1, freq_xtal); //BW = 1.53MHz break; default: break; } // curr_band = band; // } //A command about AGC clock's pre-divide ratio if( freq_xtal >= 28000 ) result &= fc2580_i2c_write(pTuner, 0x4B, 0x22 ); //Commands about VCO Band and PLL setting. result &= fc2580_i2c_write(pTuner, 0x02, data_0x02); data_0x18 = ( ( r_val == 1 )? 0x00 : ( ( r_val == 2 )? 0x10 : 0x20 ) ) + (unsigned char)(k_val >> 16); result &= fc2580_i2c_write(pTuner, 0x18, data_0x18); //Load 'R' value and high part of 'K' values result &= fc2580_i2c_write(pTuner, 0x1A, (unsigned char)( k_val >> 8 ) ); //Load middle part of 'K' value result &= fc2580_i2c_write(pTuner, 0x1B, (unsigned char)( k_val ) ); //Load lower part of 'K' value result &= fc2580_i2c_write(pTuner, 0x1C, (unsigned char)( n_val ) ); //Load 'N' value //A command about UHF LNA Load Cap if( band == FC2580_UHF_BAND ) result &= fc2580_i2c_write(pTuner, 0x2D, ( f_lo <= (unsigned int)794000 )? 0x9F : 0x8F ); //LNA_OUT_CAP return result; } /*============================================================================== fc2580 filter BW setting This function is a generic function which gets called to change Bandwidth frequency of fc2580's channel selection filter freq_xtal: kHz filter_bw 1 : 1.53MHz(TDMB) 6 : 6MHz (Bandwidth 6MHz) 7 : 6.8MHz (Bandwidth 7MHz) 8 : 7.8MHz (Bandwidth 8MHz) ==============================================================================*/ fc2580_fci_result_type fc2580_set_filter(void *pTuner, unsigned char filter_bw, unsigned int freq_xtal) { unsigned char cal_mon = 0, i; fc2580_fci_result_type result = FC2580_FCI_SUCCESS; if(filter_bw == 1) { result &= fc2580_i2c_write(pTuner, 0x36, 0x1C); result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(4151*freq_xtal/1000000) ); result &= fc2580_i2c_write(pTuner, 0x39, 0x00); result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); } if(filter_bw == 6) { result &= fc2580_i2c_write(pTuner, 0x36, 0x18); result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(4400*freq_xtal/1000000) ); result &= fc2580_i2c_write(pTuner, 0x39, 0x00); result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); } else if(filter_bw == 7) { result &= fc2580_i2c_write(pTuner, 0x36, 0x18); result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(3910*freq_xtal/1000000) ); result &= fc2580_i2c_write(pTuner, 0x39, 0x80); result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); } else if(filter_bw == 8) { result &= fc2580_i2c_write(pTuner, 0x36, 0x18); result &= fc2580_i2c_write(pTuner, 0x37, (unsigned char)(3300*freq_xtal/1000000) ); result &= fc2580_i2c_write(pTuner, 0x39, 0x80); result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); } for(i=0; i<5; i++) { fc2580_wait_msec(pTuner, 5);//wait 5ms result &= fc2580_i2c_read(pTuner, 0x2F, &cal_mon); if( (cal_mon & 0xC0) != 0xC0) { result &= fc2580_i2c_write(pTuner, 0x2E, 0x01); result &= fc2580_i2c_write(pTuner, 0x2E, 0x09); } else break; } result &= fc2580_i2c_write(pTuner, 0x2E, 0x01); return result; } /*============================================================================== fc2580 RSSI function This function is a generic function which returns fc2580's current RSSI value. none int rssi : estimated input power. ==============================================================================*/ //int fc2580_get_rssi(void) { // // unsigned char s_lna, s_rfvga, s_cfs, s_ifvga; // int ofs_lna, ofs_rfvga, ofs_csf, ofs_ifvga, rssi; // // fc2580_i2c_read(0x71, &s_lna ); // fc2580_i2c_read(0x72, &s_rfvga ); // fc2580_i2c_read(0x73, &s_cfs ); // fc2580_i2c_read(0x74, &s_ifvga ); // // // ofs_lna = // (curr_band==FC2580_UHF_BAND)? // (s_lna==0)? 0 : // (s_lna==1)? -6 : // (s_lna==2)? -17 : // (s_lna==3)? -22 : -30 : // (curr_band==FC2580_VHF_BAND)? // (s_lna==0)? 0 : // (s_lna==1)? -6 : // (s_lna==2)? -19 : // (s_lna==3)? -24 : -32 : // (curr_band==FC2580_L_BAND)? // (s_lna==0)? 0 : // (s_lna==1)? -6 : // (s_lna==2)? -11 : // (s_lna==3)? -16 : -34 : // 0;//FC2580_NO_BAND // ofs_rfvga = -s_rfvga+((s_rfvga>=11)? 1 : 0) + ((s_rfvga>=18)? 1 : 0); // ofs_csf = -6*s_cfs; // ofs_ifvga = s_ifvga/4; // // return rssi = ofs_lna+ofs_rfvga+ofs_csf+ofs_ifvga+OFS_RSSI; // //} /*============================================================================== fc2580 Xtal frequency Setting This function is a generic function which sets the frequency of xtal. frequency frequency value of internal(external) Xtal(clock) in kHz unit. ==============================================================================*/ //void fc2580_set_freq_xtal(unsigned int frequency) { // // freq_xtal = frequency; // //} ================================================ FILE: src/tuner_r82xx.c ================================================ /* * Rafael Micro R820T/R828D driver * * Copyright (C) 2013 Mauro Carvalho Chehab * Copyright (C) 2013 Steve Markgraf * * This driver is a heavily modified version of the driver found in the * Linux kernel: * http://git.linuxtv.org/linux-2.6.git/history/HEAD:/drivers/media/tuners/r820t.c * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include "rtlsdr_i2c.h" #include "tuner_r82xx.h" #define WITH_ASYM_FILTER 0 #define PRINT_PLL_ERRORS 0 #define PRINT_VGA_REG 0 #define PRINT_INITIAL_REGISTERS 0 #define PRINT_ACTUAL_VCO_AND_ERR 0 /* use fifth harmonic above this frequency in kHz, when PLL does NOT lock */ #define FIFTH_HARM_FRQ_THRESH_KHZ 1770000 #define RETRY_WITH_FIFTH_HARM_KHZ 1760000 #define DEFAULT_HARMONIC 5 #define PRINT_HARMONICS 0 /* #define VGA_FOR_AGC_MODE 16 */ #define DEFAULT_IF_VGA_VAL 11 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) #define MHZ(x) ((x)*1000*1000) #define KHZ(x) ((x)*1000) #define HF 1 #define VHF 2 #define UHF 3 /* Register Description many updates from https://github.com/old-dab/rtlsdr Reg Bitmap Symbol Description ------------------------------------------------------------------------------------ R0 [7:0] CHIP_ID reference check point for read mode: 0x96 0x00 ------------------------------------------------------------------------------------ R1 [7:6] 10 0x01 [5:0] ADC Analog-Digital Converter for detector 3 ------------------------------------------------------------------------------------ R2 [7] 1 0x02 [6] VCO_INDICATOR 0: PLL has not locked, 1: PLL has locked [5:0] Analog-Digital Converter for VCO 000000: min (1.75 GHz), 111111: max (3.6 GHz) ------------------------------------------------------------------------------------ R3 [7:4] RF_INDICATOR Mixer gain 0x03 0: Lowest, 15: Highest [3:0] LNA gain 0: Lowest, 15: Highest ------------------------------------------------------------------------------------ R4 [5:4] vco_fine_tune 0x04 [3:0] fil_cal_code ------------------------------------------------------------------------------------ R5 [7] LOOP_THROUGH Loop through ON/OFF 0x05 0: on, 1: off [6:5] AIR_CABLE1_IN 0 (only R828D) [5] PWD_LNA1 LNA 1 power control 0:on, 1:off [4] LNA_GAIN_MODE LNA gain mode switch 0: auto, 1: manual [3:0] LNA_GAIN LNA manual gain control 15: max gain, 0: min gain ------------------------------------------------------------------------------------ R6 [7] PWD_PDET1 Power detector 1 on/off 0x06 0: on, 1: off [6] PWD_PDET3 Power detector 3 on/off 0: off, 1: on [5] FILT_GAIN Filter gain 3db 0:0db, 1:+3db [4] 1 [3] CABLE2_IN 0 (only R828D) [2:0] PW_LNA LNA power control 000: max, 111: min ------------------------------------------------------------------------------------ R7 [7] IMG_R Mixer Sideband 0x07 0: lower, 1: upper [6] PWD_MIX Mixer power 0:off, 1:on [5] PW0_MIX Mixer current control 0:max current, 1:normal current [4] MIXGAIN_MODE Mixer gain mode 0:manual mode, 1:auto mode [3:0] MIX_GAIN Mixer manual gain control 0000->min, 1111->max ------------------------------------------------------------------------------------ R8 [7] PWD_AMP Mixer buffer power on/off 0x08 0: off, 1:on [6] PW0_AMP Mixer buffer current setting 0: high current, 1: low current [5] 0: Q, 1: I [4:0] IMR_G Image Gain Adjustment 0: min, 31: max ------------------------------------------------------------------------------------ R9 [7] PWD_IFFILT IF Filter power on/off 0x09 0: filter on, 1: off [6] PW1_IFFILT IF Filter current 0: high current, 1: low current [5] 0: Q, 1: I [4:0] IMR_P Image Phase Adjustment 0: min, 31: max ------------------------------------------------------------------------------------ R10 [7] PWD_FILT Filter power on/off 0x0A 0: channel filter off, 1: on [6:5] PW_FILT Filter power control 00: highest power, 11: lowest power [4] FILT_Q 1 [3:0] FILT_CODE Filter bandwidth manual fine tune 0000 Widest, 1111 narrowest ------------------------------------------------------------------------------------ R11 [7:5] FILT_BW Filter bandwidth manual course tunnel 0x0B 000: widest 010 or 001: middle 111: narrowest [4] CAL_TRIGGER 0 [3:0] HP_COR High pass filter corner control 0000: highest 1111: lowest ------------------------------------------------------------------------------------ R12 [7] SW_ADC Switch Analog-Digital Converter for detector 3 (see R1) 0: on, 1: off 0x0C [6] PWD_VGA VGA power control 0: vga power off, 1: vga power on [5] 1 [4] VGA_MODE VGA GAIN manual / pin selector 1: IF vga gain controlled by vagc pin 0: IF vga gain controlled by vga_code[3:0] [3:0] VGA_CODE IF vga manual gain control 0000: -12.0 dB 1111: +40.5 dB; -3.5dB/step ------------------------------------------------------------------------------------ R13 [7:4] LNA_VTHH LNA agc power detector voltage threshold high setting 0x0D 1111: 1.94 V 0000: 0.34 V, ~0.1 V/step [3:0] LNA_VTHL LNA agc power detector voltage threshold low setting 1111: 1.94 V 0000: 0.34 V, ~0.1 V/step ------------------------------------------------------------------------------------ R14 [7:4] MIX_VTH_H MIXER agc power detector voltage threshold high setting 0x0E 1111: 1.94 V 0000: 0.34 V, ~0.1 V/step [3:0] MIX_VTH_L MIXER agc power detector voltage threshold low setting 1111: 1.94 V 0000: 0.34 V, ~0.1 V/step ------------------------------------------------------------------------------------ R15 [7] FLT_EXT_WIDEST filter extension widest 0x0F 0: off, 1: on [4] CLK_OUT_ENB Clock out pin control 0: clk output on, 1: off [3] ring clk 1: off, 0: on [2] set cali clk 0: off, 1: on [1] CLK_AGC_ENB AGC clk control 0: internal agc clock on, 1: off [0] GPIO 0 ------------------------------------------------------------------------------------ R16 [7:5] SEL_DIV PLL to Mixer divider number control 0x10 000: mixer in = vco out / 2 001: mixer in = vco out / 4 010: mixer in = vco out / 8 011: mixer in = vco out / 16 100: mixer in = vco out / 32 101: mixer in = vco out / 64 [4] REFDIV PLL Reference frequency Divider 0 -> fref=xtal_freq 1 -> fref=xta_freql / 2 (for Xtal >24MHz) [3] X'tal Drive 0: High, 1: Low [2] 1 [1:0] CAPX Internal xtal cap setting 00->no cap 01->10pF 10->20pF 11->30pF ------------------------------------------------------------------------------------ R17 [7:6] PW_LDO_A PLL analog low drop out regulator switch 0x11 00: off 01: 2.1V 10: 2.0V 11: 1.9V [5:3] CP_CUR cp_cur 101: 0.2, 111: auto [2:0] 011 ------------------------------------------------------------------------------------ R18 [7:5] set VCO current 0x12 [4] 0: enable dithering, 1: disable dithering [3] PW_SDM 0: Enable frac pll, 1: Disable frac pll [2:0] 000 ------------------------------------------------------------------------------------ R19 [7] 0 0x13 [6] VCO control mode 0: auto mode, VCO controlled by PLL 1: manual mode, VCO controlled by DAC code[5:0] [5:0] VCO_DAC DAC for VCO 000000: min (1.75 GHz), 111111: max (3.6 GHz) ------------------------------------------------------------------------------------ R20 [7:6] SI2C PLL integer divider number input Si2c 0x14 Nint=4*Ni2c+Si2c+13 PLL divider number Ndiv = (Nint + Nfra)*2 [5:0] NI2C PLL integer divider number input Ni2c ------------------------------------------------------------------------------------ R21 [7:0] SDM_IN[8:1] PLL fractional divider number input SDM[16:1] 0x15 Nfra=SDM_IN[16]*2^-1+SDM_IN[15]*2^-2+... R22 [7:0] SDM_IN[16:9] +SDM_IN[2]*2^-15+SDM_IN[1]*2^-16 0x16 ------------------------------------------------------------------------------------ R23 [7:6] PW_LDO_D PLL digital low drop out regulator supply current switch 0x17 00: 1.8V,8mA 01: 1.8V,4mA 10: 2.0V,8mA 11: OFF [5:4] DIV_BUF_CUR div_buf_cur 10: 200u, 11: 150u [3] OPEN_D Open drain 0: High-Z, 1: Low-Z [2:0] 100 ------------------------------------------------------------------------------------ R24 [7:6] 01 [5] ring_div[0] ring_div bit 0, see ring_div[2:1] in R25 0x18 [4] ring power 0: off, 1:on [3:0] n_ring ring_vco = (16+n_ring)*8*pll_ref, n_ring = 9...14 ------------------------------------------------------------------------------------ R25 [7] PWD_RFFILT RF Filter power 0x19 0: off, 1:on [6:5] POLYFIL_CUR RF poly filter current 00: min [4] SW_AGC Switch agc_pin 0:agc=agc_in 1:agc=agc_in2 [3:2] 11 [1:0] ring_div[2:1] cal_freq = ring_vco / divisor; see ring_div[0] in R24 000: ring_freq = ring_vco / 4 001: ring_freq = ring_vco / 6 010: ring_freq = ring_vco / 8 011: ring_freq = ring_vco / 12 100: ring_freq = ring_vco / 16 101: ring_freq = ring_vco / 24 110: ring_freq = ring_vco / 32 111: ring_freq = ring_vco / 48 ------------------------------------------------------------------------------------ R26 [7:6] RF_MUX_POLY Tracking Filter switch 0x1A 00: TF on 01: Bypass [5:4] AGC clk 00: 300ms, 01: 300ms, 10: 80ms, 11: 20ms [3:2] PLL_AUTO_CLK PLL auto tune clock rate 00: 128 kHz 01: 32 kHz 10: 8 kHz [1:0] RFFILT RF FILTER band selection 00: highest band 01: med band 10: low band ------------------------------------------------------------------------------------ R27 [7:4] TF_NCH 0000 highest corner for LPNF 0x1B 1111 lowest corner for LPNF [3:0] TF_LP 0000 highest corner for LPF 1111 lowest corner for LPF ------------------------------------------------------------------------------------ R28 [7:4] MIXER_TOP Power detector 3 (Mixer) TOP(take off point) control 0x1C 0: Highest, 15: Lowest [3] discharge mode 0: on [2] 1 [1] 1: from ring = ring pll in [0] 0 ------------------------------------------------------------------------------------ R29 [7:6] 11 0x1D [5:3] LNA_TOP Power detector 1 (LNA) TOP(take off point) control 0: Highest, 7: Lowest [2:0] PDET2_GAIN Power detector 2 TOP(take off point) control 0: Highest, 7: Lowest ------------------------------------------------------------------------------------ R30 [7] sw_pdect 0x1E 1: sw_pdect = det3 [6] FILTER_EXT Filter extension under weak signal 0: Disable, 1: Enable [5:0] PDET_CLK Power detector timing control (LNA discharge current) 111111: max, 000000: min ------------------------------------------------------------------------------------ R31 [7] LT_ATT Loop through attenuation 0x1F 0: Enable, 1: Disable [6:2] 10000 [1:0] pw_ring 0: -5dB, 1: 0dB, 2: -8dB, 3: -3dB ------------------------------------------------------------------------------------ R0...R4 read, R5...R15 read/write, R16..R31 write */ /* * Static constants */ /* Those initial values start from REG_SHADOW_START */ static const uint8_t r82xx_init_array[] = { 0x80, /* Reg 0x05 */ 0x13, /* Reg 0x06 */ 0x70, /* Reg 0x07 */ 0xc0, /* Reg 0x08 */ 0x40, /* Reg 0x09 */ 0xdb, /* Reg 0x0a */ 0x6b, /* Reg 0x0b */ /* Reg 0x0c: * for manual gain was: set fixed VGA gain for now (16.3 dB): 0x08 * with active agc was: set fixed VGA gain for now (26.5 dB): 0x0b */ 0xe0 | DEFAULT_IF_VGA_VAL, /* Reg 0x0c */ 0x53, /* Reg 0x0d */ 0x75, /* Reg 0x0e */ 0x68, /* Reg 0x0f */ 0x6c, /* Reg 0x10 */ 0xbb, /* Reg 0x11 */ 0x80, /* Reg 0x12 */ VER_NUM & 0x3f, /* Reg 0x13 */ 0x0f, /* Reg 0x14 */ 0x00, /* Reg 0x15 */ 0xc0, /* Reg 0x16 */ 0x30, /* Reg 0x17 */ 0x48, /* Reg 0x18 */ 0xec, /* Reg 0x19 */ 0x60, /* Reg 0x1a */ 0x00, /* Reg 0x1b */ 0x24, /* Reg 0x1c */ 0xdd, /* Reg 0x1d */ 0x0e, /* Reg 0x1e */ 0x40 /* Reg 0x1f */ }; /* Tuner frequency ranges */ static const struct r82xx_freq_range freq_ranges[] = { { /* .freq = */ 0, /* Start freq, in MHz */ /* .open_d = */ 0x08, /* low */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0xdf, /* R27[7:0] band2,band0 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 50, /* Start freq, in MHz */ /* .open_d = */ 0x08, /* low */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0xbe, /* R27[7:0] band4,band1 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 55, /* Start freq, in MHz */ /* .open_d = */ 0x08, /* low */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x8b, /* R27[7:0] band7,band4 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 60, /* Start freq, in MHz */ /* .open_d = */ 0x08, /* low */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x7b, /* R27[7:0] band8,band4 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 65, /* Start freq, in MHz */ /* .open_d = */ 0x08, /* low */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x69, /* R27[7:0] band9,band6 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 70, /* Start freq, in MHz */ /* .open_d = */ 0x08, /* low */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x58, /* R27[7:0] band10,band7 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 75, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x44, /* R27[7:0] band11,band11 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 80, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x44, /* R27[7:0] band11,band11 */ /* .xtal_cap20p = */ 0x02, /* R16[1:0] 20pF (10) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 90, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x34, /* R27[7:0] band12,band11 */ /* .xtal_cap20p = */ 0x01, /* R16[1:0] 10pF (01) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 100, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x34, /* R27[7:0] band12,band11 */ /* .xtal_cap20p = */ 0x01, /* R16[1:0] 10pF (01) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 110, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x24, /* R27[7:0] band13,band11 */ /* .xtal_cap20p = */ 0x01, /* R16[1:0] 10pF (01) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 120, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x24, /* R27[7:0] band13,band11 */ /* .xtal_cap20p = */ 0x01, /* R16[1:0] 10pF (01) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 140, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x14, /* R27[7:0] band14,band11 */ /* .xtal_cap20p = */ 0x01, /* R16[1:0] 10pF (01) */ /* .xtal_cap10p = */ 0x01, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 180, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x13, /* R27[7:0] band14,band12 */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 220, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x13, /* R27[7:0] band14,band12 */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 250, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x11, /* R27[7:0] highest,highest */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 280, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x02, /* R26[7:6]=0 (LPF) R26[1:0]=2 (low) */ /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 310, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 450, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x41, /* R26[7:6]=1 (bypass) R26[1:0]=1 (middle) */ /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 588, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, /* .xtal_cap0p = */ 0x00, }, { /* .freq = */ 650, /* Start freq, in MHz */ /* .open_d = */ 0x00, /* high */ /* .rf_mux_ploy = */ 0x40, /* R26[7:6]=1 (bypass) R26[1:0]=0 (highest) */ /* .tf_c = */ 0x00, /* R27[7:0] highest,highest */ /* .xtal_cap20p = */ 0x00, /* R16[1:0] 0pF (00) */ /* .xtal_cap10p = */ 0x00, /* .xtal_cap0p = */ 0x00, } }; /* * I2C read/write code and shadow registers logic */ static void shadow_store(struct r82xx_priv *priv, uint8_t reg, const uint8_t *val, int len) { int r = reg - REG_SHADOW_START; if (r < 0) { len += r; r = 0; } if (len <= 0) return; if (len > NUM_REGS - r) len = NUM_REGS - r; memcpy(&priv->regs[r], val, len); } static int r82xx_write_arr(struct r82xx_priv *priv, uint8_t reg, const uint8_t *val, unsigned int len) { int rc, size, k, regOff, regIdx, bufIdx, pos = 0; /* Store the shadow registers */ shadow_store(priv, reg, val, len); do { if (len > priv->cfg->max_i2c_msg_len - 1) size = priv->cfg->max_i2c_msg_len - 1; else size = len; /* Fill I2C buffer */ priv->buf[0] = reg; memcpy(&priv->buf[1], &val[pos], size); /* override data in buffer */ for ( k = 0; k < size; ++k ) { regOff = pos + k; regIdx = reg - REG_SHADOW_START + regOff; if ( priv->override_mask[regIdx] ) { uint8_t oldBuf = priv->buf[1 + k]; bufIdx = 1 + k; priv->buf[bufIdx] = ( priv->buf[bufIdx] & (~ priv->override_mask[regIdx]) ) | ( priv->override_mask[regIdx] & priv->override_data[regIdx] ); fprintf(stderr, "override writing register %d = x%02X value x%02X by data x%02X mask x%02X => new value x%02X\n" , regIdx + REG_SHADOW_START , regIdx + REG_SHADOW_START , oldBuf , priv->override_data[regIdx] , priv->override_mask[regIdx] , priv->buf[bufIdx] ); } } rc = rtlsdr_i2c_write_fn(priv->rtl_dev, priv->cfg->i2c_addr, priv->buf, size + 1); if (rc != size + 1) { fprintf(stderr, "%s: i2c wr failed=%d reg=%02x len=%d\n", __FUNCTION__, rc, reg, size); if (rc < 0) return rc; return -1; } reg += size; len -= size; pos += size; } while (len > 0); return 0; } static int r82xx_write_reg(struct r82xx_priv *priv, uint8_t reg, uint8_t val) { return r82xx_write_arr(priv, reg, &val, 1); } int r82xx_read_cache_reg(struct r82xx_priv *priv, int reg) { reg -= REG_SHADOW_START; if (reg >= 0 && reg < NUM_REGS) return priv->regs[reg]; else return -1; } int r82xx_write_reg_mask(struct r82xx_priv *priv, uint8_t reg, uint8_t val, uint8_t bit_mask) { int rc = r82xx_read_cache_reg(priv, reg); if (rc < 0) return rc; val = (rc & ~bit_mask) | (val & bit_mask); return r82xx_write_arr(priv, reg, &val, 1); } int r82xx_write_reg_mask_ext(struct r82xx_priv *priv, uint8_t reg, uint8_t val, uint8_t bit_mask, const char * func_name) { int r; #if USE_R82XX_ENV_VARS if (priv->printI2C) { fprintf(stderr, "%s: setting I2C register %02X: old value = %02X, new value: %02X with mask %02X\n" , func_name, reg , r82xx_read_cache_reg(priv, reg) , val, bit_mask ); } #endif r = r82xx_write_reg_mask(priv, reg, val, bit_mask); return r; } static uint8_t r82xx_bitrev(uint8_t byte) { const uint8_t lut[16] = { 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf }; return (lut[byte & 0xf] << 4) | lut[byte >> 4]; } static int r82xx_read(struct r82xx_priv *priv, uint8_t reg, uint8_t *val, int len) { int rc, i; uint8_t *p = &priv->buf[1]; priv->buf[0] = reg; rc = rtlsdr_i2c_read_fn(priv->rtl_dev, priv->cfg->i2c_addr, p, len); if (rc != len) { fprintf(stderr, "%s: i2c rd failed=%d reg=%02x len=%d\n", __FUNCTION__, rc, reg, len); if (rc < 0) return rc; return -1; } /* Copy data to the output buffer */ for (i = 0; i < len; i++) val[i] = r82xx_bitrev(p[i]); return 0; } static void print_registers(struct r82xx_priv *priv) { uint8_t data[5]; int rc; unsigned int i; rc = r82xx_read(priv, 0x00, data, sizeof(data)); if (rc < 0) return; for(i=0; iopen_d, 0x08); if (rc < 0) return rc; /* RF_MUX,Polymux */ rc = r82xx_write_reg_mask(priv, 0x1a, range->rf_mux_ploy, 0xc3); if (rc < 0) return rc; /* TF BAND */ rc = r82xx_write_reg(priv, 0x1b, range->tf_c); if (rc < 0) return rc; /* XTAL CAP & Drive */ switch (priv->xtal_cap_sel) { case XTAL_LOW_CAP_30P: case XTAL_LOW_CAP_20P: val = range->xtal_cap20p | 0x08; break; case XTAL_LOW_CAP_10P: val = range->xtal_cap10p | 0x08; break; case XTAL_HIGH_CAP_0P: val = range->xtal_cap0p | 0x00; break; default: case XTAL_LOW_CAP_0P: val = range->xtal_cap0p | 0x08; break; } rc = r82xx_write_reg_mask(priv, 0x10, val, 0x0b); return rc; } /* function of Youssef (AirSpy) and Carl (RTL-SDR) */ static int r82xx_set_pll_yc(struct r82xx_priv *priv, uint32_t freq) { const uint32_t vco_min = 1770000000; const uint32_t vco_max = 3900000000U; uint32_t pll_ref = (priv->cfg->xtal); uint32_t pll_ref_2x = (pll_ref * 2); int rc; uint32_t vco_exact; uint32_t vco_frac; uint32_t con_frac; uint32_t div_num; uint32_t n_sdm; uint16_t sdm; uint8_t ni; uint8_t si; uint8_t nint; uint8_t val_dith; uint8_t data[5]; /* Calculate divider */ for (div_num = 0; div_num < 5; div_num++) { vco_exact = freq << (div_num + 1); if (vco_exact >= vco_min && vco_exact <= vco_max) { break; } } vco_exact = freq << (div_num + 1); nint = (uint8_t) ((vco_exact + (pll_ref >> 16)) / pll_ref_2x); vco_frac = vco_exact - pll_ref_2x * nint; nint -= 13; ni = (nint >> 2); si = nint - (ni << 2); /* Set the phase splitter */ rc = r82xx_write_reg_mask(priv, 0x10, (uint8_t) (div_num << 5), 0xe0); if(rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll_yc(): error writing 'phase splitter' into i2c reg 0x10\n"); return rc; } /* Disable Dither */ val_dith = (priv->disable_dither) ? 0x10 : 0x00; rc = r82xx_write_reg_mask(priv, 0x12, val_dith, 0x18); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll_yc(): error writing 'dither' into i2c reg 0x12\n"); return rc; } /* Set the rough VCO frequency */ rc = r82xx_write_reg(priv, 0x14, (uint8_t) (ni + (si << 6))); if(rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll_yc(): error writing 'rough VCO frequency' into i2c reg 0x14\n"); return rc; } if (vco_frac == 0) { /* Disable frac pll */ rc = r82xx_write_reg_mask(priv, 0x12, 0x08, 0x08); if(rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll_yc(): error writing 'disable frac pll' into i2c reg 0x12\n"); return rc; } } else { vco_frac += pll_ref >> 16; sdm = 0; for(n_sdm = 0; n_sdm < 16; n_sdm++) { con_frac = pll_ref >> n_sdm; if (vco_frac >= con_frac) { sdm |= (uint16_t) (0x8000 >> n_sdm); vco_frac -= con_frac; if (vco_frac == 0) break; } } /* actual_freq = (((nint << 16) + sdm) * (uint64_t) pll_ref_2x) >> (div_num + 1 + 16); delta = freq - actual_freq if (actual_freq != freq) { fprintf(stderr,"Tunning delta: %d Hz", delta); } */ rc = r82xx_write_reg(priv, 0x15, (uint8_t)(sdm & 0xff)); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll_yc(): error writing 'sdm lo' into i2c reg 0x15\n"); return rc; } rc = r82xx_write_reg(priv, 0x16, (uint8_t)(sdm >> 8)); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll_yc(): error writing 'sdm hi' into i2c reg 0x16\n"); return rc; } /* Enable frac pll */ rc = r82xx_write_reg_mask(priv, 0x12, 0x00, 0x08); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll_yc(): error writing 'enable frac pll' into i2c reg 0x12\n"); return rc; } } /* all PLL stuff / registers set for this frequency */ priv->tuner_pll_set = 1; /***/ /* Check if PLL has locked */ rc = r82xx_read(priv, 0x00, data, 3); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll_yc(): error reading 'pll lock status' from i2c reg 0x00..0x02\n"); return rc; } if (!(data[2] & 0x40)) { if (priv->cfg->verbose || PRINT_PLL_ERRORS) //fprintf(stderr, "r82xx_set_pll_yc(): error writing 'sdm lo' into i2c reg 0x15\n"); fprintf(stderr, "[R82XX] PLL not locked at Tuner LO %u Hz for RF %f MHz!\n", freq, priv->rf_freq * 1E-6); priv->has_lock = 0; return -1; } priv->has_lock = 1; return rc; } static int r82xx_set_pll(struct r82xx_priv *priv, uint32_t freq) { /* freq == tuner's LO frequency */ int rc, i; uint64_t vco_freq; uint64_t vco_div; uint32_t vco_min = 1770000; /* kHz */ uint32_t vco_max = (priv->cfg->vco_algo == 0) ? (vco_min * 2) : 3900000; /* kHz */ uint32_t freq_khz, pll_ref; uint32_t sdm = 0; uint8_t mix_div = 2; uint8_t div_buf = 0; uint8_t div_num = 0; uint8_t vco_power_ref = 2; uint8_t refdiv2 = 0; uint8_t ni, si, nint, vco_fine_tune, val; uint8_t vco_curr_min = (priv->cfg->vco_curr_min == 0xff) ? 0x80 : ( priv->cfg->vco_curr_min << 5 ); uint8_t vco_curr_max = (priv->cfg->vco_curr_max == 0xff) ? 0x60 : ( priv->cfg->vco_curr_max << 5 ); /* devt->r82xx_c.vco_min = 0xff; * VCO min/max current for R18/0x12 bits [7:5] in 0 .. 7. use 0xff for default */ /* devt->r82xx_c.vco_max = 0xff; * value is inverted: programmed is 7-value, that 0 is lowest current */ uint8_t data[5]; priv->tuner_pll_set = 0; if (priv->cfg->vco_algo == 2) { /* r82xx_set_pll_yc() assumes fixed maximum current */ if (priv->last_vco_curr != vco_curr_max) { rc = r82xx_write_reg_mask(priv, 0x12, vco_curr_max, 0xe0); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error writing 'vco current' into i2c reg 0x12\n"); return rc; } priv->last_vco_curr = vco_curr_max; } return r82xx_set_pll_yc(priv, freq); } /* Frequency in kHz */ freq_khz = (freq + 500) / 1000; pll_ref = priv->cfg->xtal; rc = r82xx_write_reg_mask(priv, 0x10, refdiv2, 0x10); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error writing 'refdiv2' into i2c reg 0x10\n"); return rc; } /* set pll autotune = 128kHz */ rc = r82xx_write_reg_mask(priv, 0x1a, 0x00, 0x0c); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error writing 'pll autotune 128kHz' into i2c reg 0x1a\n"); return rc; } /* set VCO current = 100 */ if (priv->last_vco_curr != vco_curr_min) { rc = r82xx_write_reg_mask(priv, 0x12, vco_curr_min, 0xe0); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error writing 'vco current min' into i2c reg 0x12\n"); return rc; } priv->last_vco_curr = vco_curr_min; } #if 0 fprintf(stderr, "vco_last = 0x%02x; vcocmin << 5 = 0x%02x; vcocmax << 5 = 0x%02x\n", (unsigned)priv->last_vco_curr, (unsigned)vco_curr_min, (unsigned)vco_curr_max); #endif /* Calculate divider */ while (mix_div <= 64) { if (((freq_khz * mix_div) >= vco_min) && ((freq_khz * mix_div) < vco_max)) { div_buf = mix_div; while (div_buf > 2) { div_buf = div_buf >> 1; div_num++; } break; } mix_div = mix_div << 1; } rc = r82xx_read(priv, 0x00, data, sizeof(data)); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error reading 'status' from i2c reg 0x00 .. 0x04\n"); return rc; } if (priv->cfg->rafael_chip == CHIP_R828D) vco_power_ref = 1; vco_fine_tune = (data[4] & 0x30) >> 4; if (vco_fine_tune > vco_power_ref) div_num = div_num - 1; else if (vco_fine_tune < vco_power_ref) div_num = div_num + 1; rc = r82xx_write_reg_mask(priv, 0x10, div_num << 5, 0xe0); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error writing 'div_num' into i2c reg 0x10\n"); return rc; } vco_freq = (uint64_t)freq * (uint64_t)mix_div; /* * We want to approximate: * * vco_freq / (2 * pll_ref) * * in the form: * * nint + sdm/65536 * * where nint,sdm are integers and 0 < nint, 0 <= sdm < 65536 * * Scaling to fixed point and rounding: * * vco_div = 65536*(nint + sdm/65536) = int( 0.5 + 65536 * vco_freq / (2 * pll_ref) ) * vco_div = 65536*nint + sdm = int( (pll_ref + 65536 * vco_freq) / (2 * pll_ref) ) */ vco_div = (pll_ref + 65536 * vco_freq) / (2 * pll_ref); nint = (uint32_t) (vco_div / 65536); sdm = (uint32_t) (vco_div % 65536); #if PRINT_ACTUAL_VCO_AND_ERR { uint64_t actual_vco = (uint64_t)2 * pll_ref * nint + (uint64_t)2 * pll_ref * sdm / 65536; fprintf(stderr, "[R82XX] requested %u Hz; selected mix_div=%u vco_freq=%lu nint=%u sdm=%u; actual_vco=%lu; tuning error=%+dHz\n", freq, mix_div, vco_freq, nint, sdm, actual_vco, (int32_t) (actual_vco - vco_freq) / mix_div); } #endif if (nint > ((128 / vco_power_ref) - 1)) { if (priv->cfg->verbose || PRINT_PLL_ERRORS) fprintf(stderr, "[R82XX] No valid PLL values for %u Hz!\n", freq); return -1; } ni = (nint - 13) / 4; si = nint - 4 * ni - 13; rc = r82xx_write_reg(priv, 0x14, ni + (si << 6)); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error writing 'ni+(si<<6)' into i2c reg 0x14\n"); return rc; } /* pw_sdm */ if (sdm == 0) val = 0x08; else val = 0x00; if (priv->disable_dither) val |= 0x10; rc = r82xx_write_reg_mask(priv, 0x12, val, 0x18); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error writing 'dither' into i2c reg 0x12\n"); return rc; } rc = r82xx_write_reg(priv, 0x16, sdm >> 8); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error writing 'sdm hi' into i2c reg 0x16\n"); return rc; } rc = r82xx_write_reg(priv, 0x15, sdm & 0xff); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error writing 'sdm lo' into i2c reg 0x12\n"); return rc; } /* all PLL stuff / registers set for this frequency - except 8 kHz pll autotune */ priv->tuner_pll_set = 1; for (i = 0; i < 2; i++) { /* Check if PLL has locked */ rc = r82xx_read(priv, 0x00, data, 3); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error reading 'pll lock status' from i2c reg 0x00 .. 0x02\n"); return rc; } if ( (data[2] & 0x40) || vco_curr_max == vco_curr_min ) break; if (!i) { /* Didn't lock. Increase VCO current */ if (priv->last_vco_curr != vco_curr_max) { rc = r82xx_write_reg_mask(priv, 0x12, vco_curr_max, 0xe0); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error writing 'vco current max' into i2c reg 0x12\n"); return rc; } priv->last_vco_curr = vco_curr_max; } } } if (!(data[2] & 0x40)) { if (priv->cfg->verbose || PRINT_PLL_ERRORS) fprintf(stderr, "[R82XX] PLL not locked at Tuner LO %u Hz for RF %f MHz!\n", freq, priv->rf_freq * 1E-6); priv->has_lock = 0; return -1; } #if 0 else fprintf(stderr, "[R82XX] PLL locked at Tuner LO %u Hz for RF %f MHz!\n", freq, priv->rf_freq * 1E-6); #endif priv->has_lock = 1; /* set pll autotune = 8kHz */ rc = r82xx_write_reg_mask(priv, 0x1a, 0x08, 0x08); if (rc < 0 && priv->cfg->verbose) fprintf(stderr, "r82xx_set_pll(): error writing 'pll autotune 8kHz' into i2c reg 0x1a\n"); return rc; } int r82xx_is_tuner_locked(struct r82xx_priv *priv) { int rc; uint8_t data[5]; /* was all PLL stuff set for last frequency? */ if (! priv->tuner_pll_set) return 1; /* Check if PLL has locked */ rc = r82xx_read(priv, 0x00, data, sizeof(data)); if (rc < 0) return -3; if (!(data[2] & 0x40)) { if (priv->cfg->verbose || PRINT_PLL_ERRORS) fprintf(stderr, "[R82XX] PLL not locked at check!\n"); return 1; } return 0; } static int r82xx_sysfreq_sel(struct r82xx_priv *priv, enum r82xx_tuner_type type) { int rc; uint8_t lna_top = 0xe5; /* detect bw 3, lna top:4, predet top:2 */ uint8_t pre_dect = 0x40; uint8_t air_cable1_in = 0x00; uint8_t cable2_in = 0x00; if (priv->cfg->use_predetect) { rc = r82xx_write_reg_mask(priv, 0x06, pre_dect, 0x40); if (rc < 0) return rc; } priv->input = air_cable1_in; /* Air-IN only for Astrometa */ rc = r82xx_write_reg_mask(priv, 0x05, air_cable1_in, 0x60); if (rc < 0) return rc; rc = r82xx_write_reg_mask(priv, 0x06, cable2_in, 0x08); if (rc < 0) return rc; /* * Set LNA */ if (type != TUNER_ANALOG_TV) { /* LNA TOP: lowest */ rc = r82xx_write_reg_mask(priv, 0x1d, 0, 0x38); if (rc < 0) return rc; /* 0: PRE_DECT off */ rc = r82xx_write_reg_mask(priv, 0x06, 0, 0x40); if (rc < 0) return rc; /* agc clk 250hz */ rc = r82xx_write_reg_mask(priv, 0x1a, 0x30, 0x30); if (rc < 0) return rc; /* write LNA TOP = 3 */ rc = r82xx_write_reg_mask(priv, 0x1d, 0x18, 0x38); if (rc < 0) return rc; /* agc clk 60hz */ rc = r82xx_write_reg_mask(priv, 0x1a, 0x20, 0x30); if (rc < 0) return rc; } else { /* PRE_DECT off */ rc = r82xx_write_reg_mask(priv, 0x06, 0, 0x40); if (rc < 0) return rc; /* write LNA TOP */ rc = r82xx_write_reg_mask(priv, 0x1d, lna_top, 0x38); if (rc < 0) return rc; /* agc clk 1Khz, external det1 cap 1u */ rc = r82xx_write_reg_mask(priv, 0x1a, 0x00, 0x30); if (rc < 0) return rc; rc = r82xx_write_reg_mask(priv, 0x10, 0x00, 0x04); if (rc < 0) return rc; } return 0; } static int r82xx_set_tv_standard(struct r82xx_priv *priv, enum r82xx_tuner_type type, uint32_t delsys) { int rc, i; uint8_t data[5]; int need_calibration = 1; /* BW < 6 MHz */ uint8_t filt_q = 0x10; /* r10[4]:low q(1'b1) */ /* for LT Gain test */ if (type != TUNER_ANALOG_TV) { rc = r82xx_write_reg_mask(priv, 0x1d, 0x00, 0x38); if (rc < 0) return rc; } priv->rf_freq = 56000 * 1000; /* set a default frequency */ priv->if_band_center_freq = 0; priv->int_freq = 3570 * 1000; priv->sideband = 0; /* Check if standard changed. If so, filter calibration is needed */ /* as we call this function only once in rtlsdr, force calibration */ if (need_calibration) { for (i = 0; i < 2; i++) { /* set cali clk =on */ rc = r82xx_write_reg_mask(priv, 0x0f, 0x04, 0x04); if (rc < 0) return rc; priv->tuner_pll_set = 0; rc = r82xx_set_pll(priv, priv->rf_freq); if (rc < 0 || !priv->has_lock) return rc; /* Start Trigger */ rc = r82xx_write_reg_mask_ext(priv, 0x0b, 0x10, 0x10, __FUNCTION__); if (rc < 0) return rc; /* Stop Trigger */ rc = r82xx_write_reg_mask_ext(priv, 0x0b, 0x00, 0x10, __FUNCTION__); if (rc < 0) return rc; /* set cali clk =off */ rc = r82xx_write_reg_mask(priv, 0x0f, 0x00, 0x04); if (rc < 0) return rc; /* Check if calibration worked */ rc = r82xx_read(priv, 0x00, data, sizeof(data)); if (rc < 0) return rc; priv->fil_cal_code = data[4] & 0x0f; if (priv->fil_cal_code && priv->fil_cal_code != 0x0f) break; } /* narrowest */ if (priv->fil_cal_code == 0x0f) priv->fil_cal_code = 0; } rc = r82xx_write_reg_mask_ext(priv, 0x0a, filt_q | priv->fil_cal_code, 0x1f, __FUNCTION__); if (rc < 0) return rc; /* Store current standard. If it changes, re-calibrate the tuner */ priv->delsys = delsys; priv->type = type; priv->bw = 3; return 0; } /* measured with a Racal 6103E GSM test set at 928 MHz with -60 dBm * input power, for raw results see: * http://steve-m.de/projects/rtl-sdr/gain_measurement/r820t/ */ #define VGA_BASE_GAIN -47 static const int r82xx_vga_gain_steps[] = { 0, 26, 26, 30, 42, 35, 24, 13, 14, 32, 36, 34, 35, 37, 35, 36 /* -47, -21, 5, 35, 77, 112, 136, 149, 163, 195, 231, 265, 300, 337, 372, 408 */ }; static const int r82xx_lna_gain_steps[] = { 0, 9, 13, 40, 38, 13, 31, 22, 26, 31, 26, 14, 19, 5, 35, 13 }; static const int r82xx_mixer_gain_steps[] = { 0, 5, 10, 10, 19, 9, 10, 25, 17, 10, 8, 16, 13, 6, 3, -8 }; static void r82xx_get_rf_gain_index(int gain, int *ptr_lna_index, int *ptr_mix_index) { int i, total_gain = 0; int mix_index = 0, lna_index = 0; for (i = 0; i < 15; i++) { if (total_gain >= gain) break; total_gain += r82xx_lna_gain_steps[++lna_index]; if (total_gain >= gain) break; total_gain += r82xx_mixer_gain_steps[++mix_index]; } *ptr_lna_index = lna_index; *ptr_mix_index = mix_index; } static int r82xx_get_if_gain_index(int gain) { int vga_index, total_gain = VGA_BASE_GAIN; for (vga_index = 0; vga_index < 15; vga_index++) { if (total_gain >= gain) break; total_gain += r82xx_vga_gain_steps[++vga_index]; } return vga_index; } static int r82xx_get_lna_gain_from_index(int lna_index) { int i, total_gain = 0; if ( lna_index < 0 || lna_index > 15 ) return 0; for ( i = 0; i <= lna_index; ++i ) total_gain += r82xx_lna_gain_steps[i]; return total_gain; } static int r82xx_get_mixer_gain_from_index(int mixer_index) { int i, total_gain = 0; if ( mixer_index < 0 || mixer_index > 15 ) return 0; for ( i = 0; i <= mixer_index; ++i ) total_gain += r82xx_mixer_gain_steps[i]; return total_gain; } static int r82xx_get_vga_gain_from_index(int vga_index) { int i, total_gain = VGA_BASE_GAIN; if ( vga_index < 0 || vga_index > 15 ) return 0; for ( i = 0; i <= vga_index; ++i ) total_gain += r82xx_vga_gain_steps[i]; return total_gain; } /* set HF gain (LNA/Mixer) and pass through for IF gain (VGA) */ int r82xx_set_gain(struct r82xx_priv *priv, int set_manual_gain, int gain, int extended_mode, int lna_gain_idx, int mixer_gain_idx, int vga_gain_idx, int *rtl_vga_control) { int rc; int new_if_mode = priv->last_if_mode; uint8_t data[4]; if (extended_mode || set_manual_gain) { if (set_manual_gain) { r82xx_get_rf_gain_index(gain, &lna_gain_idx, &mixer_gain_idx); } /* LNA auto off == manual */ rc = r82xx_write_reg_mask(priv, 0x05, 0x10, 0x10); if (rc < 0) return rc; /* Mixer auto off == manual mode */ rc = r82xx_write_reg_mask(priv, 0x07, 0, 0x10); if (rc < 0) return rc; rc = r82xx_read(priv, 0x00, data, sizeof(data)); if (rc < 0) return rc; /* Set LNA */ rc = r82xx_write_reg_mask(priv, 0x05, lna_gain_idx, 0x0f); if (rc < 0) return rc; /* Set Mixer */ rc = r82xx_write_reg_mask(priv, 0x07, mixer_gain_idx, 0x0f); if (rc < 0) return rc; /* save last values */ priv->last_manual_gain = set_manual_gain; priv->last_extended_mode = extended_mode; priv->last_LNA_value = lna_gain_idx; priv->last_Mixer_value = mixer_gain_idx; /* prepare VGA */ if (extended_mode) { new_if_mode = vga_gain_idx +10000; } } else { /* LNA auto on = AGC */ rc = r82xx_write_reg_mask(priv, 0x05, 0, 0x10); if (rc < 0) return rc; /* Mixer auto on = AGC */ rc = r82xx_write_reg_mask(priv, 0x07, 0x10, 0x10); if (rc < 0) return rc; /* save last values */ priv->last_manual_gain = set_manual_gain; priv->last_extended_mode = extended_mode; #ifdef VGA_FOR_AGC_MODE new_if_mode = VGA_FOR_AGC_MODE; #endif } /* Set VGA */ rc = r82xx_set_if_mode(priv, new_if_mode, rtl_vga_control); return rc; } int r82xx_get_rf_gain(struct r82xx_priv *priv) { int lna_gain = r82xx_get_lna_gain_from_index(priv->last_LNA_value); int mix_gain = r82xx_get_mixer_gain_from_index(priv->last_Mixer_value); int gain = lna_gain + mix_gain; return gain; } int r82xx_get_if_gain(struct r82xx_priv *priv) { int vga_gain = r82xx_get_vga_gain_from_index(priv->last_VGA_value); return vga_gain; } /* set IF gain (VGA) */ int r82xx_set_if_mode(struct r82xx_priv *priv, int if_mode, int *rtl_vga_control) { int rc = 0, vga_gain_idx = 0; int is_rtlsdr_blog_v4; if (rtl_vga_control) *rtl_vga_control = 0; if ( 0 == if_mode || 10016 == if_mode ) { vga_gain_idx = 0x10; } else if ( -2500 <= if_mode && if_mode <= 2500 ) { vga_gain_idx = r82xx_get_if_gain_index(if_mode); } else if ( 2500 < if_mode && if_mode < 10000 ) { vga_gain_idx = r82xx_get_if_gain_index(if_mode - 5000); } else if ( 10000 <= if_mode && if_mode <= 10016+15 ) { vga_gain_idx = if_mode -10000; } else { /* assume 0 == default */ fprintf(stderr, "%s: invalid if_mode value %d; setting to default: %d\n", __FUNCTION__, if_mode, 10000 + DEFAULT_IF_VGA_VAL ); /* was 26.5 dB == -12 dB + 0x0b * 3.5 dB with AGC on * and 16.3 dB == -12 dB + 8 * 3.5 dB with AGC off * BUT IT makes no sense to make this different! */ /* vga_gain_idx = (priv->last_extended_mode || priv->last_manual_gain) ? 0x08 : 0x0b; */ if_mode = 10000 + DEFAULT_IF_VGA_VAL; vga_gain_idx = DEFAULT_IF_VGA_VAL; } #if PRINT_VGA_REG fprintf(stderr, "%s: writing 0x%02X (=%.1f dB) into VGA register %sactivating RTL AGC control\n", __FUNCTION__, (vga_gain_idx & 0x0f), (-12.0 + 3.5 * (vga_gain_idx & 0x0f)), ( (vga_gain_idx & 0x10) && rtl_vga_control ) ? "" : "de" ); #endif /* VGA auto control does not work on the V4, fix to an appropriate value instead like in Osmocom drivers * Furthermore, this auto VGA needs to be looked at more closely, it appears to cause a lot of spectrum pumping * and intermod on other devices. Fixed VGA gain always seems to yield better results. */ is_rtlsdr_blog_v4 = rtlsdr_check_dongle_model(priv->rtl_dev, "RTLSDRBlog", "Blog V4"); if(is_rtlsdr_blog_v4) { rc = r82xx_write_reg_mask(priv, 0x0c, 0x08, 0x9f); } else { rc = r82xx_write_reg_mask(priv, 0x0c, vga_gain_idx, 0x1f); } if (rc < 0) return rc; priv->last_if_mode = if_mode; priv->last_VGA_value = vga_gain_idx; if ( (vga_gain_idx & 0x10) && rtl_vga_control ) *rtl_vga_control = 1; return rc; } /* expose/permit tuner specific i2c register hacking! */ int r82xx_set_i2c_register(struct r82xx_priv *priv, unsigned i2c_register, unsigned data, unsigned mask) { uint8_t reg = i2c_register & 0xFF; uint8_t reg_mask = mask & 0xFF; uint8_t reg_val = data & 0xFF; return r82xx_write_reg_mask(priv, reg, reg_val, reg_mask); } //-cs- int r82xx_get_i2c_register(struct r82xx_priv *priv, unsigned char* data, int len) { int rc, i, len1; // The lower 5 I2C registers can be read with the normal read fct, the upper ones are read from the cache if(len < 5) len1 = len; else len1 = 5; rc = r82xx_read(priv, 0x00, data, len1); if (rc < 0) return rc; if(len > 5) for (i = 5; i < len; i++) data[i] = r82xx_read_cache_reg(priv, i); return 0; } //-cs- end int r82xx_set_i2c_override(struct r82xx_priv *priv, unsigned i2c_register, unsigned data, unsigned mask) { uint8_t reg = i2c_register & 0xFF; uint8_t reg_mask = mask & 0xFF; uint8_t reg_val = data & 0xFF; fprintf(stderr, "%s: register %d = %02X. mask %02X, data %03X\n" , __FUNCTION__, i2c_register, i2c_register, mask, data ); if ( REG_SHADOW_START <= reg && reg < REG_SHADOW_START + NUM_REGS ) { uint8_t oldMask = priv->override_mask[reg - REG_SHADOW_START]; uint8_t oldData = priv->override_data[reg - REG_SHADOW_START]; if ( data & ~0xFF ) { priv->override_mask[reg - REG_SHADOW_START] &= ~reg_mask; priv->override_data[reg - REG_SHADOW_START] &= ~reg_mask; fprintf(stderr, "%s: subtracted override mask for register %02X. old mask %02X, old data %02X. new mask is %02X, new data %02X\n" , __FUNCTION__ , i2c_register , oldMask, oldData , priv->override_mask[reg - REG_SHADOW_START] , priv->override_data[reg - REG_SHADOW_START] ); } else { priv->override_mask[reg - REG_SHADOW_START] |= reg_mask; priv->override_data[reg - REG_SHADOW_START] &= (~reg_mask); fprintf(stderr, "override_data[] &= ( ~(mask %02X) = %02X ) => %02X\n", reg_mask, ~reg_mask, priv->override_data[reg - REG_SHADOW_START] ); priv->override_data[reg - REG_SHADOW_START] |= (reg_mask & reg_val); fprintf(stderr, "override_data[] |= ( mask %02X & val %02X )\n", reg_mask, reg_val ); fprintf(stderr, "%s: added override mask for register %d = %02X. old mask %02X, old data %02X. new mask is %02X, new data %02X\n" , __FUNCTION__ , i2c_register, i2c_register , oldMask, oldData , priv->override_mask[reg - REG_SHADOW_START] , priv->override_data[reg - REG_SHADOW_START] ); } return r82xx_write_reg_mask_ext(priv, reg, 0, 0, __FUNCTION__); } else return -1; } struct IFinfo { int sharpCorner; /* 1 = at LSB (lower side band); 2 = at USB (upper side band); 3 = both LSB+USB */ int bw; /* 3-dB bandwidth in kHz - available around IF frequency, which becomes center frequency */ int fif; /* IF frequency in kHz for the RTL2832 */ int fc; /* IF frequency correction in kHz */ uint8_t reg10Lo; /* low-part of I2C register 0x0A */ uint8_t reg11; /* content of I2C register 0x0B */ uint8_t reg30Hi; /* R30 = 0x1E: channel filter extension "on weak signal" */ }; /* narrowest IF bandpass with reg10/reg11/reg30 = 0x0F, 0xEF, 0x60: * 539 .. 2002 kHz (mirrored from tuner) * calculate IF center frequency from that bandpass edges */ #define IFA(BW) (2002 - BW / 2) #define IFB(BW) (539 + BW / 2) /* duplicated lower bandwidths to allow "sideband" selection: which is filtered with help of IF corner */ static const struct IFinfo IFi[] = { #if (WITH_ASYM_FILTER) { 1, 200+1, IFA( 200), 33, 0x0F, 0xEF, 0x60 }, /* steep low freq edge */ { 2, 200+2, IFB( 200), 3, 0x0F, 0xEF, 0x60 }, /* steep high freq edge */ #endif { 3, 290+0, 1950, -25, 0x0F, 0xE7, 0x00 }, /* centered with hpf - high pass filter */ #if (WITH_ASYM_FILTER) { 1, 290+1, IFA( 290), 26, 0x0F, 0xEF, 0x60 }, /* steep low freq edge */ { 2, 290+2, IFB( 290), 2, 0x0F, 0xEF, 0x60 }, /* steep high freq edge */ #endif { 3, 375+0, 1870, -13, 0x0F, 0xE8, 0x00 }, /* centered with hpf */ #if (WITH_ASYM_FILTER) { 1, 375+1, IFA( 375), 23, 0x0F, 0xEF, 0x60 }, /* steep low freq edge */ { 2, 375+2, IFB( 375), 3, 0x0F, 0xEF, 0x60 }, /* steep high freq edge */ #endif { 3, 420+0, 2100, 21, 0x0F, 0xD7, 0x00 }, /* centered with hpf */ #if (WITH_ASYM_FILTER) { 1, 420+1, IFA( 420), 23, 0x0F, 0xEF, 0x60 }, /* steep low freq edge */ { 2, 420+2, IFB( 420), 3, 0x0F, 0xEF, 0x60 }, /* steep high freq edge */ #endif { 3, 470+0, 1800, -12, 0x0F, 0xE9, 0x00 }, /* centered with hpf */ #if (WITH_ASYM_FILTER) { 1, 470+1, IFA( 470), 18, 0x0F, 0xEF, 0x60 }, /* steep low freq edge */ { 2, 470+2, IFB( 470), 2, 0x0F, 0xEF, 0x60 }, /* steep high freq edge */ #endif { 3, 600+0, 1700, 6, 0x0F, 0xEA, 0x00 }, /* centered with hpf */ #if (WITH_ASYM_FILTER) { 1, 600+1, IFA( 600), 16, 0x0F, 0xEF, 0x60 }, /* steep low freq edge */ { 2, 600+2, IFB( 600), 3, 0x0F, 0xEF, 0x60 }, /* steep high freq edge */ #endif { 3, 860+0, 1550, 8, 0x0F, 0xEB, 0x00 }, /* centered with hpf */ #if (WITH_ASYM_FILTER) { 1, 860+1, IFA( 860), 17, 0x0F, 0xEF, 0x60 }, /* steep low freq edge */ { 2, 860+2, IFB( 860), -12, 0x0F, 0xEF, 0x60 }, /* steep high freq edge */ #endif { 3, 950+0, 2200, 5, 0x0F, 0x88, 0x00 }, /* centered with hpf */ #if (WITH_ASYM_FILTER) { 1, 950+1, IFA( 950), 6, 0x0F, 0xEF, 0x60 }, /* steep low freq edge */ { 2, 950+2, IFB( 950), 0, 0x0F, 0xEF, 0x60 }, /* steep high freq edge */ #endif { 3, 1100+0, 2100, 25, 0x0F, 0x89, 0x00 }, /* centered with hpf */ #if (WITH_ASYM_FILTER) { 1, 1100+1, IFA(1100), 24, 0x0F, 0xEF, 0x60 }, /* steep low freq edge */ { 2, 1100+2, IFB(1100), 0, 0x0F, 0xEF, 0x60 }, /* steep high freq edge */ #endif { 3, 1200+0, 1350, 0, 0x0F, 0xEE, 0x00 }, /* centered with hpf */ { 3, 1300+0, 2050, -7, 0x0F, 0x8A, 0x00 }, /* centered with hpf */ #if (WITH_ASYM_FILTER) { 1, 1300+1, IFA(1300), 26, 0x0F, 0xEF, 0x60 }, /* steep low freq edge */ { 2, 1300+2, IFB(1300), 0, 0x0F, 0xEF, 0x60 }, /* steep high freq edge */ #endif { 3, 1500+3, 1300, -24, 0x0F, 0xEF, 0x60 }, { 3, 1600+0, 1900, 0, 0x0F, 0x8B, 0x00 }, /* centered with hpf */ { 3, 1750+3, 1400, 12, 0x0F, 0xCF, 0x60 }, /* 20 */ { 3, 1800+0, 1400, 0, 0x0F, 0xAF, 0x00 }, { 3, 1950+3, 1500, 30, 0x0F, 0x8F, 0x60 }, { 3, 2200+0, 1600, 0, 0x0F, 0x8F, 0x00 }, { 3, 3000+0, 2000, 0, 0x04, 0x8F, 0x00 }, { 3, 5000+0, 3570, 0, 0x0B, 0x6B, 0x00 } }; /* settings from Oldenburger: static const int r82xx_bws[]= { 300, 450, 600, 900, 1100, 1200, 1300, 1500, 1800, 2200, 3000, 5000 }; static const uint8_t r82xx_0xa[]= { 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0e, 0x0f, 0x0f, 0x04, 0x0b }; static const uint8_t r82xx_0xb[]= { 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xaf, 0x8f, 0x8f, 0x6b }; static const int r82xx_if[] = { 1700, 1650, 1600, 1500, 1400, 1350, 1320, 1270, 1400, 1600, 2000, 3570 }; */ static const int r82xx_bw_tablen = sizeof(IFi) / sizeof(IFi[0]); #define FILT_HP_BW1 350000 #define FILT_HP_BW2 380000 int r82xx_set_bandwidth(struct r82xx_priv *priv, int bw, uint32_t rate, uint32_t * applied_bw, int apply) { int rc; int i; int real_bw = 0; #if USE_R82XX_ENV_VARS uint8_t reg_09, reg_0d =0, reg_0e =0; #endif uint8_t reg_mask; uint8_t reg_0a; uint8_t reg_0b; uint8_t reg_1e = 0x60; /* default: Enable Filter extension under weak signal */ if (bw > 7000000) { // BW: 8 MHz *applied_bw = 8000000; reg_0a = 0x10; reg_0b = 0x0b; if (apply) priv->int_freq = 4570000; } else if (bw > 6000000) { // BW: 7 MHz *applied_bw = 7000000; reg_0a = 0x10; reg_0b = 0x2a; if (apply) priv->int_freq = 4570000; } else if (bw > 4500000) { // BW: 6 MHz *applied_bw = 6000000; reg_0a = 0x10; reg_0b = 0x6b; if (apply) priv->int_freq = 3570000; } else { for(i=0; i < r82xx_bw_tablen-1; ++i) { const int bwnext = IFi[i+1].bw * 1000 + ( IFi[i+1].sharpCorner == 2 ? 400 : 0 ); const int bwcurr = IFi[i].bw * 1000 + ( IFi[i].sharpCorner == 2 ? 400 : 0 ); /* bandwidth is compared to median of the current and next available bandwidth in the table */ if (bw < (bwnext + bwcurr)/2) break; } reg_0a = IFi[i].reg10Lo; reg_0b = IFi[i].reg11; reg_1e = IFi[i].reg30Hi; real_bw = IFi[i].bw * 1000; /* kHz part */ #if 0 fprintf(stderr, "%s: selected idx %d: R10 = %02X, R11 = %02X, Bw %d, IF %d\n" , __FUNCTION__, i , (unsigned)reg_0a , (unsigned)reg_0b , real_bw , IFi[i].fif * 1000 + IFi[i].fc ); #endif *applied_bw = real_bw; if (apply) priv->int_freq = ( IFi[i].fif + IFi[i].fc ) * 1000; } #if USE_R82XX_ENV_VARS // hacking RTLSDR IF-Center frequency - on environment variable if ( priv->filterCenter && apply ) { #if 0 fprintf(stderr, "*** applied IF center %d\n", priv->filterCenter); #endif priv->int_freq = priv->filterCenter; } #endif if (!apply) return 0; #if USE_R82XX_ENV_VARS /* Register 0x9 = R9: IF Filter Power/Current */ if ( priv->haveR9 ) { fprintf(stderr, "*** in function %s: PREV I2C register %02X value: %02X\n", __FUNCTION__, 0x09, r82xx_read_cache_reg(priv, 0x09) ); reg_09 = priv->valR9 << 6; reg_mask = 0xC0; // 1100 0000 fprintf(stderr, "*** read R9 from environment: %d\n", priv->valR9); rc = r82xx_write_reg_mask(priv, 0x09, reg_09, 0xc0); if (rc < 0) fprintf(stderr, "ERROR setting I2C register 0x09 to value %02X with mask %02X\n", (unsigned)reg_09, (unsigned)reg_mask); } #endif /* Register 0xA = R10 */ reg_mask = 0x0F; // default: 0001 0000 #if USE_R82XX_ENV_VARS if ( priv->haveR10H ) { reg_0a = ( reg_0a & ~0xf0 ) | ( priv->valR10H << 4 ); reg_mask = 0xf0; } if ( priv->haveR10L ) { reg_0a = ( reg_0a & ~0x0f ) | priv->valR10L; reg_mask |= 0x0f; } #endif rc = r82xx_write_reg_mask_ext(priv, 0x0a, reg_0a, reg_mask, __FUNCTION__); if (rc < 0) { fprintf(stderr, "ERROR setting I2C register 0x0A to value %02X with mask %02X\n", (unsigned)reg_0a, (unsigned)reg_mask); return rc; } /* Register 0xB = R11 with undocumented Bit 7 for filter bandwidth for Hi-part FILT_BW */ reg_mask = 0xEF; // default: 1110 1111 #if USE_R82XX_ENV_VARS if ( priv->haveR11H ) { reg_0b = ( reg_0b & ~0xE0 ) | ( priv->valR11H << 5 ); } if ( priv->haveR11L ) { reg_0b = ( reg_0b & ~0x0F ) | priv->valR11L; } #endif rc = r82xx_write_reg_mask_ext(priv, 0x0b, reg_0b, reg_mask, __FUNCTION__); if (rc < 0) { fprintf(stderr, "ERROR setting I2C register 0x0B to value %02X with mask %02X\n" , (unsigned)reg_0b, (unsigned)reg_mask); return rc; } /* Register 0xD = R13: LNA agc power detector voltage threshold high + low setting */ reg_mask = 0x00; // default: 0000 0000 #if USE_R82XX_ENV_VARS if ( priv->haveR13H ) { reg_0d = ( reg_0d & ~0xf0 ) | ( priv->valR13H << 4 ); reg_mask |= 0xF0; } if ( priv->haveR13L ) { reg_0d = ( reg_0d & ~0x0f ) | priv->valR13L; reg_mask |= 0x0F; } if ( reg_mask ) { rc = r82xx_write_reg_mask_ext(priv, 0x0d, reg_0d, reg_mask, __FUNCTION__); if (rc < 0) fprintf(stderr, "ERROR setting I2C register 0x0D to value %02X with mask %02X\n" , (unsigned)reg_0d, (unsigned)reg_mask); } #endif /* Register 0xE = R14: MIXER agc power detector voltage threshold high + low setting */ reg_mask = 0x00; // default: 0000 0000 #if USE_R82XX_ENV_VARS if ( priv->haveR14H ) { reg_0e = ( reg_0e & ~0xf0 ) | ( priv->valR14H << 4 ); reg_mask |= 0xF0; } if ( priv->haveR14L ) { reg_0e = ( reg_0e & ~0x0f ) | priv->valR14L; reg_mask |= 0x0F; } if ( reg_mask ) { rc = r82xx_write_reg_mask_ext(priv, 0x0e, reg_0e, reg_mask, __FUNCTION__); if (rc < 0) fprintf(stderr, "%s: ERROR setting I2C register 0x0E to value %02X with mask %02X\n" , __FUNCTION__, (unsigned)reg_0e, (unsigned)reg_mask); } #endif /* channel filter extension */ reg_mask = 0x40; #if USE_R82XX_ENV_VARS if ( priv->haveR30H ) { reg_1e = ( priv->valR30H << 6 ); reg_mask = reg_mask | 0xC0; } if ( priv->haveR30L ) { reg_1e = reg_1e | priv->valR30L; reg_mask = reg_mask | 0x3F; } #endif rc = r82xx_write_reg_mask_ext(priv, 0x1e, reg_1e, reg_mask, __FUNCTION__); if (rc < 0) fprintf(stderr, "%s: ERROR setting I2C register 0x1E to value %02X with mask %02X\n" , __FUNCTION__, (unsigned)reg_1e, (unsigned)reg_mask); return priv->int_freq; } #undef FILT_HP_BW1 #undef FILT_HP_BW2 int r82xx_set_bw_center(struct r82xx_priv *priv, int32_t if_band_center_freq) { priv->if_band_center_freq = if_band_center_freq; return priv->int_freq; } int r82xx_set_sideband(struct r82xx_priv *priv, int sideband) { int rc; priv->sideband = sideband; rc = r82xx_write_reg_mask(priv, 0x07, (sideband << 7) & 0x80, 0x80); if (rc < 0) return rc; return 0; } int r82xx_get_sideband(struct r82xx_priv *priv) { return priv->sideband; } static const uint32_t harm_sideband_xor[17] = { 0 /* 0 - should not happen */ , 0 /* 1 - default - as without harmonic */ , 0 /* 2: ( 2 * 90 deg) % 360 = 180 deg */ , 1 /* 3: ( 3 * 90 deg) % 360 = -90 deg */ , 0 /* 4: ( 4 * 90 deg) % 360 = 0 deg */ , 0 /* 5: ( 5 * 90 deg) % 360 = +90 deg */ , 0 /* 6: ( 6 * 90 deg) % 360 = 180 deg */ , 1 /* 7: ( 7 * 90 deg) % 360 = -90 deg */ , 0 /* 8: ( 8 * 90 deg) % 360 = 0 deg */ , 0 /* 9: ( 9 * 90 deg) % 360 = +90 deg */ , 0 /* 10: (10 * 90 deg) % 360 = 180 deg */ , 1 /* 11: (11 * 90 deg) % 360 = -90 deg */ , 0 /* 12: (12 * 90 deg) % 360 = 0 deg */ , 0 /* 13: (13 * 90 deg) % 360 = +90 deg */ , 0 /* 14: (14 * 90 deg) % 360 = 180 deg */ , 1 /* 15: (15 * 90 deg) % 360 = -90 deg */ , 0 /* 16: (16 * 90 deg) % 360 = 180 deg */ }; int r82xx_flip_rtl_sideband(struct r82xx_priv *priv) { return harm_sideband_xor[priv->tuner_harmonic]; } int r82xx_set_freq64(struct r82xx_priv *priv, uint64_t freq) { int rc = -1; int is_rtlsdr_blog_v4; uint64_t upconvert_freq; uint64_t lo_freq; uint32_t lo_freqHarm; uint8_t air_cable1_in; uint8_t open_d; uint8_t band; uint8_t cable_2_in; uint8_t cable_1_in; uint8_t air_in; int nth_harm; int harm; is_rtlsdr_blog_v4 = rtlsdr_check_dongle_model(priv->rtl_dev, "RTLSDRBlog", "Blog V4"); /* if it's an RTL-SDR Blog V4, automatically upconvert by 28.8 MHz if we tune to HF * so that we don't need to manually set any upconvert offset in the SDR software */ upconvert_freq = is_rtlsdr_blog_v4 ? ((freq < MHZ(28.8)) ? (freq + MHZ(28.8)) : freq) : freq; harm = (priv->cfg->harmonic <= 0) ? DEFAULT_HARMONIC : priv->cfg->harmonic; nth_harm = ( freq > FIFTH_HARM_FRQ_THRESH_KHZ * (uint64_t)1000 ) ? 1 : 0; for ( ; nth_harm < 2; ++nth_harm ) { priv->tuner_pll_set = 0; priv->tuner_harmonic = ( nth_harm ) ? harm : 0; if (!freq) freq = priv->rf_freq; /* ignore zero frequency; keep last one */ else priv->rf_freq = upconvert_freq; if ( priv->sideband ^ harm_sideband_xor[priv->tuner_harmonic] ) lo_freq = upconvert_freq - priv->int_freq + priv->if_band_center_freq; else lo_freq = upconvert_freq + priv->int_freq + priv->if_band_center_freq; lo_freqHarm = (nth_harm) ? ( lo_freq / harm ) : lo_freq; #if PRINT_HARMONICS fprintf(stderr, "%s(freq = %f MHz) @ %s--> intfreq %u Hz, ifcenter %d --> f %f MHz, PLL %f MHz\n" , __FUNCTION__, upconvert_freq * 1E-6, (priv->sideband ? "USB" : "LSB") , (unsigned)priv->int_freq, (int)priv->if_band_center_freq , lo_freq * 1E-6, lo_freqHarm * 1E-6 ); #endif rc = r82xx_set_mux(priv, lo_freq); if (rc < 0) { if (priv->cfg->verbose) fprintf(stderr, "r82xx_set_freq(): error at r82xx_set_mux()\n"); goto err; } rc = r82xx_set_pll(priv, lo_freqHarm); if (rc < 0 || !priv->has_lock) { if ( !nth_harm && lo_freq > RETRY_WITH_FIFTH_HARM_KHZ * 1000 ) continue; goto err; } if ( nth_harm ) { #if 0 fprintf(stderr, "r82xx_set_freq(): set up for %d-th harmonic\n", harm); #endif } break; } if (is_rtlsdr_blog_v4) { /* determine if notch filters should be on or off notches are turned OFF * when tuned within the notch band and ON when tuned outside the notch band. */ open_d = (freq <= MHZ(2.2) || (freq >= MHZ(85) && freq <= MHZ(112)) || (freq >= MHZ(172) && freq <= MHZ(242))) ? 0x00 : 0x08; rc = r82xx_write_reg_mask(priv, 0x17, open_d, 0x08); if (rc < 0) return rc; /* select tuner band based on frequency and only switch if there is a band change *(to avoid excessive register writes when tuning rapidly) */ band = (freq <= MHZ(28.8)) ? HF : ((freq > MHZ(28.8) && freq < MHZ(250)) ? VHF : UHF); /* switch between tuner inputs on the RTL-SDR Blog V4 */ if (band != priv->input) { priv->input = band; /* activate cable 2 (HF input) */ cable_2_in = (band == HF) ? 0x08 : 0x00; rc = r82xx_write_reg_mask(priv, 0x06, cable_2_in, 0x08); if (rc < 0) goto err; /* activate cable 1 (VHF input) */ cable_1_in = (band == VHF) ? 0x40 : 0x00; rc = r82xx_write_reg_mask(priv, 0x05, cable_1_in, 0x40); if (rc < 0) goto err; /* activate air_in (UHF input) */ air_in = (band == UHF) ? 0x00 : 0x20; rc = r82xx_write_reg_mask(priv, 0x05, air_in, 0x20); if (rc < 0) goto err; } } else { /* switch between 'Cable1' and 'Air-In' inputs on sticks with * R828D tuner. We switch at 345 MHz, because that's where the * noise-floor has about the same level with identical LNA * settings. The original driver used 320 MHz. */ air_cable1_in = (freq > MHZ(345)) ? 0x00 : 0x60; if ((priv->cfg->rafael_chip == CHIP_R828D) && (air_cable1_in != priv->input)) { priv->input = air_cable1_in; rc = r82xx_write_reg_mask(priv, 0x05, air_cable1_in, 0x60); } } err: #if PRINT_PLL_ERRORS if (rc < 0) fprintf(stderr, "%s: failed=%d\n", __FUNCTION__, rc); #endif return rc; } int r82xx_set_freq(struct r82xx_priv *priv, uint32_t freq) { return r82xx_set_freq64(priv, (uint64_t)freq); } int r82xx_set_dither(struct r82xx_priv *priv, int dither) { priv->disable_dither = !dither; return 0; } /* * r82xx standby logic */ int r82xx_standby(struct r82xx_priv *priv) { int rc; /* If device was not initialized yet, don't need to standby */ if (!priv->init_done) return 0; rc = r82xx_write_reg(priv, 0x06, 0xb1); if (rc < 0) return rc; rc = r82xx_write_reg(priv, 0x05, 0xa0); if (rc < 0) return rc; rc = r82xx_write_reg(priv, 0x07, 0x3a); if (rc < 0) return rc; rc = r82xx_write_reg(priv, 0x08, 0x40); if (rc < 0) return rc; rc = r82xx_write_reg(priv, 0x09, 0xc0); if (rc < 0) return rc; rc = r82xx_write_reg(priv, 0x0a, 0x36); if (rc < 0) return rc; rc = r82xx_write_reg(priv, 0x0c, 0x35); if (rc < 0) return rc; rc = r82xx_write_reg(priv, 0x0f, 0x68); if (rc < 0) return rc; rc = r82xx_write_reg(priv, 0x11, 0x03); if (rc < 0) return rc; rc = r82xx_write_reg(priv, 0x17, 0xf4); if (rc < 0) return rc; rc = r82xx_write_reg(priv, 0x19, 0x0c); /* Force initial calibration */ priv->type = -1; return rc; } /* * r82xx device init logic */ int r82xx_init(struct r82xx_priv *priv) { int rc; #if PRINT_INITIAL_REGISTERS #define INIT_NUM_READ_REGS 16 uint8_t initial_register_values[INIT_NUM_READ_REGS]; /* see what is 'default' */ int k; /* get initial register values - just to see .. */ memset( &(initial_register_values[0]), 0, sizeof(initial_register_values) ); printf("R820T/2 initial register settings:\n"); r82xx_read(priv, 0x00, initial_register_values, sizeof(initial_register_values)); for (k=0; k < INIT_NUM_READ_REGS; ++k) printf("register 0x%02x: 0x%02x\n", k, initial_register_values[k]); printf("\n"); #endif /* TODO: R828D might need r82xx_xtal_check() */ priv->xtal_cap_sel = XTAL_HIGH_CAP_0P; priv->rf_freq = 0; priv->if_band_center_freq = 0; priv->last_if_mode = 0; priv->last_manual_gain = 0; priv->last_extended_mode = 0; priv->last_LNA_value = 0; priv->last_Mixer_value = 0; priv->last_VGA_value = DEFAULT_IF_VGA_VAL; priv->last_vco_curr = 0xff; /* Initialize override registers */ memset( &(priv->override_data[0]), 0, NUM_REGS * sizeof(uint8_t) ); memset( &(priv->override_mask[0]), 0, NUM_REGS * sizeof(uint8_t) ); /* Initialize registers */ rc = r82xx_write_arr(priv, 0x05, r82xx_init_array, sizeof(r82xx_init_array)); priv->last_vco_curr = r82xx_init_array[0x12 - 0x05] & 0xe0; rc = r82xx_set_tv_standard(priv, TUNER_DIGITAL_TV, 0); if (rc < 0) goto err; rc = r82xx_sysfreq_sel(priv, TUNER_DIGITAL_TV); #if USE_R82XX_ENV_VARS priv->printI2C = 0; priv->filterCenter = 0; priv->haveR9 = priv->valR9 = 0; priv->haveR10L = priv->valR10L = 0; priv->haveR10H = priv->valR10H = 0; priv->haveR11L = priv->valR11L = 0; priv->haveR11H = priv->valR11H = 0; priv->haveR13L = priv->valR13L = 0; priv->haveR13H = priv->valR13H = 0; priv->haveR14L = priv->valR14L = 0; priv->haveR14H = priv->valR14H = 0; priv->haveR30H = priv->valR30H = 0; priv->haveR30L = priv->valR30L = 0; #endif priv->init_done = 1; #if USE_R82XX_ENV_VARS // read environment variables if (1) { char *pacPrintI2C; char *pacFilterCenter, *pacR9; char *pacR10Hi, *pacR10Lo, *pacR11Hi, *pacR11Lo; char *pacR13Hi, *pacR13Lo, *pacR14Hi, *pacR14Lo; char *pacR30Hi, *pacR30Lo; pacPrintI2C = getenv("RTL_R820_PRINT_I2C"); if ( pacPrintI2C ) priv->printI2C = atoi(pacPrintI2C); pacFilterCenter = getenv("RTL_R820_IF_CENTER"); if ( pacFilterCenter ) priv->filterCenter = atoi(pacFilterCenter); pacR9 = getenv("RTL_R820_R9_76"); if ( pacR9 ) { priv->haveR9 = 1; priv->valR9 = atoi(pacR9); if ( priv->valR9 > 3 ) { fprintf(stderr, "*** read R9 from environment: %d - but value should be 0 - 3 for bit [7:6]\n", priv->valR9); priv->haveR9 = 0; } fprintf(stderr, "*** read R9 from environment: %d\n", priv->valR9); } pacR10Hi = getenv("RTL_R820_R10_HI"); if ( pacR10Hi ) { priv->haveR10H = 1; priv->valR10H = atoi(pacR10Hi); if ( priv->valR10H > 15 ) { fprintf(stderr, "*** read R10_HI from environment: %d - but value should be 0 - 15 for bit [7:4]\n", priv->valR10H); priv->haveR10H = 0; } fprintf(stderr, "*** read R10_HI from environment: %d\n", priv->valR10H); } pacR10Lo = getenv("RTL_R820_R10_LO"); if ( pacR10Lo ) { priv->haveR10L = 1; priv->valR10L = atoi(pacR10Lo); if ( priv->valR10L > 15 ) { fprintf(stderr, "*** read R10_LO from environment: %d - but value should be 0 - 15 for bit [3:0]\n", priv->valR10L); priv->haveR10L = 0; } fprintf(stderr, "*** read R10_LO from environment: %d\n", priv->valR10L); } pacR11Hi = getenv("RTL_R820_R11_HI"); if ( pacR11Hi ) { priv->haveR11H = 1; priv->valR11H = atoi(pacR11Hi); if ( priv->valR11H > 7 ) { fprintf(stderr, "*** read R11_HI from environment: %d - but value should be 0 - 7 for bit [6:5]\n", priv->valR11H); priv->haveR11H = 0; } fprintf(stderr, "*** read R11_HI from environment: %d\n", priv->valR11H); } pacR11Lo = getenv("RTL_R820_R11_LO"); if ( pacR11Lo ) { priv->haveR11L = 1; priv->valR11L = atoi(pacR11Lo); if ( priv->valR11L > 15 ) { fprintf(stderr, "*** read R11_LO from environment: %d - but value should be 0 - 15 for bit [3:0]\n", priv->valR11L); priv->haveR11L = 0; } fprintf(stderr, "*** read R11_LO from environment: %d\n", priv->valR11L); } pacR13Hi = getenv("RTL_R820_R13_HI"); if ( pacR13Hi ) { priv->haveR13H = 1; priv->valR13H = atoi(pacR13Hi); if ( priv->valR13H > 15 ) { fprintf(stderr, "*** read R13_HI from environment: %d - but value should be 0 - 15 for bit [7:4]\n", priv->valR13H); priv->haveR13H = 0; } fprintf(stderr, "*** read R13_HI from environment: %d\n", priv->valR13H); } pacR13Lo = getenv("RTL_R820_R13_LO"); if ( pacR13Lo ) { priv->haveR13L = 1; priv->valR13L = atoi(pacR13Lo); if ( priv->valR13L > 15 ) { fprintf(stderr, "*** read R13_LO from environment: %d - but value should be 0 - 15 for bit [3:0]\n", priv->valR13L); priv->haveR13L = 0; } fprintf(stderr, "*** read R13_LO from environment: %d\n", priv->valR13L); } pacR14Hi = getenv("RTL_R820_R14_HI"); if ( pacR14Hi ) { priv->haveR14H = 1; priv->valR14H = atoi(pacR14Hi); if ( priv->valR14H > 15 ) { fprintf(stderr, "*** read R14_HI from environment: %d - but value should be 0 - 15 for bit [7:4]\n", priv->valR14H); priv->haveR14H = 0; } fprintf(stderr, "*** read R14_HI from environment: %d\n", priv->valR14H); } pacR14Lo = getenv("RTL_R820_R14_LO"); if ( pacR14Lo ) { priv->haveR14L = 1; priv->valR14L = atoi(pacR14Lo); if ( priv->valR14L > 15 ) { fprintf(stderr, "*** read R14_LO from environment: %d - but value should be 0 - 15 for bits [3:0]\n", priv->valR14L); priv->haveR14L = 0; } fprintf(stderr, "*** read R14_LO from environment: %d\n", priv->valR14L); } pacR30Hi = getenv("RTL_R820_R30_HI"); if ( pacR30Hi ) { priv->haveR30H = 1; priv->valR30H = atoi(pacR30Hi); if ( priv->valR30H > 3 ) { fprintf(stderr, "*** read R30_HI from environment: %d - but value should be 0 - 3 for bit [7:6]\n", priv->valR30H); priv->haveR30H = 0; } fprintf(stderr, "*** read R30_HI from environment: %d\n", priv->valR30H); } pacR30Lo = getenv("RTL_R820_R30_LO"); if ( pacR30Lo ) { priv->haveR30L = 1; priv->valR30L = atoi(pacR30Lo); if ( priv->valR30L > 63 ) { fprintf(stderr, "*** read R30_LO from environment: %d - but value should be 0 - 63 for bit [5:0]\n", priv->valR30L); priv->haveR30L = 0; } fprintf(stderr, "*** read R30_LO from environment: %d\n", priv->valR30L); } } #endif err: if (rc < 0) fprintf(stderr, "%s: failed=%d\n", __FUNCTION__, rc); return rc; } #if 0 /* Not used, for now */ static int r82xx_gpio(struct r82xx_priv *priv, int enable) { return r82xx_write_reg_mask(priv, 0x0f, enable ? 1 : 0, 0x01); } #endif ================================================ FILE: win32-qtcreator/CMakeLists.txt ================================================ project(rtlsdr) cmake_minimum_required(VERSION 2.8) # created and tested with # Qt 5.6.1 for Windows 32-bit (MinGW 4.9.2) from # https://www.qt.io/download-open-source/#section-2 # # and QtCreator 4.1.0 # # and LibUSB 1.0.20 from # https://sourceforge.net/projects/libusb/files/ # libusb-1.0.20.7z # # edit this path SET( LIBUSBBASE C:/src/_foreign/libusb-1.0.20 ) OPTION(RTL_FULL_STATIC_BUILD "Build rtl-tools fully static." ON) if(RTL_FULL_STATIC_BUILD) if (WIN32) if(MINGW) # Special MINGW stuff here # see https://cmake.org/pipermail/cmake/2012-September/051970.html # see http://stackoverflow.com/questions/13768515/how-to-do-static-linking-of-libwinpthread-1-dll-in-mingw set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -static -static-libgcc") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static -static-libgcc -static-libstdc++") set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_C_FLAGS} -static -static-libgcc -s") set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "${CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS} -static -static-libgcc -static-libstdc++ -s") endif() endif() endif() add_definitions( -DWIN32 -D_WIN32 -DNDEBUG ) include_directories( ../include ${LIBUSBBASE}/include/libusb-1.0 ) SET( LIBUSB ${LIBUSBBASE}/MinGW32/static/libusb-1.0.a ) SET( SOCKLIBS ws2_32 wsock32 ) SET( RTLLIBFILES ../include/rtl_tcp.h ../include/reg_field.h ../include/rtlsdr_i2c.h ../src/convenience/convenience.c ../src/convenience/convenience.h ../src/convenience/wavewrite.c ../src/convenience/wavewrite.h ../src/getopt/getopt.c ../src/getopt/getopt.h ../include/rtl-sdr_export.h ../include/rtl-sdr.h ../src/librtlsdr.c ../include/tuner_e4k.h ../src/tuner_e4k.c ../include/tuner_fc0012.h ../src/tuner_fc0012.c ../include/tuner_fc0013.h ../src/tuner_fc0013.c ../include/tuner_fc2580.h ../src/tuner_fc2580.c ../include/tuner_r82xx.h ../src/tuner_r82xx.c ) add_executable( rtl_test ../src/rtl_test.c ${RTLLIBFILES} ) target_link_libraries( rtl_test ${LIBUSB} ) add_executable( rtl_fm ../src/rtl_fm.c ${RTLLIBFILES} ) target_link_libraries( rtl_fm ${LIBUSB} ) add_executable( rtl_tcp ../src/rtl_tcp.c ${RTLLIBFILES} ) target_link_libraries( rtl_tcp ${LIBUSB} ${SOCKLIBS} ) add_executable( rtl_udp ../src/rtl_udp.c ${RTLLIBFILES} ) target_link_libraries( rtl_udp ${LIBUSB} ${SOCKLIBS} ) add_executable( rtl_sdr ../src/rtl_sdr.c ${RTLLIBFILES} ) target_link_libraries( rtl_sdr ${LIBUSB} ) add_executable( rtl_adsb ../src/rtl_adsb.c ${RTLLIBFILES} ) target_link_libraries( rtl_adsb ${LIBUSB} ) add_executable( rtl_power ../src/rtl_power.c ${RTLLIBFILES} ) target_link_libraries( rtl_power ${LIBUSB} ) add_executable( rtl_ir ../src/rtl_ir.c ${RTLLIBFILES} ) target_link_libraries( rtl_ir ${LIBUSB} ) add_executable( rtl_eeprom ../src/rtl_eeprom.c ${RTLLIBFILES} ) target_link_libraries( rtl_eeprom ${LIBUSB} ) if (NOT WIN32) # errors at compilation with MinGW, e.g. missing include sys/select.h add_executable( rtl_rpcd ../src/rtl_rpcd.c ../src/rtlsdr_rpc_msg.c ${RTLLIBFILES} ) target_link_libraries( rtl_rpcd ${LIBUSB} ) endif() ================================================ FILE: win32-qtcreator/README.txt ================================================ there is an outdated "How to compile new releases of librtlsdr (and tools) on Windows" at https://www.reddit.com/r/RTLSDR/comments/uce3e/how_to_compile_new_releases_of_librtlsdr_and/ unfortunately the link to the CMakeLists.txt is broken! so, i needed to find another solution .. 1) aquire and install Qt 5.5.x for Windows 32-bit (MinGW 4.9.2) from https://www.qt.io/download-open-source/#section-2 2) aquire and install QtCreator 4.0.x from same site as 1) probably this step is not necessary and you can use the qtcreator IDE from 1) 3) aquire LibUSB 1.0.20 from https://sourceforge.net/projects/libusb/files/ last tested: libusb-1.0.20.7z and place the file at C:/src/_foreign/libusb-1.0.20 or replace LIBUSBBASE path in CMakeLists.txt 4) start qtcreator and open the (modified) CMakeLists.txt configure compiler/environment and compile the resulting executables have no other dependencies than libwinpthread-1.dll from the MINGW system at C:\Qt\Qt5.5.1\Tools\mingw492_32\bin\ or C:\Qt\Qt5.5.1\5.5\mingw492_32\bin ======================================================================= opening main CMakeLists.txt in root (above win32-qtcreator) with above QT 5.5/qtcreator set RTL_STATIC_BUILD = ON set LIBUSB_FOUND = TRUE set LIBUSB_INCLUDE_DIR = C:/src/_foreign/libusb-1.0.20/include/libusb-1.0 set LIBUSB_LIBRARIES = C:/src/_foreign/libusb-1.0.20/MinGW32/static/libusb-1.0.a set THREADS_PTHREADS_INCLUDE_DIR = C:/Qt/Qt5.6.1/Tools/mingw492_32/i686-w64-mingw32/include set THREADS_PTHREADS_WIN32_LIBRARY = C:/Qt/Qt5.6.1/Tools/mingw492_32/i686-w64-mingw32/lib/libwinpthread.a the resulting executables have no other dependencies, except the freshly built librtlsdr.dll