diff --git a/.gitignore b/.gitignore index ad82be2..0bbd302 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,3 @@ -CMakeCache.txt -CMakeFiles/ -Makefile -cmake_install.cmake -compile_commands.json -nbproject/ -x64/ +build/ cmake_build/ -cmake_build_msvc/ dist/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 82f37c8..e5bf65e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required (VERSION 2.8) SET(CUBICSDR_VERSION_MAJOR "0") SET(CUBICSDR_VERSION_MINOR "1") -SET(CUBICSDR_VERSION_PATCH "6") -SET(CUBICSDR_VERSION_REL "beta-issue140") +SET(CUBICSDR_VERSION_PATCH "16") +SET(CUBICSDR_VERSION_REL "alpha") SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}-${CUBICSDR_VERSION_REL}") SET(CPACK_PACKAGE_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}") @@ -11,13 +11,13 @@ SET(CPACK_PACKAGE_VERSION_MAJOR ${CUBICSDR_VERSION_MAJOR}) SET(CPACK_PACKAGE_VERSION_MINOR ${CUBICSDR_VERSION_MINOR}) SET(CPACK_PACKAGE_VERSION_PATCH ${CUBICSDR_VERSION_PATCH}) +SET (VERSION_SUFFIX "" CACHE STRING "Add custom version suffix to CubicSDR application title.") + ADD_DEFINITIONS( - -DCUBICSDR_VERSION="${CUBICSDR_VERSION}" + -DCUBICSDR_VERSION="${CUBICSDR_VERSION}-${VERSION_SUFFIX}" ) -IF (NOT APPLE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") -ENDIF(NOT APPLE) macro(configure_files srcDir destDir globStr) @@ -37,7 +37,6 @@ macro(configure_files srcDir destDir globStr) endforeach(templateFile) endmacro(configure_files) - macro(configure_files_recurse srcDir destDir) message(STATUS "Configuring directory ${destDir}") make_directory(${destDir}) @@ -67,24 +66,42 @@ else( CMAKE_SIZEOF_VOID_P EQUAL 8 ) SET( EX_PLATFORM_NAME "x86" ) endif( CMAKE_SIZEOF_VOID_P EQUAL 8 ) -if ( WIN32 AND EX_PLATFORM EQUAL 64) - set(BUILD_X86 OFF CACHE BOOL "Copy & Build 32-bit files even if 64-bit compiler is detected.") - if (BUILD_X86) - SET( EX_PLATFORM 32 ) - SET( EX_PLATFORM_NAME "x86" ) - endif (BUILD_X86) -endif ( WIN32 AND EX_PLATFORM EQUAL 64) SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${EX_PLATFORM_NAME}) SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_BINARY_DIR}/${EX_PLATFORM_NAME}) SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_BINARY_DIR}/${EX_PLATFORM_NAME}) -find_package(OpenGL REQUIRED) +IF (MSVC) + include_directories ("${PROJECT_SOURCE_DIR}/external/wglext") + SET(FFTW_INCLUDES "${PROJECT_SOURCE_DIR}/external/fftw-3.3.4/") + SET(FFTW_LIBRARIES "${PROJECT_SOURCE_DIR}/external/fftw-3.3.4/${EX_PLATFORM}/libfftw3f-3.lib") + SET(LIQUID_INCLUDES "${PROJECT_SOURCE_DIR}/external/liquid-dsp/include/") + SET(LIQUID_LIBRARIES "${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/libliquid.lib") +ELSE (MSVC) + ADD_DEFINITIONS( + -std=c++0x + -pthread + ) +ENDIF(MSVC) -find_package(wxWidgets COMPONENTS gl core base REQUIRED) +find_package(OpenGL REQUIRED) +#IF (NOT WIN32) +find_package(FFTW REQUIRED) +find_package(Liquid REQUIRED) +include_directories(${LIQUID_INCLUDES} ${FFTW_INCLUDES}) +SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} ${LIQUID_LIBRARIES} ${FFTW_LIBRARIES}) +#ENDIF() + +find_package(wxWidgets COMPONENTS gl core propgrid adv base REQUIRED) set(wxWidgets_CONFIGURATION mswu) include(${wxWidgets_USE_FILE}) +find_package(SoapySDR "0.4.0" NO_MODULE REQUIRED) +include_directories(${SOAPY_SDR_INCLUDE_DIR}) +SET(OTHER_LIBRARIES ${SOAPY_SDR_LIBRARY} ${OTHER_LIBRARIES}) +ADD_DEFINITIONS( + -DUSE_SOAPY_SDR=1 +) IF (WIN32) set(wxWidgets_USE_STATIC ON) @@ -116,56 +133,31 @@ IF (WIN32) ENDIF (MSVC) ENDIF(USE_AUDIO_DS) - # ASIO? - #IF(USE_AUDIO_ASIO) - #ENDIF(USE_AUDIO_ASIO) - - # FFTW - include_directories ( ${PROJECT_SOURCE_DIR}/external/fftw-3.3.4 ${PROJECT_SOURCE_DIR}/external/rtl-sdr-release ) - set(FFTW_LIB libfftw3f-3) - link_directories ( ${PROJECT_SOURCE_DIR}/external/fftw-3.3.4/${EX_PLATFORM} ) - configure_files(${PROJECT_SOURCE_DIR}/external/fftw-3.3.4/${EX_PLATFORM} ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll") - - include_directories ( ${PROJECT_SOURCE_DIR}/external/liquid-dsp/include ) - set(LIQUID_LIB libliquid) - - # Haven't looked into why these are different, just explicitly including everything for now until it can be sorted neatly. - IF (MSVC) - # LIQUID - link_directories ( ${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM} ) - configure_files(${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM} ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME}/ "*.dll") - # RTL-SDR - link_directories ( ${PROJECT_SOURCE_DIR}/external/rtl-sdr-release/msvc/${EX_PLATFORM} ) - configure_files(${PROJECT_SOURCE_DIR}/external/rtl-sdr-release/msvc/${EX_PLATFORM} ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll") - ELSE (MSVC) # GCC - # LIQUID - link_directories ( ${PROJECT_SOURCE_DIR}/external/liquid-dsp/gcc/${EX_PLATFORM} ) - configure_files(${PROJECT_SOURCE_DIR}/external/liquid-dsp/gcc/${EX_PLATFORM} ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll") - # RTL-SDR - link_directories ( ${PROJECT_SOURCE_DIR}/external/rtl-sdr-release/gcc/${EX_PLATFORM} ) - configure_files(${PROJECT_SOURCE_DIR}/external/rtl-sdr-release/gcc/${EX_PLATFORM} ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll") - ENDIF(MSVC) - - # Copy DLL files to build dir - configure_files(${PROJECT_SOURCE_DIR}/external/fftw-3.3.4/${EX_PLATFORM_NAME} ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll") - + SET(USE_MINGW_PATCH OFF CACHE BOOL "Add some missing functions when compiling against mingw liquid-dsp.") + IF (USE_MINGW_PATCH) + SET(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} legacy_stdio_definitions.lib libgcc.a") + ADD_DEFINITIONS( + -DMINGW_PATCH=1 + ) + SET (GCC_LINKDIR "" CACHE STRING "") + IF (GCC_LINKDIR) + link_directories("${GCC_LINKDIR}") + ENDIF() + ENDIF() ENDIF (WIN32) IF (UNIX AND NOT APPLE) set(BUILD_DEB OFF CACHE BOOL "Build DEB") - set(RTLSDR_INCLUDE "/usr/local/include" CACHE FILEPATH "RTL-SDR Include Path") - set(RTLSDR_LIB "/usr/local/lib" CACHE FILEPATH "RTL-SDR Lib Path") - set(USE_AUDIO_PULSE ON CACHE BOOL "Use Pulse Audio") - set(USE_AUDIO_JACK OFF CACHE BOOL "Use Jack Audio") - set(USE_AUDIO_ALSA OFF CACHE BOOL "Use ALSA Audio") - set(USE_AUDIO_OSS OFF CACHE BOOL "Use OSS Audio") - include_directories(${RTLSDR_INCLUDE}) - link_directories(${RTLSDR_LIB}) - set(FFTW_LIB fftw3f) - set(LIQUID_LIB liquid) - set(OTHER_LIBRARIES dl) + SET(USE_AUDIO_PULSE ON CACHE BOOL "Use Pulse Audio") + SET(USE_AUDIO_JACK OFF CACHE BOOL "Use Jack Audio") + SET(USE_AUDIO_ALSA OFF CACHE BOOL "Use ALSA Audio") + SET(USE_AUDIO_OSS OFF CACHE BOOL "Use OSS Audio") + + SET(FFTW_LIB fftw3f) + SET(LIQUID_LIB liquid) + SET(OTHER_LIBRARIES ${OTHER_LIBRARIES} dl) IF(USE_AUDIO_PULSE) SET (OTHER_LIBRARIES ${OTHER_LIBRARIES} pulse-simple pulse) @@ -203,21 +195,19 @@ ENDIF(USE_AUDIO_OSS) ENDIF(UNIX AND NOT APPLE) IF (APPLE) - set(RTLSDR_INCLUDE "/opt/local/include" CACHE FILEPATH "RTL-SDR Include Path") - set(RTLSDR_LIB "/opt/local/lib" CACHE FILEPATH "RTL-SDR Lib Path") - include_directories(${RTLSDR_INCLUDE}) - link_directories(${RTLSDR_LIB}) - set(LIB_DIRS "${LIB_DIRS} ${RTLSDR_LIB}") + SET(CMAKE_OSX_DEPLOYMENT_TARGET, "10.10") - set(FFTW_LIB fftw3f) - set(LIQUID_LIB liquid) + SET(FFTW_LIB fftw3f) + SET(LIQUID_LIB liquid) + link_directories(/usr/local/lib) + link_directories(/opt/local/lib) ADD_DEFINITIONS( -D__MACOSX_CORE__ ) FIND_LIBRARY(COREAUDIO_LIBRARY CoreAudio) - SET (OTHER_LIBRARIES ${COREAUDIO_LIBRARY}) + SET (OTHER_LIBRARIES ${COREAUDIO_LIBRARY} ${OTHER_LIBRARIES}) set(BUNDLE_APP OFF CACHE BOOL "Bundle Application") ENDIF (APPLE) @@ -230,8 +220,9 @@ SET (cubicsdr_sources src/FrequencyDialog.cpp src/IOThread.cpp src/sdr/SDRDeviceInfo.cpp - src/sdr/SDRThread.cpp src/sdr/SDRPostThread.cpp + src/sdr/SDREnumerator.cpp + src/sdr/SoapySDRThread.h src/demod/DemodulatorPreThread.cpp src/demod/DemodulatorThread.cpp src/demod/DemodulatorWorkerThread.cpp @@ -260,6 +251,7 @@ SET (cubicsdr_sources src/visual/ScopeContext.cpp src/visual/SpectrumCanvas.cpp src/visual/WaterfallCanvas.cpp + src/visual/GainCanvas.cpp src/process/VisualProcessor.cpp src/process/ScopeVisualProcessor.cpp src/process/SpectrumVisualProcessor.cpp @@ -267,6 +259,8 @@ SET (cubicsdr_sources src/process/FFTDataDistributor.cpp src/process/SpectrumVisualDataThread.cpp src/ui/GLPanel.cpp + src/forms/SDRDevices/SDRDevices.cpp + src/forms/SDRDevices/SDRDevicesForm.cpp external/rtaudio/RtAudio.cpp external/lodepng/lodepng.cpp external/tinyxml/tinyxml.cpp @@ -284,8 +278,9 @@ SET (cubicsdr_headers src/FrequencyDialog.h src/IOThread.h src/sdr/SDRDeviceInfo.h - src/sdr/SDRThread.h src/sdr/SDRPostThread.h + src/sdr/SDREnumerator.h + src/sdr/SoapySDRThread.cpp src/demod/DemodulatorPreThread.h src/demod/DemodulatorThread.h src/demod/DemodulatorWorkerThread.h @@ -316,6 +311,7 @@ SET (cubicsdr_headers src/visual/ScopeContext.h src/visual/SpectrumCanvas.h src/visual/WaterfallCanvas.h + src/visual/GainCanvas.h src/process/VisualProcessor.h src/process/ScopeVisualProcessor.h src/process/SpectrumVisualProcessor.h @@ -327,6 +323,8 @@ SET (cubicsdr_headers src/ui/UITestCanvas.h src/ui/UITestContext.cpp src/ui/UITestContext.h + src/forms/SDRDevices/SDRDevices.h + src/forms/SDRDevices/SDRDevicesForm.h external/rtaudio/RtAudio.h external/lodepng/lodepng.h external/tinyxml/tinyxml.h @@ -349,21 +347,23 @@ SET (cubicsdr_headers set(REG_EXT "[^/]*([.]cpp|[.]c|[.]h|[.]hpp)$") -SOURCE_GROUP("Base" REGULAR_EXPRESSION src/${REG_EXT}) -SOURCE_GROUP("SDR" REGULAR_EXPRESSION src/sdr/${REG_EXT}) -SOURCE_GROUP("Demodulator" REGULAR_EXPRESSION src/demod/${REG_EXT}) -SOURCE_GROUP("Audio" REGULAR_EXPRESSION src/audio/${REG_EXT}) -SOURCE_GROUP("Utility" REGULAR_EXPRESSION src/util/${REG_EXT}) -SOURCE_GROUP("Panel" REGULAR_EXPRESSION src/panel/${REG_EXT}) -SOURCE_GROUP("Visual" REGULAR_EXPRESSION src/visual/${REG_EXT}) -SOURCE_GROUP("Process" REGULAR_EXPRESSION src/process/${REG_EXT}) -SOURCE_GROUP("UI" REGULAR_EXPRESSION src/ui/${REG_EXT}) -SOURCE_GROUP("_ext-RTAudio" REGULAR_EXPRESSION external/rtaudio/.*${REG_EXT}) -SOURCE_GROUP("_ext-LodePNG" REGULAR_EXPRESSION external/lodepng/.*${REG_EXT}) -SOURCE_GROUP("_ext-TinyXML" REGULAR_EXPRESSION external/tinyxml/.*${REG_EXT}) -SOURCE_GROUP("_ext-CubicVR2" REGULAR_EXPRESSION external/cubicvr2/.*${REG_EXT}) +SOURCE_GROUP("Base" REGULAR_EXPRESSION "src/${REG_EXT}") +SOURCE_GROUP("Forms\\SDRDevices" REGULAR_EXPRESSION "src/forms/SDRDevices/${REG_EXT}") +SOURCE_GROUP("SDR" REGULAR_EXPRESSION "src/sdr/${REG_EXT}") +SOURCE_GROUP("Demodulator" REGULAR_EXPRESSION "src/demod/${REG_EXT}") +SOURCE_GROUP("Audio" REGULAR_EXPRESSION "src/audio/${REG_EXT}") +SOURCE_GROUP("Utility" REGULAR_EXPRESSION "src/util/${REG_EXT}") +SOURCE_GROUP("Visual" REGULAR_EXPRESSION "src/visual/${REG_EXT}") +SOURCE_GROUP("Panel" REGULAR_EXPRESSION "src/panel/${REG_EXT}") +SOURCE_GROUP("Process" REGULAR_EXPRESSION "src/process/${REG_EXT}") +SOURCE_GROUP("UI" REGULAR_EXPRESSION "src/ui/${REG_EXT}") +SOURCE_GROUP("_ext-RTAudio" REGULAR_EXPRESSION "external/rtaudio/.*${REG_EXT}") +SOURCE_GROUP("_ext-LodePNG" REGULAR_EXPRESSION "external/lodepng/.*${REG_EXT}") +SOURCE_GROUP("_ext-TinyXML" REGULAR_EXPRESSION "external/tinyxml/.*${REG_EXT}") +SOURCE_GROUP("_ext-CubicVR2" REGULAR_EXPRESSION "external/cubicvr2/.*${REG_EXT}") include_directories ( + ${PROJECT_SOURCE_DIR}/src/forms/SDRDevices ${PROJECT_SOURCE_DIR}/src/sdr ${PROJECT_SOURCE_DIR}/src/demod ${PROJECT_SOURCE_DIR}/src/audio @@ -379,18 +379,6 @@ include_directories ( ${PROJECT_SOURCE_DIR}/external/cubicvr2/math ) -IF (MSVC) -include_directories ( - ${PROJECT_SOURCE_DIR}/external/wglext -) - -ELSE (MSVC) - ADD_DEFINITIONS( - -std=c++0x - -pthread - ) -ENDIF(MSVC) - set(RES_FILES "") if(MINGW OR MSVC) set(RES_FILES ${PROJECT_SOURCE_DIR}/cubicsdr.rc) @@ -409,8 +397,12 @@ IF (NOT BUNDLE_APP) configure_files(${PROJECT_SOURCE_DIR}/font ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.fnt") configure_files(${PROJECT_SOURCE_DIR}/font ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.png") configure_files(${PROJECT_SOURCE_DIR}/icon ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} CubicSDR.ico) + IF(MSVC) + configure_files(${PROJECT_SOURCE_DIR}/external/fftw-3.3.4/${EX_PLATFORM}/ ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll") + configure_files(${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/ ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll") + ENDIF() add_executable(CubicSDR ${cubicsdr_sources} ${cubicsdr_headers} ${RES_FILES}) - target_link_libraries(CubicSDR rtlsdr ${LIQUID_LIB} ${FFTW_LIB} ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} ${OTHER_LIBRARIES}) + target_link_libraries(CubicSDR ${LIQUID_LIB} ${FFTW_LIB} ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} ${OTHER_LIBRARIES}) ENDIF (NOT BUNDLE_APP) IF (MSVC) @@ -428,7 +420,7 @@ ENDIF(MSVC) IF (APPLE) ADD_DEFINITIONS( -DHAVE_TYPE_TRAITS=1 - -mmacosx-version-min=10.9 + -mmacosx-version-min=10.10 ) ENDIF(APPLE) @@ -436,6 +428,15 @@ IF (APPLE AND BUNDLE_APP) PROJECT(CubicSDR) SET(MACOSX_BUNDLE_BUNDLE_NAME CubicSDR) + set(BUNDLE_SOAPY_MODS OFF CACHE BOOL "Bundle local SoapySDR modules") + set(BUNDLE_MIR_SDR OFF CACHE BOOL "Bundle mir_sdr for personal use only -- do not distribute.") + + IF (BUNDLE_SOAPY_MODS) + ADD_DEFINITIONS( + -DBUNDLE_SOAPY_MODS=1 + ) + ENDIF() + ADD_DEFINITIONS( -std=c++0x -pthread @@ -477,9 +478,9 @@ IF (APPLE AND BUNDLE_APP) ${PROJECT_SOURCE_DIR}/icon/CubicSDR.icns PROPERTIES MACOSX_PACKAGE_LOCATION Resources - ) - - target_link_libraries(CubicSDR rtlsdr ${LIQUID_LIB} ${FFTW_LIB} ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} ${OTHER_LIBRARIES}) + ) + + target_link_libraries(CubicSDR ${LIQUID_LIB} ${FFTW_LIB} ${wxWidgets_LIBRARIES} ${OPENGL_LIBRARIES} ${OTHER_LIBRARIES}) SET_TARGET_PROPERTIES(CubicSDR PROPERTIES MACOSX_BUNDLE TRUE) SET_TARGET_PROPERTIES(CubicSDR PROPERTIES @@ -489,16 +490,50 @@ IF (APPLE AND BUNDLE_APP) # MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}" MACOSX_BUNDLE_GUI_IDENTIFIER "com.cubicproductions.cubicsdr" MACOSX_BUNDLE_ICON_FILE CubicSDR.icns - ) + ) SET(APPS "${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME}/CubicSDR.app") # SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) # SET(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) + + IF (BUNDLE_SOAPY_MODS) + + message(STATUS "SOAPY_ROOT: ${SOAPY_SDR_ROOT}") + file(GLOB SOAPY_MODS ${SOAPY_SDR_ROOT}/lib/SoapySDR/modules/*.so) + + FOREACH(SOAPY_MOD_FILE ${SOAPY_MODS}) + INSTALL( FILES "${SOAPY_MOD_FILE}" + DESTINATION "${APPS}/Contents/MacOS/modules" + COMPONENT Runtime + ) + ENDFOREACH() + + ENDIF(BUNDLE_SOAPY_MODS) INSTALL(CODE " - SET(BU_COPY_FULL_FRAMEWORK_CONTENTS ON) + SET(BU_COPY_FULL_FRAMEWORK_CONTENTS ON) include(BundleUtilities) - fixup_bundle(\"${APPS}\" \"\" \"${RTLSDR_LIB}\") + fixup_bundle(\"${APPS}\" \"\" \"/usr/local/lib\") + " COMPONENT Runtime) + + IF (BUNDLE_SOAPY_MODS) + FOREACH(SOAPY_MOD_FILE ${SOAPY_MODS}) + GET_FILENAME_COMPONENT(SOAPY_MOD_NAME ${SOAPY_MOD_FILE} NAME) + IF(NOT BUNDLE_MIR_SDR) # prevent inclusion of libmirsdrapi-rsp.so + IF(${SOAPY_MOD_NAME} STREQUAL "libsdrPlaySupport.so") + message(STATUS "Excluding libsdrPlaySupport.so") + CONTINUE() + ELSE() + message(STATUS "Bundling ${SOAPY_MOD_NAME} from ${SOAPY_MOD_FILE}") + ENDIF() + ENDIF() + INSTALL(CODE " + fixup_bundle(\"${APPS}\" \"${APPS}/Contents/MacOS/modules/${SOAPY_MOD_NAME}\" \"/usr/local/lib\") + " COMPONENT Runtime) + ENDFOREACH() + ENDIF(BUNDLE_SOAPY_MODS) + + INSTALL(CODE " VERIFY_APP(\"${APPS}\") " COMPONENT Runtime) @@ -511,6 +546,8 @@ IF (APPLE AND BUNDLE_APP) ENDIF (APPLE AND BUNDLE_APP) IF (WIN32 AND BUILD_INSTALLER) + set(BUNDLE_SOAPY_MODS OFF CACHE BOOL "Bundle local SoapySDR modules") + set(CPACK_GENERATOR NSIS) set(CPACK_PACKAGE_NAME "CubicSDR") set(CPACK_PACKAGE_VENDOR "cubicsdr.com") @@ -533,6 +570,7 @@ IF (WIN32 AND BUILD_INSTALLER) set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".") install(TARGETS CubicSDR RUNTIME DESTINATION .) + install(FILES ${PROJECT_SOURCE_DIR}/font/vera_sans_mono12.fnt ${PROJECT_SOURCE_DIR}/font/vera_sans_mono16.fnt @@ -548,20 +586,20 @@ IF (WIN32 AND BUILD_INSTALLER) ${PROJECT_SOURCE_DIR}/font/vera_sans_mono48_0.png ${PROJECT_SOURCE_DIR}/icon/CubicSDR.ico ${PROJECT_SOURCE_DIR}/external/fftw-3.3.4/${EX_PLATFORM}/libfftw3f-3.dll - DESTINATION .) - IF (MSVC) - install(FILES ${PROJECT_SOURCE_DIR}/external/liquid-dsp/msvc/${EX_PLATFORM}/libliquid.dll - ${PROJECT_SOURCE_DIR}/external/rtl-sdr-release/msvc/${EX_PLATFORM}/rtlsdr.dll - ${PROJECT_SOURCE_DIR}/external/rtl-sdr-release/msvc/${EX_PLATFORM}/libusb-1.0.dll DESTINATION .) - ELSE (MSVC) - install(FILES - ${PROJECT_SOURCE_DIR}/external/liquid-dsp/gcc/${EX_PLATFORM}/libliquid.dll - ${PROJECT_SOURCE_DIR}/external/rtl-sdr-release/gcc/${EX_PLATFORM}/librtlsdr.dll - ${PROJECT_SOURCE_DIR}/external/rtl-sdr-release/gcc/${EX_PLATFORM}/libusb-1.0.dll - DESTINATION .) - ENDIF(MSVC) + + IF (BUNDLE_SOAPY_MODS) + ADD_DEFINITIONS( + -DBUNDLE_SOAPY_MODS=1 + ) + file(GLOB SOAPY_BINS ${SOAPY_SDR_ROOT}/bin/*.dll) + file(GLOB SOAPY_MODS ${SOAPY_SDR_ROOT}/lib/SoapySDR/modules/*.dll) + message(STATUS "SOAPY_BINS: ${SOAPY_BINS}") + message(STATUS "SOAPY_MODS: ${SOAPY_MODS}") + install(FILES ${SOAPY_BINS} DESTINATION .) + install(FILES ${SOAPY_MODS} DESTINATION modules) + ENDIF(BUNDLE_SOAPY_MODS) IF(MSVC AND EX_PLATFORM EQUAL 32) install(FILES @@ -572,8 +610,8 @@ IF (WIN32 AND BUILD_INSTALLER) set(CPACK_PACKAGE_EXECUTABLES CubicSDR "CubicSDR") IF (MSVC) - install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/external/msvc/${EX_PLATFORM_NAME}/vcredist_${EX_PLATFORM_NAME}.exe DESTINATION vc_redist) - set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\vc_redist\\\\vcredist_${EX_PLATFORM_NAME}.exe\\\" /q:a'") + install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/external/msvc/${EX_PLATFORM_NAME}/vc_redist.${EX_PLATFORM_NAME}.exe DESTINATION vc_redist) + set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\vc_redist\\\\vc_redist.${EX_PLATFORM_NAME}.exe\\\" /q:a'") ENDIF (MSVC) @@ -583,10 +621,10 @@ ENDIF (WIN32 AND BUILD_INSTALLER) IF (UNIX AND BUILD_DEB) - set(CPACK_GENERATOR DEB) + set(CPACK_GENERATOR DEB) set(CPACK_PACKAGE_NAME "CubicSDR") - SET(CPACK_DEBIAN_PACKAGE_DEPENDS " librtlsdr0, libfftw3-single3, libwxgtk3.0-0, libpulse0") - SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Charles J. Cliffe ") + SET(CPACK_DEBIAN_PACKAGE_DEPENDS " libfftw3-single3, libwxgtk3.0-0, libpulse0") + SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Charles J. Cliffe ") SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "CubicSDR Software Defined Radio application v${CUBICSDR_VERSION}") SET(CPACK_DEBIAN_PACKAGE_SECTION "comm") SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE") diff --git a/README.md b/README.md index 1c07547..db76b87 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,16 @@ CubicSDR Cross-Platform Software-Defined Radio Application +Please see the [CubicSDR GitHub Wiki](https://github.com/cjcliffe/CubicSDR/wiki) for build instructions. + Utilizes: -------- - - liquid-dsp (http://liquidsdr.org/ https://github.com/jgaeddert/liquid-dsp) - - FFTW (http://www.fftw.org/ https://github.com/FFTW/fftw3) - - RtAudio (http://www.music.mcgill.ca/~gary/rtaudio/ http://github.com/thestk/rtaudio/) - - Osmocom RTLSDR (http://sdr.osmocom.org/trac/wiki/rtl-sdr) + - liquid-dsp (http://liquidsdr.org/ -- https://github.com/jgaeddert/liquid-dsp) + - SoapySDR (http://www.pothosware.com/ -- https://github.com/pothosware/SoapySDR) + - FFTW (http://www.fftw.org/ -- https://github.com/FFTW/fftw3) + - RtAudio (http://www.music.mcgill.ca/~gary/rtaudio/ -- http://github.com/thestk/rtaudio/) - LodePNG (http://lodev.org/lodepng/) - - BMFont (http://www.angelcode.com/ http://www.angelcode.com/products/bmfont/) + - BMFont (http://www.angelcode.com/ -- http://www.angelcode.com/products/bmfont/) - Bitstream Vera font (http://en.wikipedia.org/wiki/Bitstream_Vera) - OpenGL (https://www.opengl.org/) - wxWidgets (https://www.wxwidgets.org/) @@ -20,9 +22,20 @@ Features and Status: -------------------- - Simple UI - Devices - - [x] RTL-SDR - - [ ] rtl_tcp client - - [ ] gr-osmosdr + - [x] SoapySDR Device support (known working checked) + - [x] SoapySDRPlay for SDRPlay (Maintained by C.J.) + - [x] SoapyRTLSDR for RTL-SDR (Maintained by C.J.) + - [x] SoapyHackRF for HackRF + - [x] SoapyBladeRF for BladeRF + - [ ] SoapyUHD for Ettus USRP (untested) + - [x] SoapyRemote, use any SoapySDR Device via network (works on Pi) + - [ ] SoapyAirSpy (WIP by C.J.) + - [ ] SoapyAudio (WIP by C.J.) + - [x] SoapyOsmo for GrOsmoSDR devices + - [ ] OsmoSDR + - [ ] MiriSDR + - [ ] RFSpace + - [x] AirSpy - Basic Features - [x] Device Selection - [x] Bandwidth @@ -30,12 +43,15 @@ Features and Status: - [x] Load/Save session - [x] Audio sample rate - [x] Device PPM + - [x] Waterfall speed + - [x] Spectrum average speed + - [x] Gain Controls + - [ ] Bookmarks + - [ ] History - [ ] Default preferences - [ ] Audio defaults - [x] Device defaults - - [ ] Bookmarks - - [ ] History - - [ ] Run as rtl_tcp server and visualize control + - [ ] Run any device as rtl_tcp server and visualize control - Neat Visuals - [ ] 2D visuals - [x] Y Scope @@ -59,7 +75,7 @@ Features and Status: - [x] USB - [x] DSB - [x] I/Q - - [ ] Controls + - [x] Controls - [x] Display Frequency and allow manual adjustments - [x] Allow selection of demodulation type - [x] Display separate zoomed-in view of current waterfall and spectrum, allow adjustments @@ -68,8 +84,6 @@ Features and Status: - [x] Volume control - [x] Direct frequency input - [x] Mute - - [x] Waterfall speed - - [ ] RTL-SDR Gain - Basic Input Controls - [x] Drag spectrum to change center frequency - [x] Hold shift and click on waterfall to create a new demodulator @@ -125,7 +139,7 @@ Features and Status: - [ ] Update visuals to OpenGL 3.x / OpenGL ES - [x] Resolve constant refresh on visuals that don't change often - [ ] Resolve all driver/platform vertical sync issues - - [ ] Group and divide IQ data distribution workload instead of 100% distribution per instance + - [x] Group and divide IQ data distribution workload instead of 100% distribution per instance Advanced Goals and ideas: @@ -146,19 +160,19 @@ Advanced Goals and ideas: - Visual outputs? * Take control of additional devices and spawning new demodulators (i.e. trunkers) * Script manager / live editor - * Provide scriptable liquid-dsp modulation for trancievers? + * Provide scriptable liquid-dsp modulation for transceivers? * Allow scripts to launch/run headless (no UI) - "PVR" like mode with waterfall time shifting - L/R and surround-sound balance settings for separating and listening to mono streams - Add tool for converting decimated I/Q recording to video - - Select video features such as title/demodulation/scope/waterfall/etc + - Select video features such as title/demodulation/scope/waterfall/etc. - Render to video from GL frames->ffmpeg/mencoder /w demodulated audio - - Accessability / Control + - Accessibility / Control - USB/MIDI control surfaces - Joystick / gamepad input - Vibration / force-feeback - - Investigate compilation via emscripten using rtl_tcp for input - - Create web server+rtl_tcp bundle for embedded devices + - Investigate compilation via emscripten using SoapyRemote for input + - Create web server+SoapyRemote bundle for embedded devices - Use emscripten compiled CubicSDR via embedded web server Target Platforms: diff --git a/cmake/Modules/FindFFTW.cmake b/cmake/Modules/FindFFTW.cmake new file mode 100644 index 0000000..0a4c607 --- /dev/null +++ b/cmake/Modules/FindFFTW.cmake @@ -0,0 +1,22 @@ +# - Find FFTW +# Find the native FFTW includes and library +# +# FFTW_INCLUDES - where to find fftw3.h +# FFTW_LIBRARIES - List of libraries when using FFTW. +# FFTW_FOUND - True if FFTW found. + +if (FFTW_INCLUDES) + # Already in cache, be silent + set (FFTW_FIND_QUIETLY TRUE) +endif (FFTW_INCLUDES) + +find_path (FFTW_INCLUDES fftw3.h) + +find_library (FFTW_LIBRARIES NAMES fftw3) + +# handle the QUIETLY and REQUIRED arguments and set FFTW_FOUND to TRUE if +# all listed variables are TRUE +include (FindPackageHandleStandardArgs) +find_package_handle_standard_args (FFTW DEFAULT_MSG FFTW_LIBRARIES FFTW_INCLUDES) + +#mark_as_advanced (FFTW_LIBRARIES FFTW_INCLUDES) diff --git a/cmake/Modules/FindLiquid.cmake b/cmake/Modules/FindLiquid.cmake new file mode 100644 index 0000000..5ce9d0d --- /dev/null +++ b/cmake/Modules/FindLiquid.cmake @@ -0,0 +1,22 @@ +# - Find LIQUID +# Find the native LIQUID includes and library +# +# LIQUID_INCLUDES - where to find LIQUID.h +# LIQUID_LIBRARIES - List of libraries when using LIQUID. +# LIQUID_FOUND - True if LIQUID found. + +if (LIQUID_INCLUDES) + # Already in cache, be silent + set (LIQUID_FIND_QUIETLY TRUE) +endif (LIQUID_INCLUDES) + +find_path (LIQUID_INCLUDES liquid/liquid.h) + +find_library (LIQUID_LIBRARIES NAMES liquid) + +# handle the QUIETLY and REQUIRED arguments and set LIQUID_FOUND to TRUE if +# all listed variables are TRUE +include (FindPackageHandleStandardArgs) +find_package_handle_standard_args (LIQUID DEFAULT_MSG LIQUID_LIBRARIES LIQUID_INCLUDES) + +#mark_as_advanced (LIQUID_LIBRARIES LIQUID_INCLUDES) diff --git a/external/fftw-3.3.4/64/libfftw3f-3.exp b/external/fftw-3.3.4/64/libfftw3f-3.exp index f7fe72a..c300e32 100644 Binary files a/external/fftw-3.3.4/64/libfftw3f-3.exp and b/external/fftw-3.3.4/64/libfftw3f-3.exp differ diff --git a/external/fftw-3.3.4/64/libfftw3f-3.lib b/external/fftw-3.3.4/64/libfftw3f-3.lib new file mode 100644 index 0000000..0940a86 Binary files /dev/null and b/external/fftw-3.3.4/64/libfftw3f-3.lib differ diff --git a/external/msvc/x64/vc_redist.x64.exe b/external/msvc/x64/vc_redist.x64.exe new file mode 100644 index 0000000..048fda2 Binary files /dev/null and b/external/msvc/x64/vc_redist.x64.exe differ diff --git a/external/msvc/x64/vcredist_x64.exe b/external/msvc/x64/vcredist_x64.exe deleted file mode 100644 index 5473640..0000000 Binary files a/external/msvc/x64/vcredist_x64.exe and /dev/null differ diff --git a/external/msvc/x86/vc_redist.x86.exe b/external/msvc/x86/vc_redist.x86.exe new file mode 100644 index 0000000..caea04a Binary files /dev/null and b/external/msvc/x86/vc_redist.x86.exe differ diff --git a/external/msvc/x86/vcredist_x86.exe b/external/msvc/x86/vcredist_x86.exe deleted file mode 100644 index b795578..0000000 Binary files a/external/msvc/x86/vcredist_x86.exe and /dev/null differ diff --git a/external/rtl-sdr-release/AUTHORS b/external/rtl-sdr-release/AUTHORS deleted file mode 100644 index 36da22f..0000000 --- a/external/rtl-sdr-release/AUTHORS +++ /dev/null @@ -1,4 +0,0 @@ -Steve Markgraf -Dimitri Stolnikov -Hoernchen -Kyle Keen diff --git a/external/rtl-sdr-release/COPYING b/external/rtl-sdr-release/COPYING deleted file mode 100644 index d511905..0000000 --- a/external/rtl-sdr-release/COPYING +++ /dev/null @@ -1,339 +0,0 @@ - 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. diff --git a/external/rtl-sdr-release/COPYING.libusbx b/external/rtl-sdr-release/COPYING.libusbx deleted file mode 100644 index 5ab7695..0000000 --- a/external/rtl-sdr-release/COPYING.libusbx +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 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. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -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 and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, 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 library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete 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 distribute a copy of this License along with the -Library. - - 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 Library or any portion -of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -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 Library, 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 Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you 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. - - If distribution of 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 satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be 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. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library 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. - - 9. 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 Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -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 with -this License. - - 11. 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 Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library 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 Library. - -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. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library 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. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser 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 Library -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 Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -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 - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "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 -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. 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 LIBRARY 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 -LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. 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 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. - - This 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 this library; 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. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/external/rtl-sdr-release/README b/external/rtl-sdr-release/README deleted file mode 100644 index 7f04203..0000000 --- a/external/rtl-sdr-release/README +++ /dev/null @@ -1,6 +0,0 @@ -rtl-sdr -turns your Realtek RTL2832 based DVB dongle into a SDR receiver -====================================================================== - -For more information see: -http://sdr.osmocom.org/trac/wiki/rtl-sdr diff --git a/external/rtl-sdr-release/README.windows.txt b/external/rtl-sdr-release/README.windows.txt deleted file mode 100644 index 3e0dd4b..0000000 --- a/external/rtl-sdr-release/README.windows.txt +++ /dev/null @@ -1,10 +0,0 @@ -The Windows build is using -https://github.com/libusbx/libusbx 33ba1231a1b07425eaa83935f84b2e4b7f904f35 -and -http://sources.redhat.com/pthreads-win32/ cvs -See the corresponding COPYING* files for the license text. - -You might need to grab the runtime from -http://www.microsoft.com/en-us/download/details.aspx?id=8328 -or -http://www.microsoft.com/en-us/download/details.aspx?id=13523 \ No newline at end of file diff --git a/external/rtl-sdr-release/gcc/32/convenience_static.lib b/external/rtl-sdr-release/gcc/32/convenience_static.lib deleted file mode 100644 index 859a8fb..0000000 Binary files a/external/rtl-sdr-release/gcc/32/convenience_static.lib and /dev/null differ diff --git a/external/rtl-sdr-release/gcc/32/libusb-1.0.dll b/external/rtl-sdr-release/gcc/32/libusb-1.0.dll deleted file mode 100644 index bce94fc..0000000 Binary files a/external/rtl-sdr-release/gcc/32/libusb-1.0.dll and /dev/null differ diff --git a/external/rtl-sdr-release/gcc/32/rtlsdr.dll b/external/rtl-sdr-release/gcc/32/rtlsdr.dll deleted file mode 100644 index 40cff2b..0000000 Binary files a/external/rtl-sdr-release/gcc/32/rtlsdr.dll and /dev/null differ diff --git a/external/rtl-sdr-release/gcc/32/rtlsdr.lib b/external/rtl-sdr-release/gcc/32/rtlsdr.lib deleted file mode 100644 index 26de5d5..0000000 Binary files a/external/rtl-sdr-release/gcc/32/rtlsdr.lib and /dev/null differ diff --git a/external/rtl-sdr-release/gcc/32/rtlsdr_static.lib b/external/rtl-sdr-release/gcc/32/rtlsdr_static.lib deleted file mode 100644 index 5505ea8..0000000 Binary files a/external/rtl-sdr-release/gcc/32/rtlsdr_static.lib and /dev/null differ diff --git a/external/rtl-sdr-release/gcc/64/libconvenience_static.a b/external/rtl-sdr-release/gcc/64/libconvenience_static.a deleted file mode 100644 index 59f0d7d..0000000 Binary files a/external/rtl-sdr-release/gcc/64/libconvenience_static.a and /dev/null differ diff --git a/external/rtl-sdr-release/gcc/64/liblibgetopt_static.a b/external/rtl-sdr-release/gcc/64/liblibgetopt_static.a deleted file mode 100644 index c9c9637..0000000 Binary files a/external/rtl-sdr-release/gcc/64/liblibgetopt_static.a and /dev/null differ diff --git a/external/rtl-sdr-release/gcc/64/librtlsdr.dll b/external/rtl-sdr-release/gcc/64/librtlsdr.dll deleted file mode 100644 index 5ef522a..0000000 Binary files a/external/rtl-sdr-release/gcc/64/librtlsdr.dll and /dev/null differ diff --git a/external/rtl-sdr-release/gcc/64/librtlsdr.dll.a b/external/rtl-sdr-release/gcc/64/librtlsdr.dll.a deleted file mode 100644 index 2e0542e..0000000 Binary files a/external/rtl-sdr-release/gcc/64/librtlsdr.dll.a and /dev/null differ diff --git a/external/rtl-sdr-release/gcc/64/librtlsdr_static.a b/external/rtl-sdr-release/gcc/64/librtlsdr_static.a deleted file mode 100644 index b9f0342..0000000 Binary files a/external/rtl-sdr-release/gcc/64/librtlsdr_static.a and /dev/null differ diff --git a/external/rtl-sdr-release/gcc/64/libusb-1.0.dll b/external/rtl-sdr-release/gcc/64/libusb-1.0.dll deleted file mode 100644 index 79dba9e..0000000 Binary files a/external/rtl-sdr-release/gcc/64/libusb-1.0.dll and /dev/null differ diff --git a/external/rtl-sdr-release/msvc/32/libusb-1.0.dll b/external/rtl-sdr-release/msvc/32/libusb-1.0.dll deleted file mode 100644 index 6fc1fbd..0000000 Binary files a/external/rtl-sdr-release/msvc/32/libusb-1.0.dll and /dev/null differ diff --git a/external/rtl-sdr-release/msvc/32/rtlsdr.dll b/external/rtl-sdr-release/msvc/32/rtlsdr.dll deleted file mode 100644 index 3815341..0000000 Binary files a/external/rtl-sdr-release/msvc/32/rtlsdr.dll and /dev/null differ diff --git a/external/rtl-sdr-release/msvc/32/rtlsdr.exp b/external/rtl-sdr-release/msvc/32/rtlsdr.exp deleted file mode 100644 index b50f96c..0000000 Binary files a/external/rtl-sdr-release/msvc/32/rtlsdr.exp and /dev/null differ diff --git a/external/rtl-sdr-release/msvc/32/rtlsdr.lib b/external/rtl-sdr-release/msvc/32/rtlsdr.lib deleted file mode 100644 index d0634f9..0000000 Binary files a/external/rtl-sdr-release/msvc/32/rtlsdr.lib and /dev/null differ diff --git a/external/rtl-sdr-release/msvc/32/rtlsdr_static.lib b/external/rtl-sdr-release/msvc/32/rtlsdr_static.lib deleted file mode 100644 index a5ba312..0000000 Binary files a/external/rtl-sdr-release/msvc/32/rtlsdr_static.lib and /dev/null differ diff --git a/external/rtl-sdr-release/msvc/64/libusb-1.0.dll b/external/rtl-sdr-release/msvc/64/libusb-1.0.dll deleted file mode 100644 index 4e70c38..0000000 Binary files a/external/rtl-sdr-release/msvc/64/libusb-1.0.dll and /dev/null differ diff --git a/external/rtl-sdr-release/msvc/64/rtlsdr.dll b/external/rtl-sdr-release/msvc/64/rtlsdr.dll deleted file mode 100644 index 26f50d6..0000000 Binary files a/external/rtl-sdr-release/msvc/64/rtlsdr.dll and /dev/null differ diff --git a/external/rtl-sdr-release/msvc/64/rtlsdr.exp b/external/rtl-sdr-release/msvc/64/rtlsdr.exp deleted file mode 100644 index a63b964..0000000 Binary files a/external/rtl-sdr-release/msvc/64/rtlsdr.exp and /dev/null differ diff --git a/external/rtl-sdr-release/msvc/64/rtlsdr.lib b/external/rtl-sdr-release/msvc/64/rtlsdr.lib deleted file mode 100644 index 0e38fac..0000000 Binary files a/external/rtl-sdr-release/msvc/64/rtlsdr.lib and /dev/null differ diff --git a/external/rtl-sdr-release/msvc/64/rtlsdr_static.lib b/external/rtl-sdr-release/msvc/64/rtlsdr_static.lib deleted file mode 100644 index 4684051..0000000 Binary files a/external/rtl-sdr-release/msvc/64/rtlsdr_static.lib and /dev/null differ diff --git a/external/rtl-sdr-release/rtl-sdr.h b/external/rtl-sdr-release/rtl-sdr.h deleted file mode 100644 index 489e117..0000000 --- a/external/rtl-sdr-release/rtl-sdr.h +++ /dev/null @@ -1,378 +0,0 @@ -/* - * 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 - -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); - -RTLSDR_API int rtlsdr_open(rtlsdr_dev_t **dev, uint32_t index); - -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); - -RTLSDR_API int rtlsdr_set_center_freq(rtlsdr_dev_t *dev, uint32_t freq); - -/*! - * 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); - -/*! - * 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) - */ -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, - 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); - -/*! - * Get actual gain the device is configured to. - * - * \param dev the device handle given by rtlsdr_open() - * \return 0 on error, gain in tenths of a dB, 115 means 11.5 dB. - */ -RTLSDR_API int rtlsdr_get_tuner_gain(rtlsdr_dev_t *dev); - -/*! - * 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 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); - -/*! - * 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); - -/* streaming functions */ - -RTLSDR_API int rtlsdr_reset_buffer(rtlsdr_dev_t *dev); - -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. - * - * \param dev the device handle given by rtlsdr_open() - * \return 0 on success - */ -RTLSDR_API int rtlsdr_cancel_async(rtlsdr_dev_t *dev); - -#ifdef __cplusplus -} -#endif - -#endif /* __RTL_SDR_H */ diff --git a/external/rtl-sdr-release/rtl-sdr_export.h b/external/rtl-sdr-release/rtl-sdr_export.h deleted file mode 100644 index 69e178d..0000000 --- a/external/rtl-sdr-release/rtl-sdr_export.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 */ diff --git a/src/AppConfig.cpp b/src/AppConfig.cpp index f1a716d..4d2a360 100644 --- a/src/AppConfig.cpp +++ b/src/AppConfig.cpp @@ -2,9 +2,7 @@ #include "CubicSDR.h" DeviceConfig::DeviceConfig() : deviceId("") { - iqSwap.store(0); ppm.store(0); - directSampling.store(false); offset.store(0); } @@ -20,14 +18,6 @@ int DeviceConfig::getPPM() { return ppm.load(); } -void DeviceConfig::setDirectSampling(int mode) { - directSampling.store(mode); -} - -int DeviceConfig::getDirectSampling() { - return directSampling.load(); -} - void DeviceConfig::setOffset(long long offset) { this->offset.store(offset); } @@ -36,14 +26,6 @@ long long DeviceConfig::getOffset() { return offset.load(); } -void DeviceConfig::setIQSwap(bool iqSwap) { - this->iqSwap.store(iqSwap); -} - -bool DeviceConfig::getIQSwap() { - return iqSwap.load(); -} - void DeviceConfig::setDeviceId(std::string deviceId) { busy_lock.lock(); this->deviceId = deviceId; @@ -64,8 +46,6 @@ void DeviceConfig::save(DataNode *node) { busy_lock.lock(); *node->newChild("id") = deviceId; *node->newChild("ppm") = (int)ppm; - *node->newChild("iq_swap") = iqSwap; - *node->newChild("direct_sampling") = directSampling; *node->newChild("offset") = offset; busy_lock.unlock(); } @@ -79,32 +59,6 @@ void DeviceConfig::load(DataNode *node) { setPPM(ppmValue); std::cout << "Loaded PPM for device '" << deviceId << "' at " << ppmValue << "ppm" << std::endl; } - if (node->hasAnother("iq_swap")) { - DataNode *iq_swap_node = node->getNext("iq_swap"); - int iqSwapValue = 0; - iq_swap_node->element()->get(iqSwapValue); - setIQSwap(iqSwapValue?true:false); - std::cout << "Loaded I/Q Swap for device '" << deviceId << "' as " << (iqSwapValue?"swapped":"not swapped") << std::endl; - } - if (node->hasAnother("direct_sampling")) { - DataNode *direct_sampling_node = node->getNext("direct_sampling"); - int directSamplingValue = 0; - direct_sampling_node->element()->get(directSamplingValue); - setDirectSampling(directSamplingValue); - std::cout << "Loaded Direct Sampling Mode for device '" << deviceId << "': "; - switch (directSamplingValue) { - case 0: - std::cout << "off" << std::endl; - break; - case 1: - std::cout << "I-ADC" << std::endl; - break; - case 2: - std::cout << "Q-ADC" << std::endl; - break; - - } - } if (node->hasAnother("offset")) { DataNode *offset_node = node->getNext("offset"); long long offsetValue = 0; diff --git a/src/AppConfig.h b/src/AppConfig.h index 60de0c7..a7daebb 100644 --- a/src/AppConfig.h +++ b/src/AppConfig.h @@ -17,15 +17,9 @@ public: void setPPM(int ppm); int getPPM(); - void setDirectSampling(int mode); - int getDirectSampling(); - void setOffset(long long offset); long long getOffset(); - void setIQSwap(bool iqSwap); - bool getIQSwap(); - void setDeviceId(std::string deviceId); std::string getDeviceId(); @@ -36,8 +30,7 @@ private: std::string deviceId; std::mutex busy_lock; - std::atomic_int ppm, directSampling; - std::atomic_bool iqSwap; + std::atomic_int ppm; std::atomic_llong offset; }; diff --git a/src/AppFrame.cpp b/src/AppFrame.cpp index dc3cf8d..830b074 100644 --- a/src/AppFrame.cpp +++ b/src/AppFrame.cpp @@ -14,12 +14,11 @@ #endif #include -#include "SDRThread.h" -#include "DemodulatorMgr.h" #include "AudioThread.h" #include "CubicSDR.h" #include "DataTree.h" #include "ColorTheme.h" +#include "DemodulatorMgr.h" #include @@ -35,8 +34,11 @@ EVT_CLOSE(AppFrame::OnClose) EVT_MENU(wxID_ANY, AppFrame::OnMenu) EVT_COMMAND(wxID_ANY, wxEVT_THREAD, AppFrame::OnThread) EVT_IDLE(AppFrame::OnIdle) +EVT_SPLITTER_DCLICK(wxID_ANY, AppFrame::OnDoubleClickSash) +EVT_SPLITTER_UNSPLIT(wxID_ANY, AppFrame::OnUnSplit) wxEND_EVENT_TABLE() + AppFrame::AppFrame() : wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) { @@ -46,12 +48,25 @@ AppFrame::AppFrame() : wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL); wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL); - wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL); + demodTray = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL); int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 }; - demodModeSelector = new ModeSelectorCanvas(this, attribList); + mainSplitter = new wxSplitterWindow( this, wxID_MAIN_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE ); + mainSplitter->SetSashGravity(12.0/37.0); + mainSplitter->SetMinimumPaneSize(1); + + wxPanel *demodPanel = new wxPanel(mainSplitter, wxID_ANY); + + gainCanvas = new GainCanvas(demodPanel, attribList); + + gainSizerItem = demodTray->Add(gainCanvas, 0, wxEXPAND | wxALL, 0); + gainSizerItem->Show(false); + gainSpacerItem = demodTray->AddSpacer(1); + gainSpacerItem->Show(false); + + demodModeSelector = new ModeSelectorCanvas(demodPanel, attribList); demodModeSelector->addChoice(DEMOD_TYPE_FM, "FM"); demodModeSelector->addChoice(DEMOD_TYPE_AM, "AM"); demodModeSelector->addChoice(DEMOD_TYPE_LSB, "LSB"); @@ -90,14 +105,14 @@ AppFrame::AppFrame() : demodTray->Add(demodModeSelectorCons, 2, wxEXPAND | wxALL, 0); wxGetApp().getDemodSpectrumProcessor()->setup(1024); - demodSpectrumCanvas = new SpectrumCanvas(this, attribList); + demodSpectrumCanvas = new SpectrumCanvas(demodPanel, attribList); demodSpectrumCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000); demodVisuals->Add(demodSpectrumCanvas, 3, wxEXPAND | wxALL, 0); wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodSpectrumCanvas->getVisualDataQueue()); demodVisuals->AddSpacer(1); - demodWaterfallCanvas = new WaterfallCanvas(this, attribList); + demodWaterfallCanvas = new WaterfallCanvas(demodPanel, attribList); demodWaterfallCanvas->setup(1024, 128); demodWaterfallCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000); demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas); @@ -110,14 +125,16 @@ AppFrame::AppFrame() : demodTray->AddSpacer(1); - demodSignalMeter = new MeterCanvas(this, attribList); + demodSignalMeter = new MeterCanvas(demodPanel, attribList); demodSignalMeter->setMax(0.5); demodSignalMeter->setHelpTip("Current Signal Level. Click / Drag to set Squelch level."); + demodSignalMeter->SetMinSize(wxSize(12,24)); demodTray->Add(demodSignalMeter, 1, wxEXPAND | wxALL, 0); + demodTray->AddSpacer(1); - scopeCanvas = new ScopeCanvas(this, attribList); + scopeCanvas = new ScopeCanvas(demodPanel, attribList); scopeCanvas->setHelpTip("Audio Visuals, drag left/right to toggle Scope or Spectrum."); demodScopeTray->Add(scopeCanvas, 8, wxEXPAND | wxALL, 0); wxGetApp().getScopeProcessor()->setup(2048); @@ -125,8 +142,9 @@ AppFrame::AppFrame() : demodScopeTray->AddSpacer(1); - demodTuner = new TuningCanvas(this, attribList); + demodTuner = new TuningCanvas(demodPanel, attribList); demodTuner->setHelpTip("Testing tuner"); + demodTuner->SetMinClientSize(wxSize(200,24)); demodScopeTray->Add(demodTuner, 1, wxEXPAND | wxALL, 0); demodTray->Add(demodScopeTray, 30, wxEXPAND | wxALL, 0); @@ -135,73 +153,99 @@ AppFrame::AppFrame() : wxBoxSizer *demodGainTray = new wxBoxSizer(wxVERTICAL); - demodGainMeter = new MeterCanvas(this, attribList); + demodGainMeter = new MeterCanvas(demodPanel, attribList); demodGainMeter->setMax(2.0); demodGainMeter->setHelpTip("Current Demodulator Gain Level. Click / Drag to set Gain level."); demodGainMeter->setShowUserInput(false); + demodGainMeter->SetMinSize(wxSize(12,24)); demodGainTray->Add(demodGainMeter, 8, wxEXPAND | wxALL, 0); + demodGainTray->AddSpacer(1); - demodMuteButton = new ModeSelectorCanvas(this, attribList); + demodMuteButton = new ModeSelectorCanvas(demodPanel, attribList); demodMuteButton->addChoice(1, "M"); demodMuteButton->setPadding(-1,-1); demodMuteButton->setHighlightColor(RGBA4f(0.8,0.2,0.2)); demodMuteButton->setHelpTip("Demodulator Mute Toggle"); demodMuteButton->setToggleMode(true); demodMuteButton->setSelection(-1); - + demodMuteButton->SetMinSize(wxSize(12,24)); + demodGainTray->Add(demodMuteButton, 1, wxEXPAND | wxALL, 0); demodTray->Add(demodGainTray, 1, wxEXPAND | wxALL, 0); - - vbox->Add(demodTray, 12, wxEXPAND | wxALL, 0); - vbox->AddSpacer(1); + + demodPanel->SetSizer(demodTray); +// vbox->Add(demodTray, 12, wxEXPAND | wxALL, 0); +// vbox->AddSpacer(1); + + mainVisSplitter = new wxSplitterWindow( mainSplitter, wxID_VIS_SPLITTER, wxDefaultPosition, wxDefaultSize, wxSP_3DSASH | wxSP_LIVE_UPDATE ); + mainVisSplitter->SetSashGravity(5.0/25.0); + mainVisSplitter->SetMinimumPaneSize(1); + +// mainVisSplitter->Connect( wxEVT_IDLE, wxIdleEventHandler( AppFrame::mainVisSplitterIdle ), NULL, this ); + + wxPanel *spectrumPanel = new wxPanel(mainVisSplitter, wxID_ANY); wxBoxSizer *spectrumSizer = new wxBoxSizer(wxHORIZONTAL); + wxGetApp().getSpectrumProcessor()->setup(2048); - spectrumCanvas = new SpectrumCanvas(this, attribList); + spectrumCanvas = new SpectrumCanvas(spectrumPanel, attribList); spectrumCanvas->setShowDb(true); + spectrumCanvas->setScaleFactorEnabled(true); wxGetApp().getSpectrumProcessor()->attachOutput(spectrumCanvas->getVisualDataQueue()); - spectrumAvgMeter = new MeterCanvas(this, attribList); + spectrumAvgMeter = new MeterCanvas(spectrumPanel, attribList); spectrumAvgMeter->setHelpTip("Spectrum averaging speed, click or drag to adjust."); spectrumAvgMeter->setMax(1.0); spectrumAvgMeter->setLevel(0.65); spectrumAvgMeter->setShowUserInput(false); + spectrumAvgMeter->SetMinSize(wxSize(12,24)); + spectrumSizer->Add(spectrumCanvas, 63, wxEXPAND | wxALL, 0); spectrumSizer->AddSpacer(1); spectrumSizer->Add(spectrumAvgMeter, 1, wxEXPAND | wxALL, 0); + spectrumPanel->SetSizer(spectrumSizer); - vbox->Add(spectrumSizer, 5, wxEXPAND | wxALL, 0); +// vbox->Add(spectrumSizer, 5, wxEXPAND | wxALL, 0); - vbox->AddSpacer(1); +// vbox->AddSpacer(1); + wxPanel *waterfallPanel = new wxPanel(mainVisSplitter, wxID_ANY); wxBoxSizer *wfSizer = new wxBoxSizer(wxHORIZONTAL); - - waterfallCanvas = new WaterfallCanvas(this, attribList); + + waterfallCanvas = new WaterfallCanvas(waterfallPanel, attribList); waterfallCanvas->setup(2048, 512); waterfallDataThread = new FFTVisualDataThread(); waterfallDataThread->setInputQueue("IQDataInput", wxGetApp().getWaterfallVisualQueue()); waterfallDataThread->setOutputQueue("FFTDataOutput", waterfallCanvas->getVisualDataQueue()); + waterfallDataThread->getProcessor()->setHideDC(true); t_FFTData = new std::thread(&FFTVisualDataThread::threadMain, waterfallDataThread); - waterfallSpeedMeter = new MeterCanvas(this, attribList); + waterfallSpeedMeter = new MeterCanvas(waterfallPanel, attribList); waterfallSpeedMeter->setHelpTip("Waterfall speed, click or drag to adjust (max 1024 lines per second)"); waterfallSpeedMeter->setMax(sqrt(1024)); waterfallSpeedMeter->setLevel(sqrt(DEFAULT_WATERFALL_LPS)); waterfallSpeedMeter->setShowUserInput(false); + waterfallSpeedMeter->SetMinSize(wxSize(12,24)); wfSizer->Add(waterfallCanvas, 63, wxEXPAND | wxALL, 0); wfSizer->AddSpacer(1); wfSizer->Add(waterfallSpeedMeter, 1, wxEXPAND | wxALL, 0); + waterfallPanel->SetSizer(wfSizer); - vbox->Add(wfSizer, 20, wxEXPAND | wxALL, 0); +// vbox->Add(wfSizer, 20, wxEXPAND | wxALL, 0); + mainVisSplitter->SplitHorizontally( spectrumPanel, waterfallPanel, 0 ); + mainSplitter->SplitHorizontally( demodPanel, mainVisSplitter ); + + vbox->Add(mainSplitter, 1, wxEXPAND | wxALL, 0); + // TODO: refactor these.. waterfallCanvas->attachSpectrumCanvas(spectrumCanvas); spectrumCanvas->attachWaterfallCanvas(waterfallCanvas); @@ -220,9 +264,11 @@ AppFrame::AppFrame() : // SetIcon(wxICON(sample)); // Make a menubar - wxMenuBar *menuBar = new wxMenuBar; + menuBar = new wxMenuBar; wxMenu *menu = new wxMenu; + menu->Append(wxID_SDR_DEVICES, "SDR Devices"); + menu->AppendSeparator(); menu->Append(wxID_OPEN, "&Open Session"); menu->Append(wxID_SAVE, "&Save Session"); menu->Append(wxID_SAVEAS, "Save Session &As.."); @@ -236,21 +282,9 @@ AppFrame::AppFrame() : menuBar->Append(menu, wxT("&File")); - menu = new wxMenu; - - menu->Append(wxID_SET_FREQ_OFFSET, "Frequency Offset"); - menu->Append(wxID_SET_PPM, "Device PPM"); - iqSwapMenuItem = menu->AppendCheckItem(wxID_SET_SWAP_IQ, "Swap I/Q"); - - wxMenu *dsMenu = new wxMenu; - - directSamplingMenuItems[0] = dsMenu->AppendRadioItem(wxID_SET_DS_OFF, "Off"); - directSamplingMenuItems[1] = dsMenu->AppendRadioItem(wxID_SET_DS_I, "I-ADC"); - directSamplingMenuItems[2] = dsMenu->AppendRadioItem(wxID_SET_DS_Q, "Q-ADC"); - - menu->AppendSubMenu(dsMenu, "Direct Sampling"); - - menuBar->Append(menu, wxT("&Settings")); + settingsMenu = new wxMenu; + + menuBar->Append(settingsMenu, wxT("&Settings")); menu = new wxMenu; @@ -296,54 +330,6 @@ AppFrame::AppFrame() : menuBar->Append(menu, wxT("&Color Scheme")); menu = new wxMenu; - - sampleRateMenuItems[wxID_BANDWIDTH_250K] = menu->AppendRadioItem(wxID_BANDWIDTH_250K, "250k"); - sampleRateMenuItems[wxID_BANDWIDTH_1000M] = menu->AppendRadioItem(wxID_BANDWIDTH_1000M, "1.0M"); - sampleRateMenuItems[wxID_BANDWIDTH_1500M] = menu->AppendRadioItem(wxID_BANDWIDTH_1024M, "1.024M"); - sampleRateMenuItems[wxID_BANDWIDTH_1024M] = menu->AppendRadioItem(wxID_BANDWIDTH_1500M, "1.5M"); - sampleRateMenuItems[wxID_BANDWIDTH_1800M] = menu->AppendRadioItem(wxID_BANDWIDTH_1800M, "1.8M"); - sampleRateMenuItems[wxID_BANDWIDTH_1920M] = menu->AppendRadioItem(wxID_BANDWIDTH_1920M, "1.92M"); - sampleRateMenuItems[wxID_BANDWIDTH_2000M] = menu->AppendRadioItem(wxID_BANDWIDTH_2000M, "2.0M"); - sampleRateMenuItems[wxID_BANDWIDTH_2048M] = menu->AppendRadioItem(wxID_BANDWIDTH_2048M, "2.048M"); - sampleRateMenuItems[wxID_BANDWIDTH_2160M] = menu->AppendRadioItem(wxID_BANDWIDTH_2160M, "2.16M"); - sampleRateMenuItems[wxID_BANDWIDTH_2400M] = menu->AppendRadioItem(wxID_BANDWIDTH_2400M, "2.4M"); - sampleRateMenuItems[wxID_BANDWIDTH_2560M] = menu->AppendRadioItem(wxID_BANDWIDTH_2560M, "2.56M"); - sampleRateMenuItems[wxID_BANDWIDTH_2880M] = menu->AppendRadioItem(wxID_BANDWIDTH_2880M, "2.88M"); -// sampleRateMenuItems[wxID_BANDWIDTH_3000M] = menu->AppendRadioItem(wxID_BANDWIDTH_3000M, "3.0M"); - sampleRateMenuItems[wxID_BANDWIDTH_3200M] = menu->AppendRadioItem(wxID_BANDWIDTH_3200M, "3.2M"); - - sampleRateMenuItems[wxID_BANDWIDTH_2400M]->Check(true); - - menuBar->Append(menu, wxT("&Input Bandwidth")); - - std::vector *devs = wxGetApp().getDevices(); - std::vector::iterator devs_i; - - if (devs->size() > 1) { - - menu = new wxMenu; - - int p = 0; - for (devs_i = devs->begin(); devs_i != devs->end(); devs_i++) { - std::string devName = (*devs_i)->getName(); - if ((*devs_i)->isAvailable()) { - devName.append(": "); - devName.append((*devs_i)->getProduct()); - devName.append(" ["); - devName.append((*devs_i)->getSerial()); - devName.append("]"); - } else { - devName.append(" (In Use?)"); - } - - menu->AppendRadioItem(wxID_DEVICE_ID + p, devName)->Check(wxGetApp().getDevice() == p); - p++; - } - - menuBar->Append(menu, wxT("Input &Device")); - } - - menu = new wxMenu; #define NUM_RATES_DEFAULT 4 int desired_rates[NUM_RATES_DEFAULT] = { 48000, 44100, 96000, 192000 }; @@ -371,7 +357,6 @@ AppFrame::AppFrame() : } for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) { - new wxMenu; int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * mdevices_i->first; wxMenu *subMenu = new wxMenu; menu->AppendSubMenu(subMenu, mdevices_i->second.name, wxT("Description?")); @@ -392,6 +377,10 @@ AppFrame::AppFrame() : } } + sampleRateMenu = new wxMenu; + + menuBar->Append(sampleRateMenu, wxT("&Input Bandwidth")); + menuBar->Append(menu, wxT("Audio &Bandwidth")); SetMenuBar(menuBar); @@ -441,7 +430,10 @@ AppFrame::AppFrame() : wxAcceleratorTable accel(3, entries); SetAcceleratorTable(accel); - + deviceChanged.store(false); + devInfo = NULL; + wxGetApp().deviceSelector(); + // static const int attribs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 }; // wxLogStatus("Double-buffered display %s supported", wxGLCanvas::IsDisplaySupported(attribs) ? "is" : "not"); // ShowFullScreen(true); @@ -452,19 +444,116 @@ AppFrame::~AppFrame() { t_FFTData->join(); } +void AppFrame::initDeviceParams(SDRDeviceInfo *devInfo) { + this->devInfo = devInfo; + deviceChanged.store(true); +} -void AppFrame::initDeviceParams(std::string deviceId) { - DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId); +void AppFrame::updateDeviceParams() { - int dsMode = devConfig->getDirectSampling(); - - if (dsMode > 0 && dsMode <= 2) { - directSamplingMenuItems[devConfig->getDirectSampling()]->Check(); + if (!deviceChanged.load()) { + return; } - if (devConfig->getIQSwap()) { - iqSwapMenuItem->Check(); + + // Build settings menu + wxMenu *newSettingsMenu = new wxMenu; + newSettingsMenu->Append(wxID_SET_FREQ_OFFSET, "Frequency Offset"); + if (devInfo->getRxChannel()->hasCORR()) { + newSettingsMenu->Append(wxID_SET_PPM, "Device PPM"); } + + agcMenuItem = newSettingsMenu->AppendCheckItem(wxID_AGC_CONTROL, "Automatic Gain"); + agcMenuItem->Check(wxGetApp().getAGCMode()); + + SoapySDR::ArgInfoList::const_iterator args_i; + + int i = 0; + settingArgs = devInfo->getSettingsArgInfo(); + for (args_i = settingArgs.begin(); args_i != settingArgs.end(); args_i++) { + SoapySDR::ArgInfo arg = (*args_i); + std::string currentVal = wxGetApp().getSDRThread()->readSetting(arg.key); + if (arg.type == SoapySDR::ArgInfo::BOOL) { + wxMenuItem *item = newSettingsMenu->AppendCheckItem(wxID_SETTINGS_BASE+i, arg.name, arg.description); + item->Check(currentVal=="true"); + i++; + } else if (arg.type == SoapySDR::ArgInfo::INT) { + newSettingsMenu->Append(wxID_SETTINGS_BASE+i, arg.name, arg.description); + i++; + } else if (arg.type == SoapySDR::ArgInfo::FLOAT) { + newSettingsMenu->Append(wxID_SETTINGS_BASE+i, arg.name, arg.description); + i++; + } else if (arg.type == SoapySDR::ArgInfo::STRING) { + if (arg.options.size()) { + wxMenu *subMenu = new wxMenu; + int j = 0; + for (std::vector::iterator str_i = arg.options.begin(); str_i != arg.options.end(); str_i++) { + std::string optName = (*str_i); + std::string displayName = optName; + if (arg.optionNames.size()) { + displayName = arg.optionNames[j]; + } + wxMenuItem *item = subMenu->AppendRadioItem(wxID_SETTINGS_BASE+i, displayName); + if (currentVal == (*str_i)) { + item->Check(); + } + j++; + i++; + } + newSettingsMenu->AppendSubMenu(subMenu, arg.name, arg.description); + } else { + newSettingsMenu->Append(wxID_SETTINGS_BASE+i, arg.name, arg.description); + i++; + } + } + } + settingsIdMax = wxID_SETTINGS_BASE+i; + + menuBar->Replace(1, newSettingsMenu, wxT("&Settings")); + settingsMenu = newSettingsMenu; + + // Build sample rate menu + sampleRates = devInfo->getRxChannel()->getSampleRates(); + sampleRateMenuItems.erase(sampleRateMenuItems.begin(),sampleRateMenuItems.end()); + + wxMenu *newSampleRateMenu = new wxMenu; + int ofs = 0; + long sampleRate = wxGetApp().getSampleRate(); + bool checked = false; + for (vector::iterator i = sampleRates.begin(); i != sampleRates.end(); i++) { + sampleRateMenuItems[wxID_BANDWIDTH_BASE+ofs] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_BASE+ofs, frequencyToStr(*i)); + if (sampleRate == (*i)) { + sampleRateMenuItems[wxID_BANDWIDTH_BASE+ofs]->Check(true); + checked = true; + } + ofs++; + } + + sampleRateMenuItems[wxID_BANDWIDTH_MANUAL] = newSampleRateMenu->AppendRadioItem(wxID_BANDWIDTH_MANUAL, wxT("Manual Entry")); + if (!checked) { + sampleRateMenuItems[wxID_BANDWIDTH_MANUAL]->Check(true); + } + + menuBar->Replace(4, newSampleRateMenu, wxT("&Input Bandwidth")); + sampleRateMenu = newSampleRateMenu; + + if (!wxGetApp().getAGCMode()) { + gainSpacerItem->Show(true); + gainSizerItem->Show(true); + gainSizerItem->SetMinSize(devInfo->getRxChannel()->getGains().size()*50,0); + demodTray->Layout(); + gainCanvas->updateGainUI(); + gainCanvas->Refresh(); + gainCanvas->Refresh(); + } else { + gainSpacerItem->Show(false); + gainSizerItem->Show(false); + demodTray->Layout(); + } + + agcMenuItem->Check(wxGetApp().getAGCMode()); + + deviceChanged.store(false); } @@ -482,19 +571,41 @@ void AppFrame::OnMenu(wxCommandEvent& event) { wxGetApp().saveConfig(); } } else if (event.GetId() == wxID_SET_DS_OFF) { - wxGetApp().setDirectSampling(0); - wxGetApp().saveConfig(); +// wxGetApp().setDirectSampling(0); +// wxGetApp().saveConfig(); } else if (event.GetId() == wxID_SET_DS_I) { - wxGetApp().setDirectSampling(1); - wxGetApp().saveConfig(); +// wxGetApp().setDirectSampling(1); +// wxGetApp().saveConfig(); } else if (event.GetId() == wxID_SET_DS_Q) { - wxGetApp().setDirectSampling(2); - wxGetApp().saveConfig(); +// wxGetApp().setDirectSampling(2); +// wxGetApp().saveConfig(); } else if (event.GetId() == wxID_SET_SWAP_IQ) { - bool swap_state = !wxGetApp().getSwapIQ(); - wxGetApp().setSwapIQ(swap_state); - wxGetApp().saveConfig(); - iqSwapMenuItem->Check(swap_state); +// bool swap_state = !wxGetApp().getSwapIQ(); +// wxGetApp().setSwapIQ(swap_state); +// wxGetApp().saveConfig(); +// iqSwapMenuItem->Check(swap_state); + } else if (event.GetId() == wxID_AGC_CONTROL) { + if (wxGetApp().getDevice() == NULL) { + agcMenuItem->Check(); + return; + } + if (!wxGetApp().getAGCMode()) { + wxGetApp().setAGCMode(true); + gainSpacerItem->Show(false); + gainSizerItem->Show(false); + demodTray->Layout(); + } else { + wxGetApp().setAGCMode(false); + gainSpacerItem->Show(true); + gainSizerItem->Show(true); + gainSizerItem->SetMinSize(wxGetApp().getDevice()->getRxChannel()->getGains().size()*40,0); + demodTray->Layout(); + gainCanvas->updateGainUI(); + gainCanvas->Refresh(); + gainCanvas->Refresh(); + } + } else if (event.GetId() == wxID_SDR_DEVICES) { + wxGetApp().deviceSelector(); } else if (event.GetId() == wxID_SET_PPM) { long ofs = wxGetNumberFromUser("Frequency correction for device in PPM.\ni.e. -51 for -51 PPM\n\nNote: you can adjust PPM interactively\nby holding ALT over the frequency tuning bar.\n", "Parts per million (PPM)", "Frequency Correction", wxGetApp().getPPM(), -1000, 1000, this); @@ -545,7 +656,7 @@ void AppFrame::OnMenu(wxCommandEvent& event) { demodTuner->Refresh(); SetTitle(CUBICSDR_TITLE); currentSessionFile = ""; - } else if (event.GetId() == wxID_EXIT) { + } else if (event.GetId() == wxID_CLOSE || event.GetId() == wxID_EXIT) { Close(false); } else if (event.GetId() == wxID_THEME_DEFAULT) { ThemeMgr::mgr.setTheme(COLOR_THEME_DEFAULT); @@ -563,73 +674,99 @@ void AppFrame::OnMenu(wxCommandEvent& event) { ThemeMgr::mgr.setTheme(COLOR_THEME_RADAR); } + if (event.GetId() >= wxID_SETTINGS_BASE && event.GetId() < settingsIdMax) { + int setIdx = event.GetId()-wxID_SETTINGS_BASE; + int menuIdx = 0; + for (std::vector::iterator arg_i = settingArgs.begin(); arg_i != settingArgs.end(); arg_i++) { + SoapySDR::ArgInfo &arg = (*arg_i); + + if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size() && setIdx >= menuIdx && setIdx < menuIdx+arg.options.size()) { + int optIdx = setIdx-menuIdx; + wxGetApp().getSDRThread()->writeSetting(arg.key, arg.options[optIdx]); + break; + } else if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size()) { + menuIdx += arg.options.size(); + } else if (menuIdx == setIdx) { + if (arg.type == SoapySDR::ArgInfo::BOOL) { + wxGetApp().getSDRThread()->writeSetting(arg.key, (wxGetApp().getSDRThread()->readSetting(arg.key)=="true")?"false":"true"); + break; + } else if (arg.type == SoapySDR::ArgInfo::STRING) { + menuIdx++; + } else if (arg.type == SoapySDR::ArgInfo::INT) { + int currentVal; + try { + currentVal = std::stoi(wxGetApp().getSDRThread()->readSetting(arg.key)); + } catch (std::invalid_argument e) { + currentVal = 0; + } + int intVal = wxGetNumberFromUser(arg.description, arg.units, arg.name, currentVal, arg.range.minimum(), arg.range.maximum(), this); + if (intVal != -1) { + wxGetApp().getSDRThread()->writeSetting(arg.key, std::to_string(intVal)); + } + break; + } else if (arg.type == SoapySDR::ArgInfo::FLOAT) { + wxString floatVal = wxGetTextFromUser(arg.description, arg.name, wxGetApp().getSDRThread()->readSetting(arg.key)); + try { + wxGetApp().getSDRThread()->writeSetting(arg.key, floatVal.ToStdString()); + } catch (std::invalid_argument e) { + // ... + } + break; + } else { + menuIdx++; + } + } else { + menuIdx++; + } + } + } + if (event.GetId() >= wxID_THEME_DEFAULT && event.GetId() <= wxID_THEME_RADAR) { demodTuner->Refresh(); demodModeSelector->Refresh(); + waterfallSpeedMeter->Refresh(); + spectrumAvgMeter->Refresh(); + gainCanvas->setThemeColors(); } switch (event.GetId()) { - case wxID_BANDWIDTH_250K: - wxGetApp().setSampleRate(250000); - break; - case wxID_BANDWIDTH_1000M: - wxGetApp().setSampleRate(1000000); - break; - case wxID_BANDWIDTH_1024M: - wxGetApp().setSampleRate(1024000); - break; - case wxID_BANDWIDTH_1500M: - wxGetApp().setSampleRate(1500000); - break; - case wxID_BANDWIDTH_1800M: - wxGetApp().setSampleRate(1800000); - break; - case wxID_BANDWIDTH_1920M: - wxGetApp().setSampleRate(1920000); - break; - case wxID_BANDWIDTH_2000M: - wxGetApp().setSampleRate(2000000); - break; - case wxID_BANDWIDTH_2048M: - wxGetApp().setSampleRate(2048000); - break; - case wxID_BANDWIDTH_2160M: - wxGetApp().setSampleRate(2160000); - break; - case wxID_BANDWIDTH_2400M: - wxGetApp().setSampleRate(2400000); - break; - case wxID_BANDWIDTH_2560M: - wxGetApp().setSampleRate(2560000); - break; - case wxID_BANDWIDTH_2880M: - wxGetApp().setSampleRate(2880000); - break; -// case wxID_BANDWIDTH_3000M: -// wxGetApp().setSampleRate(3000000); -// break; - case wxID_BANDWIDTH_3200M: - wxGetApp().setSampleRate(3200000); + case wxID_BANDWIDTH_MANUAL: + int rateHigh, rateLow; + + SDRDeviceInfo *dev = wxGetApp().getDevice(); + if (dev == NULL) { + break; + } + + SDRDeviceChannel *chan = dev->getRxChannel(); + + rateLow = 2000000; + rateHigh = 30000000; + + if (chan->getSampleRates().size()) { + rateLow = chan->getSampleRates()[0]; + rateHigh = chan->getSampleRates()[chan->getSampleRates().size()-1]; + } + + long bw = wxGetNumberFromUser("\n" + dev->getName() + "\n\n " + + "min: " + std::to_string(rateLow) + " Hz" + + ", max: " + std::to_string(rateHigh) + " Hz\n", + "Sample Rate in Hz", + "Manual Bandwidth Entry", + wxGetApp().getSampleRate(), + rateLow, + rateHigh, + this); + if (bw != -1) { + wxGetApp().setSampleRate(bw); + } break; } - - std::vector *devs = wxGetApp().getDevices(); - if (event.GetId() >= wxID_DEVICE_ID && event.GetId() <= wxID_DEVICE_ID + devs->size()) { - int devId = event.GetId() - wxID_DEVICE_ID; - wxGetApp().setDevice(devId); - - SDRDeviceInfo *dev = (*wxGetApp().getDevices())[devId]; - DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId()); - - int dsMode = devConfig->getDirectSampling(); - - if (dsMode >= 0 && dsMode <= 2) { - directSamplingMenuItems[devConfig->getDirectSampling()]->Check(); - } - - iqSwapMenuItem->Check(devConfig->getIQSwap()); + + if (event.GetId() >= wxID_BANDWIDTH_BASE && event.GetId() < wxID_BANDWIDTH_BASE+sampleRates.size()) { + wxGetApp().setSampleRate(sampleRates[event.GetId()-wxID_BANDWIDTH_BASE]); } - + if (event.GetId() >= wxID_AUDIO_BANDWIDTH_BASE) { int evId = event.GetId(); std::vector::iterator devices_i; @@ -683,11 +820,13 @@ void AppFrame::OnThread(wxCommandEvent& event) { void AppFrame::OnIdle(wxIdleEvent& event) { + if (deviceChanged.load()) { + updateDeviceParams(); + } + DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); if (demod) { - DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); - if (demod->isTracking()) { if (spectrumCanvas->getViewState()) { long long diff = abs(demod->getFrequency() - spectrumCanvas->getCenterFrequency()) + (demod->getBandwidth()/2) + (demod->getBandwidth()/4); @@ -851,8 +990,10 @@ void AppFrame::OnIdle(wxIdleEvent& event) { if (!demodTuner->HasFocus()) { demodTuner->SetFocus(); } - } else if (!waterfallCanvas->HasFocus()) { - waterfallCanvas->SetFocus(); + } else if (!wxGetApp().isDeviceSelectorOpen()) { + if (!waterfallCanvas->HasFocus()) { + waterfallCanvas->SetFocus(); + } } scopeCanvas->setPPMMode(demodTuner->isAltDown()); @@ -863,10 +1004,10 @@ void AppFrame::OnIdle(wxIdleEvent& event) { wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0)); wxGetApp().getScopeProcessor()->run(); - wxGetApp().getSpectrumDistributor()->run(); +// wxGetApp().getSpectrumDistributor()->run(); SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor(); - + if (spectrumAvgMeter->inputChanged()) { float val = spectrumAvgMeter->getInputValue(); if (val < 0.01) { @@ -892,7 +1033,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { dproc->setCenterFrequency(demodWaterfallCanvas->getCenterFrequency()); SpectrumVisualProcessor *wproc = waterfallDataThread->getProcessor(); - + if (waterfallSpeedMeter->inputChanged()) { float val = waterfallSpeedMeter->getInputValue(); waterfallSpeedMeter->setLevel(val); @@ -904,6 +1045,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) { wproc->setView(waterfallCanvas->getViewState()); wproc->setBandwidth(waterfallCanvas->getBandwidth()); wproc->setCenterFrequency(waterfallCanvas->getCenterFrequency()); + wxGetApp().getSDRPostThread()->setIQVisualRange(waterfallCanvas->getCenterFrequency(), waterfallCanvas->getBandwidth()); // waterfallCanvas->processInputQueue(); // waterfallCanvas->Refresh(); @@ -917,6 +1059,39 @@ void AppFrame::OnIdle(wxIdleEvent& event) { event.RequestMore(); } + +void AppFrame::OnDoubleClickSash(wxSplitterEvent& event) +{ + wxWindow *a, *b; + wxSplitterWindow *w = NULL; + float g = 0.5; + + if (event.GetId() == wxID_MAIN_SPLITTER) { + w = mainSplitter; + g = 12.0/37.0; + } else if (event.GetId() == wxID_VIS_SPLITTER) { + w = mainVisSplitter; + g = 7.4/37.0; + } + + if (w != NULL) { + a = w->GetWindow1(); + b = w->GetWindow2(); + w->Unsplit(); + w->SetSashGravity(g); + wxSize s = w->GetSize(); + + w->SplitHorizontally(a, b, int(float(s.GetHeight()) * g)); + } + + event.Veto(); +} + +void AppFrame::OnUnSplit(wxSplitterEvent& event) +{ + event.Veto(); +} + void AppFrame::saveSession(std::string fileName) { DataTree s("cubicsdr_session"); DataNode *header = s.rootNode()->newChild("header"); @@ -1054,3 +1229,8 @@ bool AppFrame::loadSession(std::string fileName) { return true; } + +FFTVisualDataThread *AppFrame::getWaterfallDataThread() { + return waterfallDataThread; +} + diff --git a/src/AppFrame.h b/src/AppFrame.h index 1147b94..d41c679 100644 --- a/src/AppFrame.h +++ b/src/AppFrame.h @@ -1,6 +1,10 @@ #pragma once -#include "wx/frame.h" +#include +#include +#include +#include + #include "PrimaryGLContext.h" #include "ScopeCanvas.h" @@ -9,7 +13,9 @@ #include "MeterCanvas.h" #include "TuningCanvas.h" #include "ModeSelectorCanvas.h" +#include "GainCanvas.h" #include "FFTVisualDataThread.h" +#include "SDRDeviceInfo.h" //#include "UITestCanvas.h" #include @@ -22,6 +28,11 @@ #define wxID_SET_DS_I 2005 #define wxID_SET_DS_Q 2006 #define wxID_SET_SWAP_IQ 2007 +#define wxID_SDR_DEVICES 2008 +#define wxID_AGC_CONTROL 2009 + +#define wxID_MAIN_SPLITTER 2050 +#define wxID_VIS_SPLITTER 2051 #define wxID_THEME_DEFAULT 2100 #define wxID_THEME_SHARP 2101 @@ -31,20 +42,10 @@ #define wxID_THEME_HD 2105 #define wxID_THEME_RADAR 2106 -#define wxID_BANDWIDTH_250K 2150 -#define wxID_BANDWIDTH_1000M 2151 -#define wxID_BANDWIDTH_1024M 2152 -#define wxID_BANDWIDTH_1500M 2153 -#define wxID_BANDWIDTH_1800M 2154 -#define wxID_BANDWIDTH_1920M 2155 -#define wxID_BANDWIDTH_2000M 2156 -#define wxID_BANDWIDTH_2048M 2157 -#define wxID_BANDWIDTH_2160M 2158 -#define wxID_BANDWIDTH_2400M 2159 -#define wxID_BANDWIDTH_2560M 2160 -#define wxID_BANDWIDTH_2880M 2161 -//#define wxID_BANDWIDTH_3000M 2162 -#define wxID_BANDWIDTH_3200M 2163 +#define wxID_BANDWIDTH_BASE 2150 +#define wxID_BANDWIDTH_MANUAL 2200 + +#define wxID_SETTINGS_BASE 2300 #define wxID_DEVICE_ID 3500 @@ -58,18 +59,22 @@ public: ~AppFrame(); void OnThread(wxCommandEvent& event); void OnEventInput(wxThreadEvent& event); - void initDeviceParams(std::string deviceId); + void initDeviceParams(SDRDeviceInfo *devInfo); + void updateDeviceParams(); void saveSession(std::string fileName); bool loadSession(std::string fileName); + FFTVisualDataThread *getWaterfallDataThread(); + private: void OnMenu(wxCommandEvent& event); void OnClose(wxCloseEvent& event); void OnNewWindow(wxCommandEvent& event); void OnIdle(wxIdleEvent& event); - - + void OnDoubleClickSash(wxSplitterEvent& event); + void OnUnSplit(wxSplitterEvent& event); + ScopeCanvas *scopeCanvas; SpectrumCanvas *spectrumCanvas; WaterfallCanvas *waterfallCanvas; @@ -85,7 +90,11 @@ private: MeterCanvas *spectrumAvgMeter; MeterCanvas *waterfallSpeedMeter; ModeSelectorCanvas *demodMuteButton; - + GainCanvas *gainCanvas; + wxSizerItem *gainSizerItem, *gainSpacerItem; + wxSplitterWindow *mainVisSplitter, *mainSplitter; + wxBoxSizer *demodTray; + DemodulatorInstance *activeDemodulator; std::vector devices; @@ -95,13 +104,21 @@ private: std::map sampleRateMenuItems; std::map audioSampleRateMenuItems; std::map directSamplingMenuItems; - wxMenuItem *iqSwapMenuItem; + wxMenuBar *menuBar; + wxMenu *sampleRateMenu; + wxMenuItem *agcMenuItem; + wxMenu *settingsMenu; + SoapySDR::ArgInfoList settingArgs; + int settingsIdMax; + std::vector sampleRates; std::string currentSessionFile; FFTVisualDataThread *waterfallDataThread; std::thread *t_FFTData; + SDRDeviceInfo *devInfo; + std::atomic_bool deviceChanged; wxDECLARE_EVENT_TABLE(); }; diff --git a/src/CubicSDR.cpp b/src/CubicSDR.cpp index 51344c4..c90fa0d 100644 --- a/src/CubicSDR.cpp +++ b/src/CubicSDR.cpp @@ -12,6 +12,7 @@ #endif #include "CubicSDR.h" +#include #ifdef _OSX_APP_ #include "CoreFoundation/CoreFoundation.h" @@ -32,10 +33,101 @@ class outbuf : public std::streambuf { }; #endif +#ifdef MINGW_PATCH + FILE _iob[] = { *stdin, *stdout, *stderr }; -CubicSDR::CubicSDR() : appframe(NULL), m_glContext(NULL), frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE), directSamplingMode(0), - sdrThread(NULL), sdrPostThread(NULL), spectrumVisualThread(NULL), demodVisualThread(NULL), pipeSDRCommand(NULL), pipeSDRIQData(NULL), pipeIQVisualData(NULL), pipeAudioVisualData(NULL), t_SDR(NULL), t_PostSDR(NULL) { + extern "C" FILE * __cdecl __iob_func(void) + { + return _iob; + } + + extern "C" int __cdecl __isnan(double x) + { + return _finite(x)?0:1; + } + + extern "C" int __cdecl __isnanf(float x) + { + return _finitef(x)?0:1; + } +#endif + + +std::string& filterChars(std::string& s, const std::string& allowed) { + s.erase(remove_if(s.begin(), s.end(), [&allowed](const char& c) { + return allowed.find(c) == std::string::npos; + }), s.end()); + return s; +} + +std::string frequencyToStr(long long freq) { + long double freqTemp; + freqTemp = freq; + std::string suffix(""); + std::stringstream freqStr; + + if (freqTemp >= 1.0e9) { + freqTemp /= 1.0e9; + freqStr << std::setprecision(10); + suffix = std::string("GHz"); + } else if (freqTemp >= 1.0e6) { + freqTemp /= 1.0e6; + freqStr << std::setprecision(7); + suffix = std::string("MHz"); + } else if (freqTemp >= 1.0e3) { + freqTemp /= 1.0e3; + freqStr << std::setprecision(4); + suffix = std::string("KHz"); + } + + freqStr << freqTemp; + freqStr << suffix; + + return freqStr.str(); +} + +long long strToFrequency(std::string freqStr) { + std::string filterStr = filterChars(freqStr, std::string("0123456789.MKGHmkgh")); + + int numLen = filterStr.find_first_not_of("0123456789."); + + if (numLen == std::string::npos) { + numLen = freqStr.length(); + } + + std::string numPartStr = freqStr.substr(0, numLen); + std::string suffixStr = freqStr.substr(numLen); + + std::stringstream numPartStream; + numPartStream.str(numPartStr); + + long double freqTemp = 0; + + numPartStream >> freqTemp; + + if (suffixStr.length()) { + if (suffixStr.find_first_of("Gg") != std::string::npos) { + freqTemp *= 1.0e9; + } else if (suffixStr.find_first_of("Mm") != std::string::npos) { + freqTemp *= 1.0e6; + } else if (suffixStr.find_first_of("Kk") != std::string::npos) { + freqTemp *= 1.0e3; + } else if (suffixStr.find_first_of("Hh") != std::string::npos) { + // ... + } + } else if (numPartStr.find_first_of(".") != std::string::npos || freqTemp <= 3000) { + freqTemp *= 1.0e6; + } + + return (long long) freqTemp; +} + + +CubicSDR::CubicSDR() : appframe(NULL), m_glContext(NULL), frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE), + sdrThread(NULL), sdrPostThread(NULL), spectrumVisualThread(NULL), demodVisualThread(NULL), pipeSDRIQData(NULL), pipeIQVisualData(NULL), pipeAudioVisualData(NULL), t_SDR(NULL), t_PostSDR(NULL) { + sampleRateInitialized.store(false); + agcMode.store(true); } bool CubicSDR::OnInit() { @@ -71,7 +163,8 @@ bool CubicSDR::OnInit() { frequency = wxGetApp().getConfig()->getCenterFreq(); offset = 0; ppm = 0; - directSamplingMode = 0; + devicesReady.store(false); + deviceSelectorOpen.store(false); // Visual Data spectrumVisualThread = new SpectrumVisualDataThread(); @@ -80,22 +173,15 @@ bool CubicSDR::OnInit() { pipeIQVisualData = new DemodulatorThreadInputQueue(); pipeIQVisualData->set_max_num_items(1); - spectrumDistributor.setInput(pipeIQVisualData); - pipeDemodIQVisualData = new DemodulatorThreadInputQueue(); pipeDemodIQVisualData->set_max_num_items(1); - pipeSpectrumIQVisualData = new DemodulatorThreadInputQueue(); - pipeSpectrumIQVisualData->set_max_num_items(1); - pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue(); pipeWaterfallIQVisualData->set_max_num_items(128); - spectrumDistributor.attachOutput(pipeDemodIQVisualData); - spectrumDistributor.attachOutput(pipeSpectrumIQVisualData); - getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData); - getSpectrumProcessor()->setInput(pipeSpectrumIQVisualData); + getSpectrumProcessor()->setInput(pipeIQVisualData); + getSpectrumProcessor()->setHideDC(true); pipeAudioVisualData = new DemodulatorThreadOutputQueue(); pipeAudioVisualData->set_max_num_items(1); @@ -104,83 +190,35 @@ bool CubicSDR::OnInit() { // I/Q Data pipeSDRIQData = new SDRThreadIQDataQueue(); - pipeSDRCommand = new SDRThreadCommandQueue(); - pipeSDRIQData->set_max_num_items(100); sdrThread = new SDRThread(); - sdrThread->setInputQueue("SDRCommandQueue",pipeSDRCommand); sdrThread->setOutputQueue("IQDataOutput",pipeSDRIQData); sdrPostThread = new SDRPostThread(); -// sdrPostThread->setNumVisSamples(BUF_SIZE); sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData); sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData); sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData); - - std::vector::iterator devs_i; - - SDRThread::enumerate_rtl(&devs); - SDRDeviceInfo *dev = NULL; - - if (devs.size() > 1) { - wxArrayString choices; - for (devs_i = devs.begin(); devs_i != devs.end(); devs_i++) { - std::string devName = (*devs_i)->getName(); - if ((*devs_i)->isAvailable()) { - devName.append(": "); - devName.append((*devs_i)->getProduct()); - devName.append(" ["); - devName.append((*devs_i)->getSerial()); - devName.append("]"); - } else { - devName.append(" (In Use?)"); - } - choices.Add(devName); - } - - int devId = wxGetSingleChoiceIndex(wxT("Devices"), wxT("Choose Input Device"), choices); - if (devId == -1) { // User chose to cancel - return false; - } - - dev = devs[devId]; - - sdrThread->setDeviceId(devId); - } else if (devs.size() == 1) { - dev = devs[0]; - } - - if (!dev) { - wxMessageDialog *info; - info = new wxMessageDialog(NULL, wxT("\x28\u256F\xB0\u25A1\xB0\uFF09\u256F\uFE35\x20\u253B\u2501\u253B"), wxT("RTL-SDR device not found"), wxOK | wxICON_ERROR); - info->ShowModal(); - return false; - } + sdrPostThread->setOutputQueue("IQActiveDemodVisualDataOutput", pipeDemodIQVisualData); t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread); - t_SDR = new std::thread(&SDRThread::threadMain, sdrThread); t_SpectrumVisual = new std::thread(&SpectrumVisualDataThread::threadMain, spectrumVisualThread); t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread); + sdrEnum = new SDREnumerator(); + appframe = new AppFrame(); - if (dev != NULL) { - appframe->initDeviceParams(dev->getDeviceId()); - DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId()); - ppm = devConfig->getPPM(); - offset = devConfig->getOffset(); - directSamplingMode = devConfig->getDirectSampling(); - } + t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum); -#ifdef __APPLE__ - int main_policy; - struct sched_param main_param; - - main_policy = SCHED_RR; - main_param.sched_priority = sched_get_priority_min(SCHED_RR)+2; - - pthread_setschedparam(pthread_self(), main_policy, &main_param); -#endif +//#ifdef __APPLE__ +// int main_policy; +// struct sched_param main_param; +// +// main_policy = SCHED_RR; +// main_param.sched_priority = sched_get_priority_min(SCHED_RR)+2; +// +// pthread_setschedparam(pthread_self(), main_policy, &main_param); +//#endif return true; } @@ -189,9 +227,12 @@ int CubicSDR::OnExit() { demodMgr.terminateAll(); std::cout << "Terminating SDR thread.." << std::endl; - sdrThread->terminate(); - t_SDR->join(); - + if (!sdrThread->isTerminated()) { + sdrThread->terminate(); + if (t_SDR) { + t_SDR->join(); + } + } std::cout << "Terminating SDR post-processing thread.." << std::endl; sdrPostThread->terminate(); t_PostSDR->join(); @@ -204,7 +245,6 @@ int CubicSDR::OnExit() { t_DemodVisual->join(); delete sdrThread; - delete t_SDR; delete sdrPostThread; delete t_PostSDR; @@ -214,8 +254,6 @@ int CubicSDR::OnExit() { delete t_DemodVisual; delete demodVisualThread; - delete pipeSDRCommand; - delete pipeIQVisualData; delete pipeAudioVisualData; delete pipeSDRIQData; @@ -254,17 +292,87 @@ bool CubicSDR::OnCmdLineParsed(wxCmdLineParser& parser) { config.load(); +#ifdef BUNDLE_SOAPY_MODS + if (parser.Found("b")) { + useLocalMod.store(false); + } else { + useLocalMod.store(true); + } +#else + useLocalMod.store(true); +#endif + return true; } +void CubicSDR::deviceSelector() { + if (deviceSelectorOpen) { + deviceSelectorDialog->Raise(); + deviceSelectorDialog->SetFocus(); + return; + } + deviceSelectorOpen.store(true); + deviceSelectorDialog = new SDRDevicesDialog(appframe); + deviceSelectorDialog->Show(); +} + +void CubicSDR::addRemote(std::string remoteAddr) { + SDREnumerator::addRemote(remoteAddr); + devicesReady.store(false); + t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum); +} + +void CubicSDR::removeRemote(std::string remoteAddr) { + SDREnumerator::removeRemote(remoteAddr); +} + +void CubicSDR::sdrThreadNotify(SDRThread::SDRThreadState state, std::string message) { + notify_busy.lock(); + if (state == SDRThread::SDR_THREAD_INITIALIZED) { + appframe->initDeviceParams(getDevice()); + } + if (state == SDRThread::SDR_THREAD_MESSAGE) { + notifyMessage = message; + } + if (state == SDRThread::SDR_THREAD_TERMINATED) { + t_SDR->join(); + delete t_SDR; + } + if (state == SDRThread::SDR_THREAD_FAILED) { + notifyMessage = message; +// wxMessageDialog *info; +// info = new wxMessageDialog(NULL, message, wxT("Error initializing device"), wxOK | wxICON_ERROR); +// info->ShowModal(); + } + //if (appframe) { appframe->SetStatusText(message); } + notify_busy.unlock(); +} + + +void CubicSDR::sdrEnumThreadNotify(SDREnumerator::SDREnumState state, std::string message) { + notify_busy.lock(); + if (state == SDREnumerator::SDR_ENUM_MESSAGE) { + notifyMessage = message; + } + if (state == SDREnumerator::SDR_ENUM_DEVICES_READY) { + devs = SDREnumerator::enumerate_devices("", true); + devicesReady.store(true); + } + if (state == SDREnumerator::SDR_ENUM_FAILED) { + notifyMessage = message; + sdrEnum->terminate(); + } + //if (appframe) { appframe->SetStatusText(message); } + notify_busy.unlock(); +} + + void CubicSDR::setFrequency(long long freq) { if (freq < sampleRate / 2) { freq = sampleRate / 2; } frequency = freq; - SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_TUNE); - command.llong_value = freq; - pipeSDRCommand->push(command); + sdrThread->setFrequency(freq); } long long CubicSDR::getOffset() { @@ -273,42 +381,93 @@ long long CubicSDR::getOffset() { void CubicSDR::setOffset(long long ofs) { offset = ofs; - SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_SET_OFFSET); - command.llong_value = ofs; - pipeSDRCommand->push(command); - - SDRDeviceInfo *dev = (*getDevices())[getDevice()]; + sdrThread->setOffset(offset); + SDRDeviceInfo *dev = getDevice(); config.getDevice(dev->getDeviceId())->setOffset(ofs); } -void CubicSDR::setDirectSampling(int mode) { - directSamplingMode = mode; - SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_SET_DIRECT_SAMPLING); - command.llong_value = mode; - pipeSDRCommand->push(command); - - SDRDeviceInfo *dev = (*getDevices())[getDevice()]; - config.getDevice(dev->getDeviceId())->setDirectSampling(mode); -} - -int CubicSDR::getDirectSampling() { - return directSamplingMode; -} - -void CubicSDR::setSwapIQ(bool swapIQ) { - sdrPostThread->setSwapIQ(swapIQ); - SDRDeviceInfo *dev = (*getDevices())[getDevice()]; - config.getDevice(dev->getDeviceId())->setIQSwap(swapIQ); -} - -bool CubicSDR::getSwapIQ() { - return sdrPostThread->getSwapIQ(); -} - long long CubicSDR::getFrequency() { return frequency; } +void CubicSDR::setSampleRate(long long rate_in) { + sampleRate = rate_in; + sdrThread->setSampleRate(sampleRate); + setFrequency(frequency); +} + +void CubicSDR::setDevice(SDRDeviceInfo *dev) { + if (!sdrThread->isTerminated()) { + sdrThread->terminate(); + if (t_SDR) { + t_SDR->join(); + delete t_SDR; + } + } + + for (SoapySDR::Kwargs::const_iterator i = settingArgs.begin(); i != settingArgs.end(); i++) { + sdrThread->writeSetting(i->first, i->second); + } + sdrThread->setStreamArgs(streamArgs); + sdrThread->setDevice(dev); + + DeviceConfig *devConfig = config.getDevice(dev->getDeviceId()); + + SDRDeviceChannel *chan = dev->getRxChannel(); + + if (chan) { + long long freqHigh, freqLow; + + freqHigh = chan->getRFRange().getHigh(); + freqLow = chan->getRFRange().getLow(); + +// upconverter settings don't like this, need to handle elsewhere.. +// if (frequency > freqHigh) { +// frequency = freqHigh; +// } +// else if (frequency < freqLow) { +// frequency = freqLow; +// } + + // Try for a reasonable default sample rate. + if (!sampleRateInitialized.load()) { + sampleRate = chan->getSampleRateNear(DEFAULT_SAMPLE_RATE); + sampleRateInitialized.store(true); + } + + int rateHigh, rateLow; + + rateHigh = rateLow = sampleRate; + + if (chan->getSampleRates().size()) { + rateLow = chan->getSampleRates()[0]; + rateHigh = chan->getSampleRates()[chan->getSampleRates().size()-1]; + } + + if (sampleRate > rateHigh) { + sampleRate = rateHigh; + } else if (sampleRate < rateLow) { + sampleRate = rateLow; + } + + if (frequency < sampleRate/2) { + frequency = sampleRate/2; + } + + setFrequency(frequency); + setSampleRate(sampleRate); + + setPPM(devConfig->getPPM()); + setOffset(devConfig->getOffset()); + + t_SDR = new std::thread(&SDRThread::threadMain, sdrThread); + } +} + +SDRDeviceInfo *CubicSDR::getDevice() { + return sdrThread->getDevice(); +} + ScopeVisualProcessor *CubicSDR::getScopeProcessor() { return &scopeProcessor; } @@ -321,11 +480,6 @@ SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() { return demodVisualThread->getProcessor(); } -VisualDataReDistributor *CubicSDR::getSpectrumDistributor() { - return &spectrumDistributor; -} - - DemodulatorThreadOutputQueue* CubicSDR::getAudioVisualQueue() { return pipeAudioVisualData; } @@ -342,6 +496,15 @@ DemodulatorMgr &CubicSDR::getDemodMgr() { return demodMgr; } +SDRPostThread *CubicSDR::getSDRPostThread() { + return sdrPostThread; +} + +SDRThread *CubicSDR::getSDRThread() { + return sdrThread; +} + + void CubicSDR::bindDemodulator(DemodulatorInstance *demod) { if (!demod) { return; @@ -349,14 +512,6 @@ void CubicSDR::bindDemodulator(DemodulatorInstance *demod) { sdrPostThread->bindDemodulator(demod); } -void CubicSDR::setSampleRate(long long rate_in) { - sampleRate = rate_in; - SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_SET_SAMPLERATE); - command.llong_value = rate_in; - pipeSDRCommand->push(command); - setFrequency(frequency); -} - long long CubicSDR::getSampleRate() { return sampleRate; } @@ -370,27 +525,9 @@ void CubicSDR::removeDemodulator(DemodulatorInstance *demod) { } std::vector* CubicSDR::getDevices() { - return &devs; + return devs; } -void CubicSDR::setDevice(int deviceId) { - sdrThread->setDeviceId(deviceId); - SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_SET_DEVICE); - command.llong_value = deviceId; - pipeSDRCommand->push(command); - - SDRDeviceInfo *dev = (*getDevices())[deviceId]; - DeviceConfig *devConfig = config.getDevice(dev->getDeviceId()); - - setPPM(devConfig->getPPM()); - setDirectSampling(devConfig->getDirectSampling()); - setSwapIQ(devConfig->getIQSwap()); - setOffset(devConfig->getOffset()); -} - -int CubicSDR::getDevice() { - return sdrThread->getDeviceId(); -} AppConfig *CubicSDR::getConfig() { return &config; @@ -401,29 +538,20 @@ void CubicSDR::saveConfig() { } void CubicSDR::setPPM(int ppm_in) { - if (sdrThread->getDeviceId() < 0) { - return; - } ppm = ppm_in; + sdrThread->setPPM(ppm); - SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_SET_PPM); - command.llong_value = ppm; - pipeSDRCommand->push(command); - - SDRDeviceInfo *dev = (*getDevices())[getDevice()]; - - config.getDevice(dev->getDeviceId())->setPPM(ppm_in); + SDRDeviceInfo *dev = getDevice(); + if (dev) { + config.getDevice(dev->getDeviceId())->setPPM(ppm_in); + } } int CubicSDR::getPPM() { - if (sdrThread->getDeviceId() < 0) { - return 0; + SDRDeviceInfo *dev = sdrThread->getDevice(); + if (dev) { + ppm = config.getDevice(dev->getDeviceId())->getPPM(); } - SDRDeviceInfo *dev = (*getDevices())[getDevice()]; - - SDRThreadCommand command_ppm(SDRThreadCommand::SDR_THREAD_CMD_SET_PPM); - ppm = config.getDevice(dev->getDeviceId())->getPPM(); - return ppm; } @@ -464,3 +592,57 @@ void CubicSDR::setFrequencySnap(int snap) { int CubicSDR::getFrequencySnap() { return snap; } + +bool CubicSDR::areDevicesReady() { + return devicesReady.load(); +} + +bool CubicSDR::areDevicesEnumerating() { + return !sdrEnum->isTerminated(); +} + +std::string CubicSDR::getNotification() { + std::string msg; + notify_busy.lock(); + msg = notifyMessage; + notify_busy.unlock(); + return msg; +} + +void CubicSDR::setDeviceSelectorClosed() { + deviceSelectorOpen.store(false); +} + +bool CubicSDR::isDeviceSelectorOpen() { + return deviceSelectorOpen.load(); +} + +void CubicSDR::setAGCMode(bool mode) { + agcMode.store(mode); + sdrThread->setAGCMode(mode); +} + +bool CubicSDR::getAGCMode() { + return agcMode.load(); +} + + +void CubicSDR::setGain(std::string name, float gain_in) { + sdrThread->setGain(name,gain_in); +} + +float CubicSDR::getGain(std::string name) { + return sdrThread->getGain(name); +} + +void CubicSDR::setStreamArgs(SoapySDR::Kwargs streamArgs_in) { + streamArgs = streamArgs_in; +} + +void CubicSDR::setDeviceArgs(SoapySDR::Kwargs settingArgs_in) { + settingArgs = settingArgs_in; +} + +bool CubicSDR::getUseLocalMod() { + return useLocalMod.load(); +} \ No newline at end of file diff --git a/src/CubicSDR.h b/src/CubicSDR.h index fb84e99..faff1c3 100644 --- a/src/CubicSDR.h +++ b/src/CubicSDR.h @@ -10,7 +10,12 @@ #include "PrimaryGLContext.h" #include "ThreadQueue.h" -#include "SDRThread.h" +#ifdef USE_RTL_SDR + #include "SDRThread.h" +#else + #include "SoapySDRThread.h" + #include "SDREnumerator.h" +#endif #include "SDRPostThread.h" #include "AudioThread.h" #include "DemodulatorMgr.h" @@ -21,11 +26,16 @@ #include "ScopeVisualProcessor.h" #include "SpectrumVisualProcessor.h" #include "SpectrumVisualDataThread.h" +#include "SDRDevices.h" #include #define NUM_DEMODULATORS 1 +std::string& filterChars(std::string& s, const std::string& allowed); +std::string frequencyToStr(long long freq); +long long strToFrequency(std::string freqStr); + class CubicSDR: public wxApp { public: CubicSDR(); @@ -38,35 +48,36 @@ public: virtual void OnInitCmdLine(wxCmdLineParser& parser); virtual bool OnCmdLineParsed(wxCmdLineParser& parser); + void deviceSelector(); + void sdrThreadNotify(SDRThread::SDRThreadState state, std::string message); + void sdrEnumThreadNotify(SDREnumerator::SDREnumState state, std::string message); + void setFrequency(long long freq); long long getFrequency(); void setOffset(long long ofs); long long getOffset(); - void setDirectSampling(int mode); - int getDirectSampling(); - - void setSwapIQ(bool swapIQ); - bool getSwapIQ(); - void setSampleRate(long long rate_in); long long getSampleRate(); std::vector *getDevices(); - void setDevice(int deviceId); - int getDevice(); + void setDevice(SDRDeviceInfo *dev); + SDRDeviceInfo * getDevice(); ScopeVisualProcessor *getScopeProcessor(); SpectrumVisualProcessor *getSpectrumProcessor(); SpectrumVisualProcessor *getDemodSpectrumProcessor(); - VisualDataReDistributor *getSpectrumDistributor(); DemodulatorThreadOutputQueue* getAudioVisualQueue(); DemodulatorThreadInputQueue* getIQVisualQueue(); DemodulatorThreadInputQueue* getWaterfallVisualQueue(); + DemodulatorThreadInputQueue* getActiveDemodVisualQueue(); DemodulatorMgr &getDemodMgr(); + SDRPostThread *getSDRPostThread(); + SDRThread *getSDRThread(); + void bindDemodulator(DemodulatorInstance *demod); void removeDemodulator(DemodulatorInstance *demod); @@ -82,11 +93,31 @@ public: void showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetMode = FrequencyDialog::FDIALOG_TARGET_DEFAULT); AppFrame *getAppFrame(); + bool areDevicesReady(); + bool areDevicesEnumerating(); + std::string getNotification(); + + void addRemote(std::string remoteAddr); + void removeRemote(std::string remoteAddr); + + void setDeviceSelectorClosed(); + bool isDeviceSelectorOpen(); + + void setAGCMode(bool mode); + bool getAGCMode(); + + void setGain(std::string name, float gain_in); + float getGain(std::string name); + + void setStreamArgs(SoapySDR::Kwargs streamArgs_in); + void setDeviceArgs(SoapySDR::Kwargs settingArgs_in); + + bool getUseLocalMod(); private: AppFrame *appframe; AppConfig config; PrimaryGLContext *m_glContext; - std::vector devs; + std::vector *devs; DemodulatorMgr demodMgr; @@ -94,36 +125,52 @@ private: long long offset; int ppm, snap; long long sampleRate; - int directSamplingMode; + std::atomic_bool agcMode; SDRThread *sdrThread; + SDREnumerator *sdrEnum; SDRPostThread *sdrPostThread; SpectrumVisualDataThread *spectrumVisualThread; SpectrumVisualDataThread *demodVisualThread; - SDRThreadCommandQueue* pipeSDRCommand; SDRThreadIQDataQueue* pipeSDRIQData; DemodulatorThreadInputQueue* pipeIQVisualData; DemodulatorThreadOutputQueue* pipeAudioVisualData; DemodulatorThreadInputQueue* pipeDemodIQVisualData; - DemodulatorThreadInputQueue* pipeSpectrumIQVisualData; DemodulatorThreadInputQueue* pipeWaterfallIQVisualData; + DemodulatorThreadInputQueue* pipeActiveDemodIQVisualData; ScopeVisualProcessor scopeProcessor; - VisualDataReDistributor spectrumDistributor; + SDRDevicesDialog *deviceSelectorDialog; - std::thread *t_SDR; - std::thread *t_PostSDR; - std::thread *t_SpectrumVisual; - std::thread *t_DemodVisual; + SoapySDR::Kwargs streamArgs; + SoapySDR::Kwargs settingArgs; + + std::thread *t_SDR, *t_SDREnum, *t_PostSDR, *t_SpectrumVisual, *t_DemodVisual; + std::atomic_bool devicesReady; + std::atomic_bool deviceSelectorOpen; + std::atomic_bool sampleRateInitialized; + std::atomic_bool useLocalMod; + std::string notifyMessage; + std::mutex notify_busy; }; +#ifdef BUNDLE_SOAPY_MODS +static const wxCmdLineEntryDesc commandLineInfo [] = +{ + { wxCMD_LINE_SWITCH, "h", "help", "Command line parameter help", wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP }, + { wxCMD_LINE_OPTION, "c", "config", "Specify a named configuration to use, i.e. '-c ham'" }, + { wxCMD_LINE_SWITCH, "b", "bundled", "Use bundled SoapySDR modules first instead of local." }, + { wxCMD_LINE_NONE } +}; +#else static const wxCmdLineEntryDesc commandLineInfo [] = { { wxCMD_LINE_SWITCH, "h", "help", "Command line parameter help", wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP }, { wxCMD_LINE_OPTION, "c", "config", "Specify a named configuration to use, i.e. '-c ham'" }, { wxCMD_LINE_NONE } }; +#endif DECLARE_APP(CubicSDR) diff --git a/src/CubicSDRDefs.h b/src/CubicSDRDefs.h index 49df312..07d7d02 100644 --- a/src/CubicSDRDefs.h +++ b/src/CubicSDRDefs.h @@ -27,10 +27,14 @@ const char filePathSeparator = #define BUF_SIZE (16384*6) -#define DEFAULT_SAMPLE_RATE 2400000 +#define DEFAULT_SAMPLE_RATE 2500000 #define DEFAULT_FFT_SIZE 2048 #define DEFAULT_DEMOD_TYPE 1 #define DEFAULT_DEMOD_BW 200000 #define DEFAULT_WATERFALL_LPS 30 + +#define CHANNELIZER_RATE_MAX 400000 + + diff --git a/src/FrequencyDialog.cpp b/src/FrequencyDialog.cpp index bed49cd..5284dd7 100644 --- a/src/FrequencyDialog.cpp +++ b/src/FrequencyDialog.cpp @@ -2,7 +2,6 @@ #include "wx/clipbrd.h" #include -#include #include "CubicSDR.h" wxBEGIN_EVENT_TABLE(FrequencyDialog, wxDialog) @@ -37,75 +36,6 @@ FrequencyDialog::FrequencyDialog(wxWindow * parent, wxWindowID id, const wxStrin dialogText->SetSelection(-1, -1); } -std::string& FrequencyDialog::filterChars(std::string& s, const std::string& allowed) { - s.erase(remove_if(s.begin(), s.end(), [&allowed](const char& c) { - return allowed.find(c) == std::string::npos; - }), s.end()); - return s; -} - -std::string FrequencyDialog::frequencyToStr(long long freq) { - long double freqTemp; - - freqTemp = freq; - std::string suffix(""); - std::stringstream freqStr; - - if (freqTemp >= 1.0e9) { - freqTemp /= 1.0e9; - freqStr << std::setprecision(10); - suffix = std::string("GHz"); - } else if (freqTemp >= 1.0e6) { - freqTemp /= 1.0e6; - freqStr << std::setprecision(7); - suffix = std::string("MHz"); - } else if (freqTemp >= 1.0e3) { - freqTemp /= 1.0e3; - freqStr << std::setprecision(4); - suffix = std::string("KHz"); - } - - freqStr << freqTemp; - freqStr << suffix; - - return freqStr.str(); -} - -long long FrequencyDialog::strToFrequency(std::string freqStr) { - std::string filterStr = filterChars(freqStr, std::string("0123456789.MKGHmkgh")); - - int numLen = filterStr.find_first_not_of("0123456789."); - - if (numLen == std::string::npos) { - numLen = freqStr.length(); - } - - std::string numPartStr = freqStr.substr(0, numLen); - std::string suffixStr = freqStr.substr(numLen); - - std::stringstream numPartStream; - numPartStream.str(numPartStr); - - long double freqTemp = 0; - - numPartStream >> freqTemp; - - if (suffixStr.length()) { - if (suffixStr.find_first_of("Gg") != std::string::npos) { - freqTemp *= 1.0e9; - } else if (suffixStr.find_first_of("Mm") != std::string::npos) { - freqTemp *= 1.0e6; - } else if (suffixStr.find_first_of("Kk") != std::string::npos) { - freqTemp *= 1.0e3; - } else if (suffixStr.find_first_of("Hh") != std::string::npos) { - // ... - } - } else if (numPartStr.find_first_of(".") != std::string::npos || freqTemp <= 3000) { - freqTemp *= 1.0e6; - } - - return (long long) freqTemp; -} void FrequencyDialog::OnChar(wxKeyEvent& event) { int c = event.GetKeyCode(); diff --git a/src/FrequencyDialog.h b/src/FrequencyDialog.h index 4f2b931..d0c57b0 100644 --- a/src/FrequencyDialog.h +++ b/src/FrequencyDialog.h @@ -21,14 +21,10 @@ public: wxTextCtrl * dialogText; - long long strToFrequency(std::string freqStr); - std::string frequencyToStr(long long freq); - private: DemodulatorInstance *activeDemod; void OnEnter ( wxCommandEvent &event ); void OnChar ( wxKeyEvent &event ); - std::string& filterChars(std::string& s, const std::string& allowed); FrequencyDialogTarget targetMode; DECLARE_EVENT_TABLE() }; diff --git a/src/audio/AudioThread.cpp b/src/audio/AudioThread.cpp index 789e016..c330a9d 100644 --- a/src/audio/AudioThread.cpp +++ b/src/audio/AudioThread.cpp @@ -20,7 +20,7 @@ AudioThread::AudioThread() : IOThread(), outputDevice.store(-1); gain.store(1.0); - boundThreads = new std::vector; + boundThreads.store(new std::vector); } AudioThread::~AudioThread() { @@ -428,7 +428,7 @@ bool AudioThread::isActive() { void AudioThread::setActive(bool state) { AudioThreadInput *dummy; - if (state && !active) { + if (state && !active && inputQueue) { while (!inputQueue->empty()) { // flush queue inputQueue->pop(dummy); if (dummy) { @@ -438,10 +438,12 @@ void AudioThread::setActive(bool state) { deviceController[parameters.deviceId]->bindThread(this); } else if (!state && active) { deviceController[parameters.deviceId]->removeThread(this); - while (!inputQueue->empty()) { // flush queue - inputQueue->pop(dummy); - if (dummy) { - dummy->decRefCount(); + if(inputQueue) { + while (!inputQueue->empty()) { // flush queue + inputQueue->pop(dummy); + if (dummy) { + dummy->decRefCount(); + } } } } diff --git a/src/demod/DemodulatorInstance.cpp b/src/demod/DemodulatorInstance.cpp index a8ce1c7..f5d285a 100644 --- a/src/demod/DemodulatorInstance.cpp +++ b/src/demod/DemodulatorInstance.cpp @@ -257,7 +257,9 @@ void DemodulatorInstance::setOutputDevice(int device_id) { int DemodulatorInstance::getOutputDevice() { if (currentOutputDevice == -1) { - currentOutputDevice = audioThread->getOutputDevice(); + if (audioThread) { + currentOutputDevice = audioThread->getOutputDevice(); + } } return currentOutputDevice; @@ -326,7 +328,7 @@ void DemodulatorInstance::setBandwidth(int bw) { bw = AudioThread::deviceSampleRate[getOutputDevice()]; } } - if (!active) { + if (!active && demodulatorPreThread != NULL) { currentBandwidth = bw; checkBandwidth(); demodulatorPreThread->getParams().bandwidth = currentBandwidth; diff --git a/src/demod/DemodulatorPreThread.cpp b/src/demod/DemodulatorPreThread.cpp index 5c3820f..f96dc2c 100644 --- a/src/demod/DemodulatorPreThread.cpp +++ b/src/demod/DemodulatorPreThread.cpp @@ -155,18 +155,20 @@ void DemodulatorPreThread::run() { } if (!initialized) { + inp->decRefCount(); continue; } // Requested frequency is not center, shift it into the center! if ((params.frequency - inp->frequency) != shiftFrequency || rateChanged) { shiftFrequency = params.frequency - inp->frequency; - if (abs(shiftFrequency) <= (int) ((double) (wxGetApp().getSampleRate() / 2) * 1.5)) { - nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) wxGetApp().getSampleRate()))); + if (abs(shiftFrequency) <= (int) ((double) (inp->sampleRate / 2) * 1.5)) { + nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) inp->sampleRate))); } } - if (abs(shiftFrequency) > (int) ((double) (wxGetApp().getSampleRate() / 2) * 1.5)) { + if (abs(shiftFrequency) > (int) ((double) (inp->sampleRate / 2) * 1.5)) { + inp->decRefCount(); continue; } diff --git a/src/demod/DemodulatorThread.cpp b/src/demod/DemodulatorThread.cpp index c9b831b..b46ff03 100644 --- a/src/demod/DemodulatorThread.cpp +++ b/src/demod/DemodulatorThread.cpp @@ -126,14 +126,13 @@ void DemodulatorThread::run() { nco_crcf_pll_set_bandwidth(stereoPilot, 0.25f); // half band filter used for side-band elimination - resamp2_cccf ssbFilt = resamp2_cccf_create(12,-0.25f,60.0f); + resamp2_crcf ssbFilt = resamp2_crcf_create(12,-0.25f,60.0f); // Automatic IQ gain iqAutoGain = agc_crcf_create(); agc_crcf_set_bandwidth(iqAutoGain, 0.1); - AudioThreadInput *ati_vis = new AudioThreadInput; - ati_vis->data.reserve(DEMOD_VIS_SIZE); + ReBuffer audioVisBuffers; std::cout << "Demodulator thread started.." << std::endl; @@ -271,14 +270,14 @@ void DemodulatorThread::run() { case DEMOD_TYPE_LSB: currentDemodLock = false; for (int i = 0; i < bufSize; i++) { // Reject upper band - resamp2_cccf_filter_execute(ssbFilt,(*inputData)[i],&x,&y); + resamp2_crcf_filter_execute(ssbFilt,(*inputData)[i],&x,&y); ampmodem_demodulate(demodAM, x, &demodOutputData[i]); } break; case DEMOD_TYPE_USB: currentDemodLock = false; for (int i = 0; i < bufSize; i++) { // Reject lower band - resamp2_cccf_filter_execute(ssbFilt,(*inputData)[i],&x,&y); + resamp2_crcf_filter_execute(ssbFilt,(*inputData)[i],&x,&y); ampmodem_demodulate(demodAM, y, &demodOutputData[i]); } break; @@ -734,8 +733,8 @@ void DemodulatorThread::run() { } if (ati && audioVisOutputQueue != NULL && audioVisOutputQueue->empty()) { - - ati_vis->busy_update.lock(); + AudioThreadInput *ati_vis = audioVisBuffers.getBuffer(); + ati_vis->setRefCount(1); ati_vis->sampleRate = inp->sampleRate; ati_vis->inputRate = inp->sampleRate; @@ -780,7 +779,6 @@ void DemodulatorThread::run() { // std::cout << "Signal: " << agc_crcf_get_signal_level(agc) << " -- " << agc_crcf_get_rssi(agc) << "dB " << std::endl; } - ati_vis->busy_update.unlock(); audioVisOutputQueue->push(ati_vis); } @@ -899,7 +897,7 @@ void DemodulatorThread::run() { firhilbf_destroy(firStereoR2C); firhilbf_destroy(firStereoC2R); nco_crcf_destroy(stereoPilot); - resamp2_cccf_destroy(ssbFilt); + resamp2_crcf_destroy(ssbFilt); outputBuffers.purge(); @@ -907,7 +905,7 @@ void DemodulatorThread::run() { AudioThreadInput *dummy_vis; audioVisOutputQueue->pop(dummy_vis); } - delete ati_vis; + audioVisBuffers.purge(); DemodulatorThreadCommand tCmd(DemodulatorThreadCommand::DEMOD_THREAD_CMD_DEMOD_TERMINATED); tCmd.context = this; diff --git a/src/forms/SDRDevices/SDRDevices.cpp b/src/forms/SDRDevices/SDRDevices.cpp new file mode 100644 index 0000000..3839c99 --- /dev/null +++ b/src/forms/SDRDevices/SDRDevices.cpp @@ -0,0 +1,267 @@ +#include "SDRDevices.h" + +#include +#include + +#include "CubicSDR.h" + +SDRDevicesDialog::SDRDevicesDialog( wxWindow* parent ): devFrame( parent ) { + refresh = true; + m_addRemoteButton->Disable(); + m_useSelectedButton->Disable(); + m_deviceTimer.Start(250); + + } + +void SDRDevicesDialog::OnClose( wxCloseEvent& event ) { + wxGetApp().setDeviceSelectorClosed(); + Destroy(); +} + +void SDRDevicesDialog::OnDeleteItem( wxTreeEvent& event ) { + event.Skip(); +} + +wxPGProperty *SDRDevicesDialog::addArgInfoProperty(wxPropertyGrid *pg, SoapySDR::ArgInfo arg) { + + wxPGProperty *prop = NULL; + + int intVal; + double floatVal; + std::vector::iterator stringIter; + + switch (arg.type) { + case SoapySDR::ArgInfo::INT: + try { + intVal = std::stoi(arg.value); + } catch (std::invalid_argument e) { + intVal = 0; + } + prop = pg->Append( new wxIntProperty(arg.name, wxPG_LABEL, intVal) ); + if (arg.range.minimum() != arg.range.maximum()) { + pg->SetPropertyAttribute( prop, wxPG_ATTR_MIN, arg.range.minimum()); + pg->SetPropertyAttribute( prop, wxPG_ATTR_MAX, arg.range.maximum()); + } + break; + case SoapySDR::ArgInfo::FLOAT: + try { + floatVal = std::stod(arg.value); + } catch (std::invalid_argument e) { + floatVal = 0; + } + prop = pg->Append( new wxFloatProperty(arg.name, wxPG_LABEL, floatVal) ); + if (arg.range.minimum() != arg.range.maximum()) { + pg->SetPropertyAttribute( prop, wxPG_ATTR_MIN, arg.range.minimum()); + pg->SetPropertyAttribute( prop, wxPG_ATTR_MAX, arg.range.maximum()); + } + break; + case SoapySDR::ArgInfo::BOOL: + prop = pg->Append( new wxBoolProperty(arg.name, wxPG_LABEL, (arg.value=="true")) ); + break; + case SoapySDR::ArgInfo::STRING: + if (arg.options.size()) { + intVal = 0; + prop = pg->Append( new wxEnumProperty(arg.name, wxPG_LABEL) ); + for (stringIter = arg.options.begin(); stringIter != arg.options.end(); stringIter++) { + std::string optName = (*stringIter); + std::string displayName = optName; + if (arg.optionNames.size()) { + displayName = arg.optionNames[intVal]; + } + + prop->AddChoice(displayName); + if ((*stringIter)==arg.value) { + prop->SetChoiceSelection(intVal); + } + + intVal++; + } + } else { + prop = pg->Append( new wxStringProperty(arg.name, wxPG_LABEL, arg.value) ); + } + break; + } + + if (prop != NULL) { + prop->SetHelpString(arg.key + ": " + arg.description); + } + + return prop; +} + +void SDRDevicesDialog::OnSelectionChanged( wxTreeEvent& event ) { + wxTreeItemId selId = devTree->GetSelection(); + + dev = getSelectedDevice(selId); + props.erase(props.begin(), props.end()); + if (dev) { + m_propertyGrid->Clear(); + m_propertyGrid->Append(new wxPropertyCategory("Run-time Settings")); + + SoapySDR::ArgInfoList::const_iterator args_i; + + SoapySDR::ArgInfoList args = dev->getSettingsArgInfo(); + + for (args_i = args.begin(); args_i != args.end(); args_i++) { + SoapySDR::ArgInfo arg = (*args_i); + props.push_back(addArgInfoProperty(m_propertyGrid, arg)); + } + + if (dev->getRxChannel()) { + args = dev->getRxChannel()->getStreamArgsInfo(); + + if (args.size()) { + m_propertyGrid->Append(new wxPropertyCategory("Stream Settings")); + + for (args_i = args.begin(); args_i != args.end(); args_i++) { + SoapySDR::ArgInfo arg = (*args_i); + props.push_back(addArgInfoProperty(m_propertyGrid, arg)); + } + } + } + + } + event.Skip(); +} + +void SDRDevicesDialog::OnAddRemote( wxMouseEvent& event ) { + if (!SDREnumerator::hasRemoteModule()) { + wxMessageDialog *info; + info = new wxMessageDialog(NULL, wxT("Install SoapyRemote module to add remote servers.\n\nhttps://github.com/pothosware/SoapyRemote"), wxT("SoapyRemote not found."), wxOK | wxICON_ERROR); + info->ShowModal(); + return; + } + + wxString remoteAddr = + wxGetTextFromUser("Remote Address (address[:port])\n\ni.e. 'raspberrypi.local', '192.168.1.103:1234'\n","SoapySDR Remote Address", "", this); + + if (!remoteAddr.Trim().empty()) { + wxGetApp().addRemote(remoteAddr.Trim().ToStdString()); + } + devTree->Disable(); + m_addRemoteButton->Disable(); + m_useSelectedButton->Disable(); + refresh = true; + +} + +SDRDeviceInfo *SDRDevicesDialog::getSelectedDevice(wxTreeItemId selId) { + devItems_i = devItems.find(selId); + if (devItems_i != devItems.end()) { + return devItems[selId]; + } + return NULL; +} + +void SDRDevicesDialog::OnUseSelected( wxMouseEvent& event ) { + wxTreeItemId selId = devTree->GetSelection(); + + dev = getSelectedDevice(selId); + if (dev != NULL) { + + int i = 0; + SoapySDR::ArgInfoList::const_iterator args_i; + SoapySDR::ArgInfoList args = dev->getSettingsArgInfo(); + + SoapySDR::Kwargs settingArgs; + SoapySDR::Kwargs streamArgs; + + for (args_i = args.begin(); args_i != args.end(); args_i++) { + SoapySDR::ArgInfo arg = (*args_i); + wxPGProperty *prop = props[i]; + + if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size()) { + settingArgs[arg.key] = arg.options[prop->GetChoiceSelection()]; + } else if (arg.type == SoapySDR::ArgInfo::BOOL) { + settingArgs[arg.key] = (prop->GetValueAsString()=="True")?"true":"false"; + } else { + settingArgs[arg.key] = prop->GetValueAsString(); + } + + i++; + } + + if (dev->getRxChannel()) { + args = dev->getRxChannel()->getStreamArgsInfo(); + + if (args.size()) { + for (args_i = args.begin(); args_i != args.end(); args_i++) { + SoapySDR::ArgInfo arg = (*args_i); + wxPGProperty *prop = props[i]; + + if (arg.type == SoapySDR::ArgInfo::STRING && arg.options.size()) { + streamArgs[arg.key] = arg.options[prop->GetChoiceSelection()]; + } else if (arg.type == SoapySDR::ArgInfo::BOOL) { + streamArgs[arg.key] = (prop->GetValueAsString()=="True")?"true":"false"; + } else { + streamArgs[arg.key] = prop->GetValueAsString(); + } + + i++; + } + } + } + + + wxGetApp().setDeviceArgs(settingArgs); + wxGetApp().setStreamArgs(streamArgs); + wxGetApp().setDevice(dev); + Close(); + } +} + +void SDRDevicesDialog::OnTreeDoubleClick( wxMouseEvent& event ) { + OnUseSelected(event); +} + +void SDRDevicesDialog::OnDeviceTimer( wxTimerEvent& event ) { + if (refresh) { + if (wxGetApp().areDevicesEnumerating() || !wxGetApp().areDevicesReady()) { + std::string msg = wxGetApp().getNotification(); + devStatusBar->SetStatusText(msg); + devTree->DeleteAllItems(); + devTree->AddRoot(msg); + event.Skip(); + return; + } + + devTree->DeleteAllItems(); + + wxTreeItemId devRoot = devTree->AddRoot("Devices"); + wxTreeItemId localBranch = devTree->AppendItem(devRoot, "Local"); + wxTreeItemId remoteBranch = devTree->AppendItem(devRoot, "Remote"); + + devs[""] = SDREnumerator::enumerate_devices("",true); + if (devs[""] != NULL) { + for (devs_i = devs[""]->begin(); devs_i != devs[""]->end(); devs_i++) { + devItems[devTree->AppendItem(localBranch, (*devs_i)->getName())] = (*devs_i); + } + } + + std::vector remotes = SDREnumerator::getRemotes(); + std::vector::iterator remotes_i; + std::vector::iterator remoteDevs_i; + + if (remotes.size()) { + for (remotes_i = remotes.begin(); remotes_i != remotes.end(); remotes_i++) { + devs[*remotes_i] = SDREnumerator::enumerate_devices(*remotes_i, true); + wxTreeItemId remoteNode = devTree->AppendItem(remoteBranch, *remotes_i); + + if (devs[*remotes_i] != NULL) { + for (remoteDevs_i = devs[*remotes_i]->begin(); remoteDevs_i != devs[*remotes_i]->end(); remoteDevs_i++) { + devItems[devTree->AppendItem(remoteNode, (*remoteDevs_i)->getName())] = (*remoteDevs_i); + } + } + } + } + + m_addRemoteButton->Enable(); + m_useSelectedButton->Enable(); + devTree->Enable(); + devTree->ExpandAll(); + + devStatusBar->SetStatusText("Ready."); + + refresh = false; + } +} \ No newline at end of file diff --git a/src/forms/SDRDevices/SDRDevices.fbp b/src/forms/SDRDevices/SDRDevices.fbp new file mode 100644 index 0000000..9d67c3f --- /dev/null +++ b/src/forms/SDRDevices/SDRDevices.fbp @@ -0,0 +1,945 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + SDRDevicesForm + 1000 + none + 0 + SDRDevicesForm + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + devFrame + + 700,467 + wxDEFAULT_FRAME_STYLE + + CubicSDR :: SDR Devices + + + + wxTAB_TRAVERSAL + 1 + + + + + + + + + + OnClose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + 1 + + 1 + + 0 + wxID_ANY + + + devStatusBar + protected + + + wxST_SIZEGRIP + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + devFrameSizer + wxVERTICAL + none + + 5 + wxEXPAND | wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_panel3 + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer4 + wxHORIZONTAL + none + + 5 + wxEXPAND | wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_panel6 + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer6 + wxVERTICAL + none + + 5 + wxEXPAND|wxALIGN_RIGHT + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + devTree + 1 + + + protected + 1 + + Resizable + 1 + + wxTR_DEFAULT_STYLE + + 0 + + + + + + + + + + + + OnTreeDoubleClick + + + + + + + + + + + + + + + + + + OnDeleteItem + + + + + + + + + + + + + + OnSelectionChanged + + + + + + + + 5 + wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_panel4 + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer5 + wxHORIZONTAL + none + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Add Remote + + 0 + + + 0 + + 1 + m_addRemoteButton + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + OnAddRemote + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Use Selected + + 0 + + + 0 + + 1 + m_useSelectedButton + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + OnUseSelected + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND | wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_panel61 + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + wxTAB_TRAVERSAL + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer7 + wxVERTICAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + SoapySDR Device Options + + 0 + + + 0 + + 1 + m_staticText1 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + + 1 + + 0 + 0 + wxID_ANY + 1 + + 0 + + + 0 + + 1 + m_propertyGrid + 1 + + + protected + 1 + + Resizable + 1 + + wxPG_DEFAULT_STYLE + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + wxID_ANY + m_deviceTimer + 0 + 5000 + protected + OnDeviceTimer + + + + diff --git a/src/forms/SDRDevices/SDRDevices.h b/src/forms/SDRDevices/SDRDevices.h new file mode 100644 index 0000000..74441ae --- /dev/null +++ b/src/forms/SDRDevices/SDRDevices.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +#include "SDRDevices.h" +#include "SDRDevicesForm.h" +#include "SoapySDRThread.h" +#include "SDREnumerator.h" + +class SDRDevicesDialog: public devFrame { +public: + SDRDevicesDialog( wxWindow* parent ); + + void OnClose( wxCloseEvent& event ); + void OnDeleteItem( wxTreeEvent& event ); + void OnSelectionChanged( wxTreeEvent& event ); + void OnAddRemote( wxMouseEvent& event ); + void OnUseSelected( wxMouseEvent& event ); + void OnTreeDoubleClick( wxMouseEvent& event ); + void OnDeviceTimer( wxTimerEvent& event ); + +private: + SDRDeviceInfo *getSelectedDevice(wxTreeItemId selId); + wxPGProperty *addArgInfoProperty(wxPropertyGrid *pg, SoapySDR::ArgInfo arg); + + bool refresh; + std::map* > devs; + std::vector::iterator devs_i; + std::map devItems; + std::map::iterator devItems_i; + SDRDeviceInfo *dev = NULL; + std::vector props; +}; \ No newline at end of file diff --git a/src/forms/SDRDevices/SDRDevicesForm.cpp b/src/forms/SDRDevices/SDRDevicesForm.cpp new file mode 100644 index 0000000..8863060 --- /dev/null +++ b/src/forms/SDRDevices/SDRDevicesForm.cpp @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Aug 23 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "SDRDevicesForm.h" + +/////////////////////////////////////////////////////////////////////////// + +devFrame::devFrame( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxDefaultSize, wxDefaultSize ); + + devStatusBar = this->CreateStatusBar( 1, wxST_SIZEGRIP, wxID_ANY ); + wxBoxSizer* devFrameSizer; + devFrameSizer = new wxBoxSizer( wxVERTICAL ); + + m_panel3 = new wxPanel( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer4; + bSizer4 = new wxBoxSizer( wxHORIZONTAL ); + + m_panel6 = new wxPanel( m_panel3, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer6; + bSizer6 = new wxBoxSizer( wxVERTICAL ); + + devTree = new wxTreeCtrl( m_panel6, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE ); + devTree->Enable( false ); + + bSizer6->Add( devTree, 1, wxEXPAND|wxALIGN_RIGHT, 5 ); + + m_panel4 = new wxPanel( m_panel6, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer5; + bSizer5 = new wxBoxSizer( wxHORIZONTAL ); + + m_addRemoteButton = new wxButton( m_panel4, wxID_ANY, wxT("Add Remote"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer5->Add( m_addRemoteButton, 1, wxALL, 5 ); + + m_useSelectedButton = new wxButton( m_panel4, wxID_ANY, wxT("Use Selected"), wxDefaultPosition, wxDefaultSize, 0 ); + bSizer5->Add( m_useSelectedButton, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + + m_panel4->SetSizer( bSizer5 ); + m_panel4->Layout(); + bSizer5->Fit( m_panel4 ); + bSizer6->Add( m_panel4, 0, wxEXPAND, 5 ); + + + m_panel6->SetSizer( bSizer6 ); + m_panel6->Layout(); + bSizer6->Fit( m_panel6 ); + bSizer4->Add( m_panel6, 1, wxEXPAND | wxALL, 5 ); + + m_panel61 = new wxPanel( m_panel3, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL ); + wxBoxSizer* bSizer7; + bSizer7 = new wxBoxSizer( wxVERTICAL ); + + m_staticText1 = new wxStaticText( m_panel61, wxID_ANY, wxT("SoapySDR Device Options"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText1->Wrap( -1 ); + bSizer7->Add( m_staticText1, 0, wxALL, 5 ); + + m_propertyGrid = new wxPropertyGrid(m_panel61, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPG_DEFAULT_STYLE); + bSizer7->Add( m_propertyGrid, 1, wxALL|wxEXPAND, 5 ); + + + m_panel61->SetSizer( bSizer7 ); + m_panel61->Layout(); + bSizer7->Fit( m_panel61 ); + bSizer4->Add( m_panel61, 1, wxEXPAND | wxALL, 5 ); + + + m_panel3->SetSizer( bSizer4 ); + m_panel3->Layout(); + bSizer4->Fit( m_panel3 ); + devFrameSizer->Add( m_panel3, 1, wxEXPAND | wxALL, 5 ); + + + this->SetSizer( devFrameSizer ); + this->Layout(); + m_deviceTimer.SetOwner( this, wxID_ANY ); + + this->Centre( wxBOTH ); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( devFrame::OnClose ) ); + devTree->Connect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( devFrame::OnTreeDoubleClick ), NULL, this ); + devTree->Connect( wxEVT_COMMAND_TREE_DELETE_ITEM, wxTreeEventHandler( devFrame::OnDeleteItem ), NULL, this ); + devTree->Connect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( devFrame::OnSelectionChanged ), NULL, this ); + m_addRemoteButton->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnAddRemote ), NULL, this ); + m_useSelectedButton->Connect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnUseSelected ), NULL, this ); + this->Connect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( devFrame::OnDeviceTimer ) ); +} + +devFrame::~devFrame() +{ + // Disconnect Events + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( devFrame::OnClose ) ); + devTree->Disconnect( wxEVT_LEFT_DCLICK, wxMouseEventHandler( devFrame::OnTreeDoubleClick ), NULL, this ); + devTree->Disconnect( wxEVT_COMMAND_TREE_DELETE_ITEM, wxTreeEventHandler( devFrame::OnDeleteItem ), NULL, this ); + devTree->Disconnect( wxEVT_COMMAND_TREE_SEL_CHANGED, wxTreeEventHandler( devFrame::OnSelectionChanged ), NULL, this ); + m_addRemoteButton->Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnAddRemote ), NULL, this ); + m_useSelectedButton->Disconnect( wxEVT_LEFT_UP, wxMouseEventHandler( devFrame::OnUseSelected ), NULL, this ); + this->Disconnect( wxID_ANY, wxEVT_TIMER, wxTimerEventHandler( devFrame::OnDeviceTimer ) ); + +} diff --git a/src/forms/SDRDevices/SDRDevicesForm.h b/src/forms/SDRDevices/SDRDevicesForm.h new file mode 100644 index 0000000..efaa3e1 --- /dev/null +++ b/src/forms/SDRDevices/SDRDevicesForm.h @@ -0,0 +1,73 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Aug 23 2015) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __SDRDEVICESFORM_H__ +#define __SDRDEVICESFORM_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class devFrame +/////////////////////////////////////////////////////////////////////////////// +class devFrame : public wxFrame +{ + private: + + protected: + wxStatusBar* devStatusBar; + wxPanel* m_panel3; + wxPanel* m_panel6; + wxTreeCtrl* devTree; + wxPanel* m_panel4; + wxButton* m_addRemoteButton; + wxButton* m_useSelectedButton; + wxPanel* m_panel61; + wxStaticText* m_staticText1; + wxPropertyGrid* m_propertyGrid; + wxTimer m_deviceTimer; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnTreeDoubleClick( wxMouseEvent& event ) { event.Skip(); } + virtual void OnDeleteItem( wxTreeEvent& event ) { event.Skip(); } + virtual void OnSelectionChanged( wxTreeEvent& event ) { event.Skip(); } + virtual void OnAddRemote( wxMouseEvent& event ) { event.Skip(); } + virtual void OnUseSelected( wxMouseEvent& event ) { event.Skip(); } + virtual void OnDeviceTimer( wxTimerEvent& event ) { event.Skip(); } + + + public: + + devFrame( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = wxT("CubicSDR :: SDR Devices"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 700,467 ), long style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL ); + + ~devFrame(); + +}; + +#endif //__SDRDEVICESFORM_H__ diff --git a/src/panel/WaterfallPanel.cpp b/src/panel/WaterfallPanel.cpp index 5de721a..6ad0161 100644 --- a/src/panel/WaterfallPanel.cpp +++ b/src/panel/WaterfallPanel.cpp @@ -10,6 +10,7 @@ WaterfallPanel::WaterfallPanel() : GLPanel(), fft_size(0), waterfall_lines(0), w void WaterfallPanel::setup(int fft_size_in, int num_waterfall_lines_in) { waterfall_lines = num_waterfall_lines_in; fft_size = fft_size_in; + lines_buffered.store(0); if (points.size() != fft_size) { points.resize(fft_size); @@ -23,6 +24,8 @@ void WaterfallPanel::setup(int fft_size_in, int num_waterfall_lines_in) { waterfall_ofs[i] = waterfall_lines - 1; } + texInitialized.store(false); + bufferInitialized.store(false); } void WaterfallPanel::refreshTheme() { @@ -52,7 +55,47 @@ void WaterfallPanel::setPoints(std::vector &points) { void WaterfallPanel::step() { int half_fft_size = fft_size / 2; - if (!waterfall[0]) { + if (!bufferInitialized.load()) { + if (waterfall_slice != NULL) { + delete waterfall_slice; + } + waterfall_slice = new unsigned char[half_fft_size]; + bufferInitialized.store(true); + } + + if (!texInitialized.load()) { + return; + } + + if (points.size()) { + for (int j = 0; j < 2; j++) { + for (int i = 0, iMax = half_fft_size; i < iMax; i++) { + float v = points[j * half_fft_size + i]; + + float wv = v < 0 ? 0 : (v > 0.99 ? 0.99 : v); + + waterfall_slice[i] = (unsigned char) floor(wv * 255.0); + } + + int newBufSize = (half_fft_size*lines_buffered.load()+half_fft_size); + if (lineBuffer[j].size() < newBufSize) { + lineBuffer[j].resize(newBufSize); + rLineBuffer[j].resize(newBufSize); + } + memcpy(&(lineBuffer[j][half_fft_size*lines_buffered.load()]), waterfall_slice, sizeof(unsigned char) * half_fft_size); + } + lines_buffered++; + } +} + +void WaterfallPanel::update() { + int half_fft_size = fft_size / 2; + + if (!bufferInitialized.load()) { + return; + } + + if (!texInitialized.load()) { glGenTextures(2, waterfall); unsigned char *waterfall_tex; @@ -73,38 +116,42 @@ void WaterfallPanel::step() { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, half_fft_size, waterfall_lines, 0, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_tex); } - if (waterfall_slice != NULL) { - delete waterfall_slice; - } - waterfall_slice = new unsigned char[half_fft_size]; - delete[] waterfall_tex; + + texInitialized.store(true); } - if (points.size()) { + for (int i = 0, iMax = lines_buffered.load(); i < iMax; i++) { + for (int j = 0; j < 2; j++) { + memcpy(&(rLineBuffer[j][i*half_fft_size]), + &(lineBuffer[j][((iMax-1)*half_fft_size)-(i*half_fft_size)]), sizeof(unsigned char) * half_fft_size); + } + } + + int run_ofs = 0; + while (lines_buffered.load()) { + int run_lines = lines_buffered.load(); + if (run_lines > waterfall_ofs[0]) { + run_lines = waterfall_ofs[0]; + } for (int j = 0; j < 2; j++) { - for (int i = 0, iMax = half_fft_size; i < iMax; i++) { - float v = points[j * half_fft_size + i]; - - float wv = v < 0 ? 0 : (v > 0.99 ? 0.99 : v); - - waterfall_slice[i] = (unsigned char) floor(wv * 255.0); - } - glBindTexture(GL_TEXTURE_2D, waterfall[j]); - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, waterfall_ofs[j], half_fft_size, 1, GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) waterfall_slice); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, waterfall_ofs[j]-run_lines, half_fft_size, run_lines, + GL_COLOR_INDEX, GL_UNSIGNED_BYTE, (GLvoid *) &(rLineBuffer[j][run_ofs])); + + waterfall_ofs[j]-=run_lines; if (waterfall_ofs[j] == 0) { waterfall_ofs[j] = waterfall_lines; } - - waterfall_ofs[j]--; } + run_ofs += run_lines*half_fft_size; + lines_buffered.store(lines_buffered.load()-run_lines); } } void WaterfallPanel::drawPanelContents() { - if (!waterfall[0]) { + if (!texInitialized.load()) { return; } @@ -130,7 +177,7 @@ void WaterfallPanel::drawPanelContents() { float half_pixel = 1.0 / viewWidth; float half_texel = 1.0 / (float) half_fft_size; float vtexel = 1.0 / (float) waterfall_lines; - float vofs = (float) (waterfall_ofs[0] + 1) * vtexel; + float vofs = (float) (waterfall_ofs[0]) * vtexel; glBindTexture(GL_TEXTURE_2D, waterfall[0]); glBegin (GL_QUADS); @@ -144,7 +191,7 @@ void WaterfallPanel::drawPanelContents() { glVertex3f(-1.0, 1.0, 0.0); glEnd(); - vofs = (float) (waterfall_ofs[1] + 1) * vtexel; + vofs = (float) (waterfall_ofs[1]) * vtexel; glBindTexture(GL_TEXTURE_2D, waterfall[1]); glBegin(GL_QUADS); glTexCoord2f(0.0 + half_texel, 1.0 + vofs); diff --git a/src/panel/WaterfallPanel.h b/src/panel/WaterfallPanel.h index 65c664a..8c24eea 100644 --- a/src/panel/WaterfallPanel.h +++ b/src/panel/WaterfallPanel.h @@ -1,6 +1,7 @@ #pragma once #include "GLPanel.h" +#include class WaterfallPanel : public GLPanel { public: @@ -9,7 +10,8 @@ public: void refreshTheme(); void setPoints(std::vector &points); void step(); - + void update(); + protected: void drawPanelContents(); @@ -21,6 +23,10 @@ private: int fft_size; int waterfall_lines; unsigned char *waterfall_slice; + std::vector lineBuffer[2]; + std::vector rLineBuffer[2]; + std::atomic_int lines_buffered; + std::atomic_bool texInitialized, bufferInitialized; ColorTheme *activeTheme; }; diff --git a/src/process/FFTDataDistributor.cpp b/src/process/FFTDataDistributor.cpp index 99bb38f..a2e10db 100644 --- a/src/process/FFTDataDistributor.cpp +++ b/src/process/FFTDataDistributor.cpp @@ -1,6 +1,7 @@ #include "FFTDataDistributor.h" FFTDataDistributor::FFTDataDistributor() : linesPerSecond(DEFAULT_WATERFALL_LPS), lineRateAccum(0.0), fftSize(DEFAULT_FFT_SIZE) { + bufferedItems = 0; } void FFTDataDistributor::setFFTSize(int fftSize) { @@ -25,34 +26,44 @@ void FFTDataDistributor::process() { if (inp) { if (inputBuffer.sampleRate != inp->sampleRate || inputBuffer.frequency != inp->frequency) { + + bufferMax = inp->sampleRate / 4; +// std::cout << "Buffer Max: " << bufferMax << std::endl; + bufferOffset = 0; + inputBuffer.sampleRate = inp->sampleRate; inputBuffer.frequency = inp->frequency; - inputBuffer.data.assign(inp->data.begin(), inp->data.end()); - } else { - inputBuffer.data.insert(inputBuffer.data.end(), inp->data.begin(), inp->data.end()); + inputBuffer.data.resize(bufferMax); } + if ((bufferOffset + bufferedItems + inp->data.size()) > bufferMax) { + memmove(&inputBuffer.data[0], &inputBuffer.data[bufferOffset], bufferedItems*sizeof(liquid_float_complex)); + bufferOffset = 0; + } else { + memcpy(&inputBuffer.data[bufferOffset+bufferedItems],&inp->data[0],inp->data.size()*sizeof(liquid_float_complex)); + bufferedItems += inp->data.size(); + } inp->decRefCount(); } else { continue; } // number of seconds contained in input - double inputTime = (double)inputBuffer.data.size() / (double)inputBuffer.sampleRate; + double inputTime = (double)bufferedItems / (double)inputBuffer.sampleRate; // number of lines in input - double inputLines = (double)inputBuffer.data.size()/(double)fftSize; + double inputLines = (double)bufferedItems / (double)fftSize; // ratio required to achieve the desired rate double lineRateStep = ((double)linesPerSecond * inputTime)/(double)inputLines; - if (inputBuffer.data.size() >= fftSize) { + if (bufferedItems >= fftSize) { int numProcessed = 0; - if (lineRateAccum + (lineRateStep * ((double)inputBuffer.data.size()/(double)fftSize)) < 1.0) { + if (lineRateAccum + (lineRateStep * ((double)bufferedItems/(double)fftSize)) < 1.0) { // move along, nothing to see here.. - lineRateAccum += (lineRateStep * ((double)inputBuffer.data.size()/(double)fftSize)); - numProcessed = inputBuffer.data.size(); + lineRateAccum += (lineRateStep * ((double)bufferedItems/(double)fftSize)); + numProcessed = bufferedItems; } else { - for (int i = 0, iMax = inputBuffer.data.size(); i < iMax; i += fftSize) { + for (int i = 0, iMax = bufferedItems; i < iMax; i += fftSize) { if ((i + fftSize) > iMax) { break; } @@ -62,7 +73,7 @@ void FFTDataDistributor::process() { DemodulatorThreadIQData *outp = outputBuffers.getBuffer(); outp->frequency = inputBuffer.frequency; outp->sampleRate = inputBuffer.sampleRate; - outp->data.assign(inputBuffer.data.begin()+i,inputBuffer.data.begin()+i+fftSize); + outp->data.assign(inputBuffer.data.begin()+bufferOffset+i,inputBuffer.data.begin()+bufferOffset+i+fftSize); distribute(outp); while (lineRateAccum >= 1.0) { @@ -74,8 +85,13 @@ void FFTDataDistributor::process() { } } if (numProcessed) { - inputBuffer.data.erase(inputBuffer.data.begin(), inputBuffer.data.begin() + numProcessed); - } + bufferedItems -= numProcessed; + bufferOffset += numProcessed; + } + if (bufferedItems <= 0) { + bufferedItems = 0; + bufferOffset = 0; + } } } } diff --git a/src/process/FFTDataDistributor.h b/src/process/FFTDataDistributor.h index 561a0e7..677248e 100644 --- a/src/process/FFTDataDistributor.h +++ b/src/process/FFTDataDistributor.h @@ -3,6 +3,7 @@ #include "VisualProcessor.h" #include "DemodDefs.h" #include +#include class FFTDataDistributor : public VisualProcessor { public: @@ -19,4 +20,5 @@ protected: int fftSize; int linesPerSecond; double lineRateAccum; + int bufferMax, bufferOffset, bufferedItems; }; diff --git a/src/process/ScopeVisualProcessor.cpp b/src/process/ScopeVisualProcessor.cpp index 7282ec9..dad6a03 100644 --- a/src/process/ScopeVisualProcessor.cpp +++ b/src/process/ScopeVisualProcessor.cpp @@ -6,6 +6,8 @@ ScopeVisualProcessor::ScopeVisualProcessor(): fftInData(NULL), fftwOutput(NULL), scopeEnabled.store(true); spectrumEnabled.store(true); fft_average_rate = 0.65; + fft_ceil_ma = fft_ceil_maa = 0; + fft_floor_ma = fft_floor_maa = 0; } ScopeVisualProcessor::~ScopeVisualProcessor() { @@ -66,9 +68,7 @@ void ScopeVisualProcessor::process() { audioInputData->decRefCount(); return; } - - audioInputData->busy_update.lock(); - + ScopeRenderData *renderData = NULL; if (scopeEnabled) { @@ -81,7 +81,7 @@ void ScopeVisualProcessor::process() { renderData->channels = audioInputData->channels; renderData->inputRate = audioInputData->inputRate; renderData->sampleRate = audioInputData->sampleRate; - + if (renderData->waveform_points.size() != iMax * 2) { renderData->waveform_points.resize(iMax * 2); } @@ -112,12 +112,10 @@ void ScopeVisualProcessor::process() { } renderData->spectrum = false; - distribute(renderData); } if (spectrumEnabled) { - renderData = outputBuffers.getBuffer(); iMax = audioInputData->data.size(); if (audioInputData->channels==1) { @@ -138,7 +136,14 @@ void ScopeVisualProcessor::process() { } } } + + renderData = outputBuffers.getBuffer(); + renderData->channels = audioInputData->channels; + renderData->inputRate = audioInputData->inputRate; + renderData->sampleRate = audioInputData->sampleRate; + + audioInputData->decRefCount(); fftwf_execute(fftw_plan); @@ -157,9 +162,9 @@ void ScopeVisualProcessor::process() { } for (i = 0; i < (fftSize/2); i++) { - fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate; fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * fft_average_rate; - + fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate; + if (fft_result_maa[i] > fft_ceil) { fft_ceil = fft_result_maa[i]; } @@ -167,7 +172,7 @@ void ScopeVisualProcessor::process() { fft_floor = fft_result_maa[i]; } } - + fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05; fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05; @@ -176,8 +181,8 @@ void ScopeVisualProcessor::process() { int outSize = fftSize/2; - if (audioInputData->sampleRate != audioInputData->inputRate) { - outSize = (int)floor((float)outSize * ((float)audioInputData->sampleRate/(float)audioInputData->inputRate)); + if (renderData->sampleRate != renderData->inputRate) { + outSize = (int)floor((float)outSize * ((float)renderData->sampleRate/(float)renderData->inputRate)); } if (renderData->waveform_points.size() != outSize*2) { @@ -193,12 +198,10 @@ void ScopeVisualProcessor::process() { renderData->fft_floor = fft_floor_maa; renderData->fft_ceil = fft_ceil_maa; renderData->fft_size = fftSize/2; - renderData->inputRate = audioInputData->inputRate; - renderData->sampleRate = audioInputData->sampleRate; renderData->spectrum = true; distribute(renderData); + } else { + audioInputData->decRefCount(); } - - audioInputData->busy_update.unlock(); } } diff --git a/src/process/SpectrumVisualProcessor.cpp b/src/process/SpectrumVisualProcessor.cpp index 8dc890d..4174ccf 100644 --- a/src/process/SpectrumVisualProcessor.cpp +++ b/src/process/SpectrumVisualProcessor.cpp @@ -8,6 +8,7 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : lastInputBandwidth(0), last fftSize.store(0); centerFreq.store(0); bandwidth.store(0); + hideDC.store(false); freqShifter = nco_crcf_create(LIQUID_NCO); shiftFrequency = 0; @@ -16,6 +17,7 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : lastInputBandwidth(0), last fft_floor_ma = fft_floor_maa = 0.0; desiredInputSize.store(0); fft_average_rate = 0.65; + scaleFactor.store(1.0); } SpectrumVisualProcessor::~SpectrumVisualProcessor() { @@ -95,6 +97,11 @@ void SpectrumVisualProcessor::setup(int fftSize_in) { busy_run.unlock(); } +void SpectrumVisualProcessor::setHideDC(bool hideDC) { + this->hideDC.store(hideDC); +} + + void SpectrumVisualProcessor::process() { if (!isOutputEmpty()) { return; @@ -301,13 +308,51 @@ void SpectrumVisualProcessor::process() { fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05; fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05; + float sf = scaleFactor.load(); + for (int i = 0, iMax = fftSize; i < iMax; i++) { float v = (log10(fft_result_maa[i]+0.25 - (fft_floor_maa-0.75)) / log10((fft_ceil_maa+0.25) - (fft_floor_maa-0.75))); output->spectrum_points[i * 2] = ((float) i / (float) iMax); - output->spectrum_points[i * 2 + 1] = v; + output->spectrum_points[i * 2 + 1] = v*sf; + } + + if (hideDC.load()) { // DC-spike removal + long long freqMin = centerFreq-(bandwidth/2); + long long freqMax = centerFreq+(bandwidth/2); + long long zeroPt = (iqData->frequency-freqMin); + + if (freqMin < iqData->frequency && freqMax > iqData->frequency) { + int freqRange = int(freqMax-freqMin); + int freqStep = freqRange/fftSize; + int fftStart = (zeroPt/freqStep)-(2000/freqStep); + int fftEnd = (zeroPt/freqStep)+(2000/freqStep); + +// std::cout << "range:" << freqRange << ", step: " << freqStep << ", start: " << fftStart << ", end: " << fftEnd << std::endl; + + if (fftEnd-fftStart < 2) { + fftEnd++; + fftStart--; + } + + int numSteps = (fftEnd-fftStart); + int halfWay = fftStart+(numSteps/2); + + if ((fftEnd+numSteps/2+1 < fftSize) && (fftStart-numSteps/2-1 >= 0) && (fftEnd > fftStart)) { + int n = 1; + for (int i = fftStart; i < halfWay; i++) { + output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftStart - n) * 2 + 1]; + n++; + } + n = 1; + for (int i = halfWay; i < fftEnd; i++) { + output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftEnd + n) * 2 + 1]; + n++; + } + } + } } - output->fft_ceiling = fft_ceil_maa; + output->fft_ceiling = fft_ceil_maa/sf; output->fft_floor = fft_floor_maa; } @@ -319,3 +364,13 @@ void SpectrumVisualProcessor::process() { busy_run.unlock(); } + +void SpectrumVisualProcessor::setScaleFactor(float sf) { + scaleFactor.store(sf); +} + + +float SpectrumVisualProcessor::getScaleFactor() { + return scaleFactor.load(); +} + diff --git a/src/process/SpectrumVisualProcessor.h b/src/process/SpectrumVisualProcessor.h index 6b7d079..f08ee60 100644 --- a/src/process/SpectrumVisualProcessor.h +++ b/src/process/SpectrumVisualProcessor.h @@ -33,6 +33,10 @@ public: int getDesiredInputSize(); void setup(int fftSize); + void setHideDC(bool hideDC); + + void setScaleFactor(float sf); + float getScaleFactor(); protected: void process(); @@ -68,4 +72,6 @@ private: std::vector resampleBuffer; std::atomic_int desiredInputSize; std::mutex busy_run; + std::atomic_bool hideDC; + std::atomic scaleFactor; }; diff --git a/src/sdr/SDRDeviceInfo.cpp b/src/sdr/SDRDeviceInfo.cpp index 992f331..7896cfa 100644 --- a/src/sdr/SDRDeviceInfo.cpp +++ b/src/sdr/SDRDeviceInfo.cpp @@ -1,4 +1,163 @@ #include "SDRDeviceInfo.h" +#include + +SDRDeviceRange::SDRDeviceRange() { + low = 0; + high = 0; +} + +SDRDeviceRange::SDRDeviceRange(double low, double high) { + this->low = low; + this->high = high; +} + +SDRDeviceRange::SDRDeviceRange(std::string name, double low, double high) : SDRDeviceRange(low, high) { + this->name = name; +} + +double SDRDeviceRange::getLow() { + return low; +} +void SDRDeviceRange::setLow(double low) { + this->low = low; +} +double SDRDeviceRange::getHigh() { + return high; +} +void SDRDeviceRange::setHigh(double high) { + this->high = high; +} + +std::string SDRDeviceRange::getName() { + return this->name; +} + +void SDRDeviceRange::setName(std::string name) { + this->name = name; +} + +SDRDeviceChannel::SDRDeviceChannel() { + hardwareDC = false; + hasCorr = false; +} + +SDRDeviceChannel::~SDRDeviceChannel() { + +} + +int SDRDeviceChannel::getChannel() { + return channel; +} + +void SDRDeviceChannel::setChannel(int channel) { + this->channel = channel; +} + +bool SDRDeviceChannel::isFullDuplex() { + return fullDuplex; +} + +void SDRDeviceChannel::setFullDuplex(bool fullDuplex) { + this->fullDuplex = fullDuplex; +} + +bool SDRDeviceChannel::isTx() { + return tx; +} + +void SDRDeviceChannel::setTx(bool tx) { + this->tx = tx; +} + +bool SDRDeviceChannel::isRx() { + return rx; +} + +void SDRDeviceChannel::setRx(bool rx) { + this->rx = rx; +} + +SDRDeviceRange &SDRDeviceChannel::getGain() { + return rangeGain; +} + +SDRDeviceRange &SDRDeviceChannel::getLNAGain() { + return rangeLNA; +} + +SDRDeviceRange &SDRDeviceChannel::getFreqRange() { + return rangeFull; +} + +SDRDeviceRange &SDRDeviceChannel::getRFRange() { + return rangeRF; +} + +void SDRDeviceChannel::addGain(SDRDeviceRange range) { + gainInfo.push_back(range); +} + +std::vector &SDRDeviceChannel::getGains() { + return gainInfo; +} + +void SDRDeviceChannel::addGain(std::string name, SoapySDR::Range range) { + gainInfo.push_back(SDRDeviceRange(name,range.minimum(),range.maximum())); +} + +std::vector &SDRDeviceChannel::getSampleRates() { + return sampleRates; +} + +long SDRDeviceChannel::getSampleRateNear(long sampleRate_in) { + long returnRate = sampleRates[0]; + long sDelta = (long)sampleRate_in-sampleRates[0]; + long minDelta = std::abs(sDelta); + for (std::vector::iterator i = sampleRates.begin(); i != sampleRates.end(); i++) { + long thisDelta = std::abs(sampleRate_in - (*i)); + if (thisDelta < minDelta) { + minDelta = thisDelta; + returnRate = (*i); + } + } + return returnRate; +} + +std::vector &SDRDeviceChannel::getFilterBandwidths() { + return filterBandwidths; +} + +const bool& SDRDeviceChannel::hasHardwareDC() const { + return hardwareDC; +} + +void SDRDeviceChannel::setHardwareDC(const bool& hardware) { + hardwareDC = hardware; +} + +const bool& SDRDeviceChannel::hasCORR() const { + return hasCorr; +} + +void SDRDeviceChannel::setCORR(const bool& hasCorr) { + this->hasCorr = hasCorr; +} + +void SDRDeviceChannel::setStreamArgsInfo(SoapySDR::ArgInfoList streamArgs) { + streamArgInfo = streamArgs; +} + +SoapySDR::ArgInfoList SDRDeviceChannel::getStreamArgsInfo() { + return streamArgInfo; +} + +std::vector SDRDeviceChannel::getStreamArgNames() { + std::vector names; + for (SoapySDR::ArgInfoList::const_iterator i = streamArgInfo.begin(); i != streamArgInfo.end(); i++) { + names.push_back((*i).key); + } + return names; +} SDRDeviceInfo::SDRDeviceInfo() : name(""), serial(""), available(false) { @@ -9,12 +168,20 @@ std::string SDRDeviceInfo::getDeviceId() { std::string deviceId; deviceId.append(getName()); - deviceId.append(" :: "); - deviceId.append(getSerial()); +// deviceId.append(" :: "); +// deviceId.append(getSerial()); return deviceId; } +const int SDRDeviceInfo::getIndex() const { + return index; +} + +void SDRDeviceInfo::setIndex(const int index) { + this->index = index; +} + bool SDRDeviceInfo::isAvailable() const { return available; } @@ -63,3 +230,88 @@ void SDRDeviceInfo::setProduct(const std::string& product) { this->product = product; } +const std::string& SDRDeviceInfo::getDriver() const { + return driver; +} + +void SDRDeviceInfo::setDriver(const std::string& driver) { + this->driver = driver; +} + +const std::string& SDRDeviceInfo::getHardware() const { + return hardware; +} + +void SDRDeviceInfo::setHardware(const std::string& hardware) { + this->hardware = hardware; +} + +bool SDRDeviceInfo::hasTimestamps() const { + return timestamps; +} + +void SDRDeviceInfo::setTimestamps(bool timestamps) { + this->timestamps = timestamps; +} + +void SDRDeviceInfo::setDeviceArgs(SoapySDR::Kwargs deviceArgs) { + this->deviceArgs = deviceArgs; +} + +SoapySDR::Kwargs SDRDeviceInfo::getDeviceArgs() { + return deviceArgs; +} + +void SDRDeviceInfo::setStreamArgs(SoapySDR::Kwargs streamArgs) { + this->streamArgs = streamArgs; +} + +SoapySDR::Kwargs SDRDeviceInfo::getStreamArgs() { + return streamArgs; +} + +void SDRDeviceInfo::setSettingsInfo(SoapySDR::ArgInfoList settingsArgs) { + settingInfo = settingsArgs; +} + +SoapySDR::ArgInfoList SDRDeviceInfo::getSettingsArgInfo() { + return settingInfo; +} + +std::vector SDRDeviceInfo::getSettingNames() { + std::vector names; + for (SoapySDR::ArgInfoList::const_iterator i = settingInfo.begin(); i != settingInfo.end(); i++) { + names.push_back((*i).key); + } + return names; +} + + +void SDRDeviceInfo::addChannel(SDRDeviceChannel *chan) { + channels.push_back(chan); +} + +std::vector &SDRDeviceInfo::getChannels() { + return channels; +} + +SDRDeviceChannel * SDRDeviceInfo::getRxChannel() { + std::vector::iterator channel_i; + for (channel_i = channels.begin(); channel_i != channels.end(); channel_i++) { + if ((*channel_i)->isRx()) { + return (*channel_i); + } + } + return NULL; +} + +SDRDeviceChannel * SDRDeviceInfo::getTxChannel() { + std::vector::iterator channel_i; + for (channel_i = channels.begin(); channel_i != channels.end(); channel_i++) { + if ((*channel_i)->isTx()) { + return (*channel_i); + } + } + return NULL; +} + diff --git a/src/sdr/SDRDeviceInfo.h b/src/sdr/SDRDeviceInfo.h index 36086ad..ccec06c 100644 --- a/src/sdr/SDRDeviceInfo.h +++ b/src/sdr/SDRDeviceInfo.h @@ -1,6 +1,105 @@ #pragma once #include +#include + +#include + +/* + ---------------------------------------------------- + -- Device identification + ---------------------------------------------------- + driver=rtl + hardware=rtl + + ---------------------------------------------------- + -- Peripheral summary + ---------------------------------------------------- + Channels: 1 Rx, 0 Tx + Timestamps: NO + + ---------------------------------------------------- + -- RX Channel 0 + ---------------------------------------------------- + Full-duplex: YES + Antennas: RX + Full gain range: [0, 49.6] dB + LNA gain range: [0, 49.6] dB + Full freq range: [24, 1766] MHz + RF freq range: [24, 1766] MHz + CORR freq range: MHz + Sample rates: [0.25, 2.56] MHz + Filter bandwidths: [] MHz +*/ + +class SDRDeviceRange { +public: + SDRDeviceRange(); + SDRDeviceRange(double low, double high); + SDRDeviceRange(std::string name, double low, double high); + + double getLow(); + void setLow(double low); + double getHigh(); + void setHigh(double high); + std::string getName(); + void setName(std::string name); + +private: + std::string name; + double low, high; +}; + +class SDRDeviceChannel { +public: + SDRDeviceChannel(); + ~SDRDeviceChannel(); + + int getChannel(); + void setChannel(int channel); + + bool isFullDuplex(); + void setFullDuplex(bool fullDuplex); + + bool isTx(); + void setTx(bool tx); + + bool isRx(); + void setRx(bool rx); + + void addGain(SDRDeviceRange range); + void addGain(std::string name, SoapySDR::Range range); + std::vector &getGains(); + + SDRDeviceRange &getGain(); + SDRDeviceRange &getLNAGain(); + SDRDeviceRange &getFreqRange(); + SDRDeviceRange &getRFRange(); + + std::vector &getSampleRates(); + long getSampleRateNear(long sampleRate_in); + std::vector &getFilterBandwidths(); + + const bool& hasHardwareDC() const; + void setHardwareDC(const bool& hardware); + + const bool& hasCORR() const; + void setCORR(const bool& corr); + + void setStreamArgsInfo(SoapySDR::ArgInfoList streamArgs); + SoapySDR::ArgInfoList getStreamArgsInfo(); + std::vector getStreamArgNames(); + +private: + int channel; + bool fullDuplex, tx, rx, hardwareDC, hasCorr; + SDRDeviceRange rangeGain, rangeLNA, rangeFull, rangeRF; + std::vector sampleRates; + std::vector filterBandwidths; + SoapySDR::ArgInfoList streamArgInfo; + std::vector gainInfo; +}; + class SDRDeviceInfo { public: @@ -8,6 +107,9 @@ public: std::string getDeviceId(); + const int getIndex() const; + void setIndex(const int index); + bool isAvailable() const; void setAvailable(bool available); @@ -25,12 +127,39 @@ public: const std::string& getProduct() const; void setProduct(const std::string& product); + + const std::string& getDriver() const; + void setDriver(const std::string& driver); + + const std::string& getHardware() const; + void setHardware(const std::string& hardware); + + bool hasTimestamps() const; + void setTimestamps(bool timestamps); + + void addChannel(SDRDeviceChannel *chan); + std::vector &getChannels(); + SDRDeviceChannel * getRxChannel(); + SDRDeviceChannel * getTxChannel(); + + void setDeviceArgs(SoapySDR::Kwargs deviceArgs); + SoapySDR::Kwargs getDeviceArgs(); + + void setStreamArgs(SoapySDR::Kwargs deviceArgs); + SoapySDR::Kwargs getStreamArgs(); + + void setSettingsInfo(SoapySDR::ArgInfoList settingsArgs); + SoapySDR::ArgInfoList getSettingsArgInfo(); + + std::vector getSettingNames(); private: - std::string name; - std::string serial; - std::string product; - std::string manufacturer; - std::string tuner; - bool available; + int index; + std::string name, serial, product, manufacturer, tuner; + std::string driver, hardware; + bool timestamps, available; + + SoapySDR::Kwargs deviceArgs, streamArgs; + SoapySDR::ArgInfoList settingInfo; + std::vector channels; }; diff --git a/src/sdr/SDREnumerator.cpp b/src/sdr/SDREnumerator.cpp new file mode 100644 index 0000000..2202e3d --- /dev/null +++ b/src/sdr/SDREnumerator.cpp @@ -0,0 +1,298 @@ +#include "SDREnumerator.h" +#include "CubicSDRDefs.h" +#include +#include "CubicSDR.h" +#include + + +std::vector SDREnumerator::factories; +std::vector SDREnumerator::modules; +std::vector SDREnumerator::remotes; +std::map< std::string, std::vector > SDREnumerator::devs; +bool SDREnumerator::soapy_initialized = false; +bool SDREnumerator::has_remote = false; + +SDREnumerator::SDREnumerator() : IOThread() { + +} + +SDREnumerator::~SDREnumerator() { + +} + + +std::vector *SDREnumerator::enumerate_devices(std::string remoteAddr, bool noInit) { + + if (SDREnumerator::devs[remoteAddr].size()) { + return &SDREnumerator::devs[remoteAddr]; + } + + if (noInit) { + return NULL; + } + + if (!soapy_initialized) { + std::cout << "SoapySDR init.." << std::endl; + std::cout << "\tAPI Version: v" << SoapySDR::getAPIVersion() << std::endl; + std::cout << "\tABI Version: v" << SoapySDR::getABIVersion() << std::endl; + std::cout << "\tInstall root: " << SoapySDR::getRootPath() << std::endl; + + std::cout << "\tLoading modules... " << std::endl; + #ifdef BUNDLE_SOAPY_MODS + bool localModPref = wxGetApp().getUseLocalMod(); + if (localModPref) { + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules.."); + std::cout << "Checking local system SoapySDR modules.." << std::flush; + SoapySDR::loadModules(); + } + + wxFileName exePath = wxFileName(wxStandardPaths::Get().GetExecutablePath()); + std::vector localMods = SoapySDR::listModules(exePath.GetPath().ToStdString() + "/modules/"); + for (std::vector::iterator mods_i = localMods.begin(); mods_i != localMods.end(); mods_i++) { + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Initializing bundled SoapySDR module " + (*mods_i) + ".."); + std::cout << "Loading bundled SoapySDR module " << (*mods_i) << ".." << std::endl; + SoapySDR::loadModule(*mods_i); + } + + if (!localModPref) { + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules.."); + std::cout << "Checking system SoapySDR modules.." << std::flush; + SoapySDR::loadModules(); + } + #else + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Loading SoapySDR modules.."); + SoapySDR::loadModules(); + #endif + +// modules = SoapySDR::listModules(); +// for (size_t i = 0; i < modules.size(); i++) { +// std::cout << "\tModule found: " << modules[i] << std::endl; +// } +// if (modules.empty()) { +// std::cout << "No modules found!" << std::endl; +// } + + if (SDREnumerator::factories.size()) { + SDREnumerator::factories.erase(SDREnumerator::factories.begin(), SDREnumerator::factories.end()); + } + + std::cout << "\tAvailable factories..."; + SoapySDR::FindFunctions factories = SoapySDR::Registry::listFindFunctions(); + for (SoapySDR::FindFunctions::const_iterator it = factories.begin(); it != factories.end(); ++it) { + if (it != factories.begin()) { + std::cout << ", "; + } + std::cout << it->first; + + if (it->first == "remote") { + has_remote = true; + } + SDREnumerator::factories.push_back(it->first); + } + if (factories.empty()) { + std::cout << "No factories found!" << std::endl; + } + std::cout << std::endl; + soapy_initialized = true; + } + + std::vector results; + SoapySDR::Kwargs enumArgs; + bool isRemote = false; + + if (remoteAddr.length()) { + std::cout << "Enumerating remote address: " << remoteAddr << std::endl; + enumArgs["driver"] = "remote"; + enumArgs["remote"] = remoteAddr; + isRemote = true; + + results = SoapySDR::Device::enumerate(enumArgs); + } else { + results = SoapySDR::Device::enumerate(); + } + + if (isRemote) { + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Opening remote server ") + remoteAddr + ".."); + } + for (size_t i = 0; i < results.size(); i++) { +// std::cout << "Found device " << i << std::endl; + SDRDeviceInfo *dev = new SDRDeviceInfo(); + + SoapySDR::Kwargs deviceArgs = results[i]; + + for (SoapySDR::Kwargs::const_iterator it = deviceArgs.begin(); it != deviceArgs.end(); ++it) { + std::cout << " " << it->first << " = " << it->second << std::endl; + if (it->first == "driver") { + dev->setDriver(it->second); + } else if (it->first == "label" || it->first == "device") { + dev->setName(it->second); + } + } + + dev->setDeviceArgs(deviceArgs); + + std::cout << "Make device " << i << std::endl; + try { + SoapySDR::Device *device = SoapySDR::Device::make(dev->getDeviceArgs()); + SoapySDR::Kwargs info = device->getHardwareInfo(); + for (SoapySDR::Kwargs::const_iterator it = info.begin(); it != info.end(); ++it) { + std::cout << " " << it->first << "=" << it->second << std::endl; + if (it->first == "hardware") { + dev->setHardware(it->second); + } + } + + if (isRemote) { + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Querying remote " + remoteAddr + " device #" + std::to_string(i) + ": " + dev-> getName()); + } else { + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Querying device #") + std::to_string(i) + ": " + dev->getName()); + } + + int numChan = device->getNumChannels(SOAPY_SDR_RX); + for (int i = 0; i < numChan; i++) { + SDRDeviceChannel *chan = new SDRDeviceChannel(); + + SoapySDR::RangeList rfRange = device->getFrequencyRange(SOAPY_SDR_RX, i); + double rfMin = rfRange[0].minimum(); + double rfMax = rfRange[rfRange.size()-1].maximum(); + chan->setChannel(i); + chan->setFullDuplex(device->getFullDuplex(SOAPY_SDR_RX, i)); + chan->setRx(true); + chan->setTx(false); + chan->getRFRange().setLow(rfMin); + chan->getRFRange().setHigh(rfMax); + + std::vector freqs = device->listFrequencies(SOAPY_SDR_RX,i); + if (std::find(freqs.begin(), freqs.end(), "CORR") != freqs.end()) { + chan->setCORR(true); + } else { + chan->setCORR(false); + } + + if (device->hasDCOffsetMode(SOAPY_SDR_RX, i)) { + chan->setHardwareDC(true); + } else { + chan->setHardwareDC(false); + } + + std::vector rates = device->listSampleRates(SOAPY_SDR_RX, i); + for (std::vector::iterator i = rates.begin(); i != rates.end(); i++) { + chan->getSampleRates().push_back((long)(*i)); + } + + chan->setStreamArgsInfo(device->getStreamArgsInfo(SOAPY_SDR_RX, i)); + + std::vector gainNames = device->listGains(SOAPY_SDR_RX, i); + + for (std::vector::iterator gname = gainNames.begin(); gname!= gainNames.end(); gname++) { + chan->addGain((*gname),device->getGainRange(SOAPY_SDR_RX, i, (*gname))); + } + + dev->addChannel(chan); + } + + + SoapySDR::Kwargs streamArgs; + + if (isRemote) { +// if (deviceArgs.count("rtl") != 0) { +// streamArgs["remote:mtu"] = "8192"; +// streamArgs["remote:window"] = "16384000"; +// } + double fullScale = 0; + std::string nativeFormat = device->getNativeStreamFormat(SOAPY_SDR_RX, dev->getRxChannel()->getChannel(), fullScale); + + if (nativeFormat.length()) { + streamArgs["remote:format"] = nativeFormat; + } + } + + dev->setStreamArgs(streamArgs); + + + dev->setSettingsInfo(device->getSettingInfo()); + + SoapySDR::Device::unmake(device); + + dev->setAvailable(true); + } catch (const std::exception &ex) { + std::cerr << "Error making device: " << ex.what() << std::endl; + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Error querying device #") + std::to_string(i)); + dev->setAvailable(false); + } + std::cout << std::endl; + + SDREnumerator::devs[remoteAddr].push_back(dev); + } + if (SDREnumerator::devs[remoteAddr].empty()) { + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("No devices found!")); + } + std::cout << std::endl; + + return &SDREnumerator::devs[remoteAddr]; +} + + +void SDREnumerator::run() { + + std::cout << "SDR enumerator starting." << std::endl; + terminated.store(false); + +// if (!remotes.size()) { +// remotes.push_back("raspberrypi.local"); +// } + + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Scanning local devices, please wait.."); + SDREnumerator::enumerate_devices(""); + + if (remotes.size()) { + std::vector::iterator remote_i; + for (remote_i = remotes.begin(); remote_i != remotes.end(); remote_i++) { + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Scanning devices at " + (*remote_i) + ", please wait.."); + SDREnumerator::enumerate_devices(*remote_i); + } + } + + std::cout << "Reporting enumeration complete." << std::endl; + terminated.store(true); + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_DEVICES_READY, "Finished scanning devices."); + std::cout << "SDR enumerator done." << std::endl; + +} + + + +void SDREnumerator::addRemote(std::string remoteAddr) { + std::vector::iterator remote_i = std::find(remotes.begin(), remotes.end(), remoteAddr); + + if (remote_i != remotes.end()) { + return; + } else { + remotes.push_back(remoteAddr); + } +} + +void SDREnumerator::removeRemote(std::string remoteAddr) { + std::vector::iterator remote_i = std::find(remotes.begin(), remotes.end(), remoteAddr); + + if (remote_i != remotes.end()) { + if (devs.find(*remote_i) != devs.end()) { + while (devs[*remote_i].size()) { + SDRDeviceInfo *devRemove = devs[*remote_i].back(); + devs[*remote_i].pop_back(); + delete devRemove; + } + } + remotes.erase(remote_i); + } else { + return; + } +} + +std::vector &SDREnumerator::getRemotes() { + return remotes; +} + +bool SDREnumerator::hasRemoteModule() { + return SDREnumerator::has_remote; +} diff --git a/src/sdr/SDREnumerator.h b/src/sdr/SDREnumerator.h new file mode 100644 index 0000000..e0c243d --- /dev/null +++ b/src/sdr/SDREnumerator.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include +#include "IOThread.h" +#include "SDRDeviceInfo.h" +#include "AppConfig.h" + +#include +#include +#include +#include + + +class SDREnumerator: public IOThread { +private: + +public: + SDREnumerator(); + ~SDREnumerator(); + enum SDREnumState { SDR_ENUM_DEVICES_READY, SDR_ENUM_MESSAGE, SDR_ENUM_TERMINATED, SDR_ENUM_FAILED }; + + static std::vector *enumerate_devices(std::string remoteAddr = "", bool noInit=false); + + void run(); + + static void addRemote(std::string remoteAddr); + static void removeRemote(std::string remoteAddr); + static std::vector &getRemotes(); + static bool hasRemoteModule(); + +protected: + static bool soapy_initialized, has_remote; + static std::vector factories; + static std::vector modules; + static std::vector remotes; + static std::map< std::string, std::vector > devs; +}; diff --git a/src/sdr/SDRPostThread.cpp b/src/sdr/SDRPostThread.cpp index 7e76654..301696c 100644 --- a/src/sdr/SDRPostThread.cpp +++ b/src/sdr/SDRPostThread.cpp @@ -5,26 +5,22 @@ #include #include -SDRPostThread::SDRPostThread() : IOThread(), - iqDataInQueue(NULL), iqDataOutQueue(NULL), iqVisualQueue(NULL), dcFilter(NULL){ - - swapIQ.store(false); +SDRPostThread::SDRPostThread() : IOThread() { + iqDataInQueue = NULL; + iqDataOutQueue = NULL; + iqVisualQueue = NULL; - // create a lookup table - for (unsigned int i = 0; i <= 0xffff; i++) { - liquid_float_complex tmp,tmp_swap; -# if (__BYTE_ORDER == __LITTLE_ENDIAN) - tmp_swap.imag = tmp.real = (float(i & 0xff) - 127.4f) * (1.0f/128.0f); - tmp_swap.real = tmp.imag = (float(i >> 8) - 127.4f) * (1.0f/128.0f); - _lut.push_back(tmp); - _lut_swap.push_back(tmp_swap); -#else // BIG_ENDIAN - tmp_swap.imag = tmp.real = (float(i >> 8) - 127.4f) * (1.0f/128.0f); - tmp_swap.real = tmp.imag = (float(i & 0xff) - 127.4f) * (1.0f/128.0f); - _lut.push_back(tmp); - _lut_swap.push_back(tmp_swap); -#endif - } + numChannels = 0; + channelizer = NULL; + + sampleRate = 0; + nRunDemods = 0; + + visFrequency.store(0); + visBandwidth.store(0); + + doRefresh.store(false); + dcFilter = iirfilt_crcf_create_dc_blocker(0.0005); } SDRPostThread::~SDRPostThread() { @@ -33,6 +29,7 @@ SDRPostThread::~SDRPostThread() { void SDRPostThread::bindDemodulator(DemodulatorInstance *demod) { busy_demod.lock(); demodulators.push_back(demod); + doRefresh.store(true); busy_demod.unlock(); } @@ -46,39 +43,121 @@ void SDRPostThread::removeDemodulator(DemodulatorInstance *demod) { if (i != demodulators.end()) { demodulators.erase(i); + doRefresh.store(true); } busy_demod.unlock(); } -void SDRPostThread::setSwapIQ(bool swapIQ) { - this->swapIQ.store(swapIQ); +void SDRPostThread::initPFBChannelizer() { +// std::cout << "Initializing post-process FIR polyphase filterbank channelizer with " << numChannels << " channels." << std::endl; + if (channelizer) { + firpfbch_crcf_destroy(channelizer); + } + channelizer = firpfbch_crcf_create_kaiser(LIQUID_ANALYZER, numChannels, 4, 60); + + chanBw = (sampleRate / numChannels); + + chanCenters.resize(numChannels+1); + demodChannelActive.resize(numChannels+1); + +// std::cout << "Channel bandwidth spacing: " << (chanBw) << std::endl; } -bool SDRPostThread::getSwapIQ() { - return this->swapIQ.load(); +void SDRPostThread::updateActiveDemodulators() { + // In range? + std::vector::iterator demod_i; + + nRunDemods = 0; + + for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) { + DemodulatorInstance *demod = *demod_i; + DemodulatorThreadInputQueue *demodQueue = demod->getIQInputDataPipe(); + + // not in range? + if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) { + // deactivate if active + if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) { + demod->setActive(false); + DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData; + dummyDataOut->frequency = frequency; + dummyDataOut->sampleRate = sampleRate; + demodQueue->push(dummyDataOut); + } + + // follow if follow mode + if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) { + wxGetApp().setFrequency(demod->getFrequency()); + demod->setFollow(false); + } + } else if (!demod->isActive()) { // in range, activate if not activated + demod->setActive(true); + if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) { + wxGetApp().getDemodMgr().setActiveDemodulator(demod); + } + } + + if (!demod->isActive()) { + continue; + } + + // Add to the current run + if (nRunDemods == runDemods.size()) { + runDemods.push_back(demod); + demodChannel.push_back(-1); + } else { + runDemods[nRunDemods] = demod; + demodChannel[nRunDemods] = -1; + } + nRunDemods++; + } +} + +void SDRPostThread::updateChannels() { + // calculate channel center frequencies, todo: cache + for (int i = 0; i < numChannels/2; i++) { + int ofs = ((chanBw) * i); + chanCenters[i] = frequency + ofs; + chanCenters[i+(numChannels/2)] = frequency - (sampleRate/2) + ofs; + } + chanCenters[numChannels] = frequency + (sampleRate/2); +} + +int SDRPostThread::getChannelAt(long long frequency) { + int chan = -1; + long long minDelta = sampleRate; + for (int i = 0; i < numChannels+1; i++) { + long long fdelta = abs(frequency - chanCenters[i]); + if (fdelta < minDelta) { + minDelta = fdelta; + chan = i; + } + } + return chan; +} + +void SDRPostThread::setIQVisualRange(long long frequency, int bandwidth) { + visFrequency.store(frequency); + visBandwidth.store(bandwidth); } void SDRPostThread::run() { #ifdef __APPLE__ pthread_t tID = pthread_self(); // ID of this thread - int priority = sched_get_priority_max( SCHED_FIFO) - 1; + int priority = sched_get_priority_max( SCHED_FIFO); sched_param prio = {priority}; // scheduling priority of thread pthread_setschedparam(tID, SCHED_FIFO, &prio); #endif - dcFilter = iirfilt_crcf_create_dc_blocker(0.0005); - std::cout << "SDR post-processing thread started.." << std::endl; iqDataInQueue = (SDRThreadIQDataQueue*)getInputQueue("IQDataInput"); iqDataOutQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQDataOutput"); iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOutput"); - - ReBuffer buffers; - std::vector fpData; - std::vector dataOut; - + iqActiveDemodVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQActiveDemodVisualDataOutput"); + iqDataInQueue->set_max_num_items(0); + + std::vector dcBuf; while (!terminated) { SDRThreadIQData *data_in; @@ -86,125 +165,185 @@ void SDRPostThread::run() { iqDataInQueue->pop(data_in); // std::lock_guard < std::mutex > lock(data_in->m_mutex); - if (data_in && data_in->data.size()) { - int dataSize = data_in->data.size()/2; - if (dataSize > fpData.capacity()) { - fpData.reserve(dataSize); - dataOut.reserve(dataSize); - } - if (dataSize != fpData.size()) { - fpData.resize(dataSize); - dataOut.resize(dataSize); + if (data_in && data_in->data.size() && data_in->numChannels) { + if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate) { + numChannels = data_in->numChannels; + sampleRate = data_in->sampleRate; + initPFBChannelizer(); + doRefresh.store(true); } - if (swapIQ) { - for (int i = 0; i < dataSize; i++) { - fpData[i] = _lut_swap[*((uint16_t*)&data_in->data[2*i])]; - } - } else { - for (int i = 0; i < dataSize; i++) { - fpData[i] = _lut[*((uint16_t*)&data_in->data[2*i])]; - } - } - - iirfilt_crcf_execute_block(dcFilter, &fpData[0], dataSize, &dataOut[0]); - - if (iqVisualQueue != NULL && !iqVisualQueue->full()) { - DemodulatorThreadIQData *visualDataOut = visualDataBuffers.getBuffer(); - visualDataOut->setRefCount(1); - - int num_vis_samples = dataOut.size(); + int dataSize = data_in->data.size(); + int outSize = data_in->data.size(); -// if (visualDataOut->data.size() < num_vis_samples) { -// if (visualDataOut->data.capacity() < num_vis_samples) { -// visualDataOut->data.reserve(num_vis_samples); -// } -// visualDataOut->data.resize(num_vis_samples); -// } -// - visualDataOut->frequency = data_in->frequency; - visualDataOut->sampleRate = data_in->sampleRate; - visualDataOut->data.assign(dataOut.begin(), dataOut.begin() + num_vis_samples); - - iqVisualQueue->push(visualDataOut); + if (outSize > dataOut.capacity()) { + dataOut.reserve(outSize); } + if (outSize != dataOut.size()) { + dataOut.resize(outSize); + } + int activeVisChannel = -1; +// if (visBandwidth.load() && visBandwidth.load() < (chanBw/2)) { +// activeVisChannel = getChannelAt(visFrequency); +// } + + if (iqDataOutQueue != NULL && !iqDataOutQueue->full() && activeVisChannel < 0) { + DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer(); + + bool doVis = false; + + if (iqVisualQueue != NULL && !iqVisualQueue->full()) { + doVis = true; + } + + iqDataOut->setRefCount(1 + (doVis?1:0)); + + iqDataOut->frequency = data_in->frequency; + iqDataOut->sampleRate = data_in->sampleRate; + iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize); + + iqDataOutQueue->push(iqDataOut); + if (doVis) { + iqVisualQueue->push(iqDataOut); + } + } + busy_demod.lock(); - int activeDemods = 0; - bool pushedData = false; + if (frequency != data_in->frequency) { + frequency = data_in->frequency; + doRefresh.store(true); + } + + if (doRefresh.load()) { + updateActiveDemodulators(); + updateChannels(); + doRefresh.store(false); + } + + DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator(); + int activeDemodChannel = -1; - if (demodulators.size() || iqDataOutQueue != NULL) { - std::vector::iterator demod_i; - for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) { - DemodulatorInstance *demod = *demod_i; - if (demod->getFrequency() != data_in->frequency - && abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) { + // Find active demodulators + if (nRunDemods || (activeVisChannel >= 0)) { + +// for (int i = 0; i < numChannels; i++) { +// firpfbch_crcf_set_channel_state(channelizer, i, (demodChannelActive[i]>0)?1:0); +// } + + // channelize data + // firpfbch output rate is (input rate / channels) + for (int i = 0, iMax = dataSize; i < iMax; i+=numChannels) { + firpfbch_crcf_analyzer_execute(channelizer, &data_in->data[i], &dataOut[i]); + } + + for (int i = 0, iMax = numChannels; i < iMax; i++) { + demodChannelActive[i] = 0; + } + + // Find nearest channel for each demodulator + for (int i = 0; i < nRunDemods; i++) { + DemodulatorInstance *demod = runDemods[i]; + demodChannel[i] = getChannelAt(demod->getFrequency()); + if (demod == activeDemod) { + activeDemodChannel = demodChannel[i]; + } + } + + for (int i = 0; i < nRunDemods; i++) { + // cache channel usage refcounts + if (demodChannel[i] >= 0) { + demodChannelActive[demodChannel[i]]++; + } + } + + // Run channels + for (int i = 0; i < numChannels+1; i++) { + int doDemodVis = ((activeDemodChannel == i) && (iqActiveDemodVisualQueue != NULL) && !iqActiveDemodVisualQueue->full())?1:0; + int doVis = 0; + +// if (activeVisChannel == i) { +// doVis = (((iqDataOutQueue != NULL))?1:0) + ((iqVisualQueue != NULL && !iqVisualQueue->full())?1:0); +// } + + if (!doVis && !doDemodVis && demodChannelActive[i] == 0) { continue; } - activeDemods++; - } - - if (iqDataOutQueue != NULL) { - activeDemods++; - } - - DemodulatorThreadIQData *demodDataOut = buffers.getBuffer(); - - // std::lock_guard < std::mutex > lock(demodDataOut->m_mutex); - demodDataOut->frequency = data_in->frequency; - demodDataOut->sampleRate = data_in->sampleRate; - demodDataOut->setRefCount(activeDemods); - demodDataOut->data.assign(dataOut.begin(), dataOut.end()); - - for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) { - DemodulatorInstance *demod = *demod_i; - DemodulatorThreadInputQueue *demodQueue = demod->getIQInputDataPipe(); - if (abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) { - if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) { - demod->setActive(false); - DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData; - dummyDataOut->frequency = data_in->frequency; - dummyDataOut->sampleRate = data_in->sampleRate; - demodQueue->push(dummyDataOut); - } - - if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) { - wxGetApp().setFrequency(demod->getFrequency()); - } - } else if (!demod->isActive()) { - demod->setActive(true); - if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) { - wxGetApp().getDemodMgr().setActiveDemodulator(demod); + DemodulatorThreadIQData *demodDataOut = buffers.getBuffer(); + demodDataOut->setRefCount(demodChannelActive[i] + doVis + doDemodVis); + demodDataOut->frequency = chanCenters[i]; + demodDataOut->sampleRate = chanBw; + + // Calculate channel buffer size + int chanDataSize = (outSize/numChannels); + + if (demodDataOut->data.size() != chanDataSize) { + if (demodDataOut->data.capacity() < chanDataSize) { + demodDataOut->data.reserve(chanDataSize); } + demodDataOut->data.resize(chanDataSize); + } + + int idx = i; + + // Extra channel wraps lower side band of lowest channel + // to fix frequency gap on upper side of spectrum + if (i == numChannels) { + idx = (numChannels/2); } - if (!demod->isActive()) { - continue; - } - if (demod->isFollow()) { - demod->setFollow(false); - } - - demodQueue->push(demodDataOut); - pushedData = true; - } - - if (iqDataOutQueue != NULL) { - if (!iqDataOutQueue->full()) { - iqDataOutQueue->push(demodDataOut); - pushedData = true; + // prepare channel data buffer + if (i == 0) { // Channel 0 requires DC correction + if (dcBuf.size() != chanDataSize) { + dcBuf.resize(chanDataSize); + } + for (int j = 0; j < chanDataSize; j++) { + dcBuf[j] = dataOut[idx]; + idx += numChannels; + } + iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]); } else { - demodDataOut->decRefCount(); + for (int j = 0; j < chanDataSize; j++) { + demodDataOut->data[j] = dataOut[idx]; + idx += numChannels; + } + } + +// if (doVis) { +// iqDataOutQueue->push(demodDataOut); +// if (doVis>1) { +// iqVisualQueue->push(demodDataOut); +// } +// } + + if (doDemodVis) { + iqActiveDemodVisualQueue->push(demodDataOut); + } + + for (int j = 0; j < nRunDemods; j++) { + if (demodChannel[j] == i) { + DemodulatorInstance *demod = runDemods[j]; + demod->getIQInputDataPipe()->push(demodDataOut); +// std::cout << "Demodulator " << j << " in channel #" << i << " ctr: " << chanCenters[i] << " dataSize: " << chanDataSize << std::endl; + } } } - - if (!pushedData && iqDataOutQueue == NULL) { - demodDataOut->setRefCount(0); + } + + bool doUpdate = false; + for (int j = 0; j < nRunDemods; j++) { + DemodulatorInstance *demod = runDemods[j]; + if (abs(frequency - demod->getFrequency()) > (sampleRate / 2)) { + doUpdate = true; } } + if (doUpdate) { + updateActiveDemodulators(); + } + busy_demod.unlock(); } data_in->decRefCount(); diff --git a/src/sdr/SDRPostThread.h b/src/sdr/SDRPostThread.h index 0162e7e..f89d6e1 100644 --- a/src/sdr/SDRPostThread.h +++ b/src/sdr/SDRPostThread.h @@ -1,6 +1,10 @@ #pragma once +#if USE_RTL_SDR #include "SDRThread.h" +#else +#include "SoapySDRThread.h" +#endif #include class SDRPostThread : public IOThread { @@ -10,26 +14,44 @@ public: void bindDemodulator(DemodulatorInstance *demod); void removeDemodulator(DemodulatorInstance *demod); - - void setSwapIQ(bool swapIQ); - bool getSwapIQ(); void run(); void terminate(); + void setIQVisualRange(long long frequency, int bandwidth); + protected: SDRThreadIQDataQueue *iqDataInQueue; DemodulatorThreadInputQueue *iqDataOutQueue; DemodulatorThreadInputQueue *iqVisualQueue; - + DemodulatorThreadInputQueue *iqActiveDemodVisualQueue; + std::mutex busy_demod; std::vector demodulators; - iirfilt_crcf dcFilter; - std::atomic_bool swapIQ; - ReBuffer visualDataBuffers; - private: - std::vector _lut; - std::vector _lut_swap; + void initPFBChannelizer(); + void updateActiveDemodulators(); + void updateChannels(); + int getChannelAt(long long frequency); + + ReBuffer buffers; + std::vector fpData; + std::vector dataOut; + std::vector chanCenters; + long long chanBw; + + int nRunDemods; + std::vector runDemods; + std::vector demodChannel; + std::vector demodChannelActive; + + ReBuffer visualDataBuffers; + atomic_bool doRefresh; + atomic_llong visFrequency; + atomic_int visBandwidth; + int numChannels, sampleRate; + long long frequency; + firpfbch_crcf channelizer; + iirfilt_crcf dcFilter; }; diff --git a/src/sdr/SDRThread.cpp b/src/sdr/SDRThread.cpp index 81f4cc0..5ee8080 100644 --- a/src/sdr/SDRThread.cpp +++ b/src/sdr/SDRThread.cpp @@ -41,8 +41,12 @@ int SDRThread::enumerate_rtl(std::vector *devs) { deviceProduct = product; deviceManufacturer = manufact; - rtlsdr_dev_t *devTest; - rtlsdr_open(&devTest, i); + rtlsdr_dev_t *devTest = nullptr; + if(rtlsdr_open(&devTest, i) < 0) + { + std::cout << "\tFailed to open device " << i << std::endl; + continue; + } std::cout << "\t Tuner type: "; switch (rtlsdr_get_tuner_type(devTest)) { diff --git a/src/sdr/SoapySDRThread.cpp b/src/sdr/SoapySDRThread.cpp new file mode 100644 index 0000000..8b80dc3 --- /dev/null +++ b/src/sdr/SoapySDRThread.cpp @@ -0,0 +1,423 @@ +#include "SoapySDRThread.h" +#include "CubicSDRDefs.h" +#include +#include "CubicSDR.h" +#include + + +SDRThread::SDRThread() : IOThread() { + device = NULL; + + deviceConfig.store(NULL); + deviceInfo.store(NULL); + + sampleRate.store(DEFAULT_SAMPLE_RATE); + frequency.store(0); + offset.store(0); + ppm.store(0); + + numElems.store(0); + + rate_changed.store(false); + freq_changed.store(false); + offset_changed.store(false); + ppm_changed .store(false); + device_changed.store(false); + + hasPPM.store(false); + hasHardwareDC.store(false); + numChannels.store(8); + + agc_mode.store(true); + agc_mode_changed.store(false); + gain_value_changed.store(false); + setting_value_changed.store(false); +} + +SDRThread::~SDRThread() { + +} + +SoapySDR::Kwargs SDRThread::combineArgs(SoapySDR::Kwargs a, SoapySDR::Kwargs b) { + SoapySDR::Kwargs c; + SoapySDR::Kwargs::iterator i; + for (i = a.begin(); i != a.end(); i++) { + c[i->first] = i->second; + } + for (i = b.begin(); i != b.end(); i++) { + c[i->first] = i->second; + } + return c; +} + +void SDRThread::init() { + SDRDeviceInfo *devInfo = deviceInfo.load(); + deviceConfig.store(wxGetApp().getConfig()->getDevice(devInfo->getDeviceId())); + DeviceConfig *devConfig = deviceConfig.load(); + + ppm.store(devConfig->getPPM()); + ppm_changed.store(true); + + std::string driverName = devInfo->getDriver(); + + offset = devConfig->getOffset(); + + SoapySDR::Kwargs args = devInfo->getDeviceArgs(); + + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Initializing device.")); + device = SoapySDR::Device::make(args); + stream = device->setupStream(SOAPY_SDR_RX,"CF32", std::vector(), combineArgs(devInfo->getStreamArgs(),streamArgs)); + + wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Activating stream.")); + device->setSampleRate(SOAPY_SDR_RX,0,sampleRate.load()); + device->setFrequency(SOAPY_SDR_RX,0,"RF",frequency - offset.load()); + device->activateStream(stream); + SDRDeviceChannel *chan = devInfo->getRxChannel(); + if (chan->hasCORR()) { + hasPPM.store(true); + device->setFrequency(SOAPY_SDR_RX,0,"CORR",ppm.load()); + } else { + hasPPM.store(false); + } + if (chan->hasHardwareDC()) { + hasHardwareDC.store(true); +// wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Found hardware DC offset correction support, internal disabled.")); + device->setDCOffsetMode(SOAPY_SDR_RX, chan->getChannel(), true); + } else { + hasHardwareDC.store(false); + } + + device->setGainMode(SOAPY_SDR_RX,0,agc_mode.load()); + + numChannels.store(getOptimalChannelCount(sampleRate.load())); + numElems.store(getOptimalElementCount(sampleRate.load(), 30)); + inpBuffer.data.resize(numElems.load()); + + buffs[0] = malloc(numElems * 2 * sizeof(float)); + + SoapySDR::ArgInfoList settingsInfo = device->getSettingInfo(); + SoapySDR::ArgInfoList::const_iterator settings_i; + + if (!setting_value_changed.load()) { + settings.erase(settings.begin(), settings.end()); + settingChanged.erase(settingChanged.begin(), settingChanged.end()); + } + + setting_busy.lock(); + for (settings_i = settingsInfo.begin(); settings_i != settingsInfo.end(); settings_i++) { + SoapySDR::ArgInfo setting = (*settings_i); + if ((settingChanged.find(setting.key) != settingChanged.end()) && (settings.find(setting.key) != settings.end())) { + device->writeSetting(setting.key, settings[setting.key]); + settingChanged[setting.key] = false; + } else { + settings[setting.key] = device->readSetting(setting.key); + settingChanged[setting.key] = false; + } + } + setting_value_changed.store(false); + setting_busy.unlock(); + + wxGetApp().sdrThreadNotify(SDRThread::SDR_THREAD_INITIALIZED, std::string("Device Initialized.")); +} + +void SDRThread::deinit() { + device->deactivateStream(stream); + device->closeStream(stream); + SoapySDR::Device::unmake(device); + free(buffs[0]); +} + +void SDRThread::readStream(SDRThreadIQDataQueue* iqDataOutQueue) { + int flags; + long long timeNs; + + + int n_read = 0; + while (n_read != numElems && !terminated) { + int n_stream_read = device->readStream(stream, buffs, numElems-n_read, flags, timeNs); + if (n_stream_read > 0) { + memcpy(&inpBuffer.data[n_read], buffs[0], n_stream_read * sizeof(float) * 2); + n_read += n_stream_read; + } else { + break; + } + } + + if (n_read > 0 && !terminated) { + SDRThreadIQData *dataOut = buffers.getBuffer(); + +// if (hasHardwareDC) { + dataOut->data.assign(inpBuffer.data.begin(), inpBuffer.data.begin()+n_read); +// } else { +// if (dataOut->data.size() != n_read) { +// dataOut->data.resize(n_read); +// } +// iirfilt_crcf_execute_block(dcFilter, &inpBuffer.data[0], n_read, &dataOut->data[0]); +// } + + + dataOut->setRefCount(1); + dataOut->frequency = frequency.load(); + dataOut->sampleRate = sampleRate.load(); + dataOut->dcCorrected = hasHardwareDC.load(); + dataOut->numChannels = numChannels.load(); + + iqDataOutQueue->push(dataOut); + } +} + +void SDRThread::readLoop() { + SDRThreadIQDataQueue* iqDataOutQueue = (SDRThreadIQDataQueue*) getOutputQueue("IQDataOutput"); + + if (iqDataOutQueue == NULL) { + return; + } + + updateGains(); + + while (!terminated.load()) { + updateSettings(); + readStream(iqDataOutQueue); + } + + buffers.purge(); +} + +void SDRThread::updateGains() { + SDRDeviceInfo *devInfo = deviceInfo.load(); + + gainValues.erase(gainValues.begin(),gainValues.end()); + gainChanged.erase(gainChanged.begin(),gainChanged.end()); + + std::vector gains = devInfo->getRxChannel()->getGains(); + for (std::vector::iterator gi = gains.begin(); gi != gains.end(); gi++) { + gainValues[(*gi).getName()] = device->getGain(SOAPY_SDR_RX, devInfo->getRxChannel()->getChannel(), (*gi).getName()); + gainChanged[(*gi).getName()] = false; + } + + gain_value_changed.store(false); +} + +void SDRThread::updateSettings() { + if (offset_changed.load()) { + if (!freq_changed.load()) { + frequency.store(frequency.load()); + freq_changed.store(true); + } + offset_changed.store(false); + } + + if (rate_changed.load()) { + device->setSampleRate(SOAPY_SDR_RX,0,sampleRate.load()); + sampleRate.store(device->getSampleRate(SOAPY_SDR_RX,0)); + numChannels.store(getOptimalChannelCount(sampleRate.load())); + numElems.store(getOptimalElementCount(sampleRate.load(), 60)); + inpBuffer.data.resize(numElems.load()); + free(buffs[0]); + buffs[0] = malloc(numElems.load() * 2 * sizeof(float)); + rate_changed.store(false); + } + + if (ppm_changed.load() && hasPPM.load()) { + device->setFrequency(SOAPY_SDR_RX,0,"CORR",ppm.load()); + ppm_changed.store(false); + } + + if (freq_changed.load()) { + device->setFrequency(SOAPY_SDR_RX,0,"RF",frequency.load() - offset.load()); + freq_changed.store(false); + } + + if (agc_mode_changed.load()) { + SDRDeviceInfo *devInfo = deviceInfo.load(); + + device->setGainMode(SOAPY_SDR_RX,devInfo->getRxChannel()->getChannel(),agc_mode.load()); + agc_mode_changed.store(false); + if (!agc_mode.load()) { + updateGains(); + } + } + + if (gain_value_changed.load() && !agc_mode.load()) { + SDRDeviceInfo *devInfo = deviceInfo.load(); + + gain_busy.lock(); + for (std::map::iterator gci = gainChanged.begin(); gci != gainChanged.end(); gci++) { + if (gci->second) { + device->setGain(SOAPY_SDR_RX, devInfo->getRxChannel()->getChannel(), gci->first, gainValues[gci->first]); + gainChanged[gci->first] = false; + } + } + gain_busy.unlock(); + + gain_value_changed.store(false); + } + + + if (setting_value_changed.load()) { + setting_busy.lock(); + + for (std::map::iterator sci = settingChanged.begin(); sci != settingChanged.end(); sci++) { + if (sci->second) { + device->writeSetting(sci->first, settings[sci->first]); + settingChanged[sci->first] = false; + } + } + + setting_value_changed.store(false); + setting_busy.unlock(); + } +} + +void SDRThread::run() { +//#ifdef __APPLE__ +// pthread_t tID = pthread_self(); // ID of this thread +// int priority = sched_get_priority_max( SCHED_FIFO); +// sched_param prio = { priority }; // scheduling priority of thread +// pthread_setschedparam(tID, SCHED_FIFO, &prio); +//#endif + + std::cout << "SDR thread starting." << std::endl; + terminated.store(false); + + if (deviceInfo.load() != NULL) { + std::cout << "device init()" << std::endl; + init(); + std::cout << "starting readLoop()" << std::endl; + readLoop(); + std::cout << "readLoop() ended." << std::endl; + deinit(); + std::cout << "device deinit()" << std::endl; + } else { + std::cout << "SDR Thread started with null device?" << std::endl; + } + + std::cout << "SDR thread done." << std::endl; + + if (!terminated.load()) { + terminated.store(true); + wxGetApp().sdrThreadNotify(SDRThread::SDR_THREAD_TERMINATED, "Done."); + } +} + + +SDRDeviceInfo *SDRThread::getDevice() { + return deviceInfo.load(); +} + +void SDRThread::setDevice(SDRDeviceInfo *dev) { + deviceInfo.store(dev); + deviceConfig.store(wxGetApp().getConfig()->getDevice(dev->getDeviceId())); +} + +int SDRThread::getOptimalElementCount(long long sampleRate, int fps) { + int elemCount = (int)floor((double)sampleRate/(double)fps); + int nch = numChannels.load(); + elemCount = int(ceil((double)elemCount/(double)nch))*nch; + std::cout << "Calculated optimal " << numChannels.load() << " channel element count of " << elemCount << std::endl; + return elemCount; +} + +int SDRThread::getOptimalChannelCount(long long sampleRate) { + int optimal_rate = CHANNELIZER_RATE_MAX; + int optimal_count = int(ceil(double(sampleRate)/double(optimal_rate))); + + if (optimal_count % 2 == 1) { + optimal_count--; + } + + if (optimal_count < 4) { + optimal_count = 4; + } + +// if (optimal_count > 16) { +// optimal_count = 16; +// } + return optimal_count; +} + + +void SDRThread::setFrequency(long long freq) { + if (freq < sampleRate.load() / 2) { + freq = sampleRate.load() / 2; + } + frequency.store(freq); + freq_changed.store(true); +} + +long long SDRThread::getFrequency() { + return frequency.load(); +} + +void SDRThread::setOffset(long long ofs) { + offset.store(ofs); + offset_changed.store(true); + std::cout << "Set offset: " << offset.load() << std::endl; +} + +long long SDRThread::getOffset() { + return offset.load(); +} + +void SDRThread::setSampleRate(int rate) { + sampleRate.store(rate); + rate_changed = true; + std::cout << "Set sample rate: " << sampleRate.load() << std::endl; +} +int SDRThread::getSampleRate() { + return sampleRate.load(); +} + +void SDRThread::setPPM(int ppm) { + this->ppm.store(ppm); + ppm_changed.store(true); + std::cout << "Set PPM: " << this->ppm.load() << std::endl; +} + +int SDRThread::getPPM() { + return ppm.load(); +} + +void SDRThread::setAGCMode(bool mode) { + agc_mode.store(mode); + agc_mode_changed.store(true); +} + +bool SDRThread::getAGCMode() { + return agc_mode.load(); +} + +void SDRThread::setGain(std::string name, float value) { + gain_busy.lock(); + gainValues[name] = value; + gainChanged[name] = true; + gain_value_changed.store(true); + gain_busy.unlock(); +} + +float SDRThread::getGain(std::string name) { + gain_busy.lock(); + float val = gainValues[name]; + gain_busy.unlock(); + return val; +} + +void SDRThread::writeSetting(std::string name, std::string value) { + setting_busy.lock(); + settings[name] = value; + settingChanged[name] = true; + setting_value_changed.store(true); + setting_busy.unlock(); +} + +std::string SDRThread::readSetting(std::string name) { + std::string val; + setting_busy.lock(); + val = device->readSetting(name); + setting_busy.unlock(); + return val; +} + +void SDRThread::setStreamArgs(SoapySDR::Kwargs streamArgs_in) { + streamArgs = streamArgs_in; +} diff --git a/src/sdr/SoapySDRThread.h b/src/sdr/SoapySDRThread.h new file mode 100644 index 0000000..4c21103 --- /dev/null +++ b/src/sdr/SoapySDRThread.h @@ -0,0 +1,112 @@ +#pragma once + +#include + +#include "ThreadQueue.h" +#include "DemodulatorMgr.h" +#include "SDRDeviceInfo.h" +#include "AppConfig.h" + +#include +#include +#include +#include + + +class SDRThreadIQData: public ReferenceCounter { +public: + long long frequency; + long long sampleRate; + bool dcCorrected; + int numChannels; + std::vector data; + + SDRThreadIQData() : + frequency(0), sampleRate(DEFAULT_SAMPLE_RATE), dcCorrected(true), numChannels(0) { + + } + + SDRThreadIQData(long long bandwidth, long long frequency, std::vector *data) : + frequency(frequency), sampleRate(bandwidth) { + + } + + ~SDRThreadIQData() { + + } +}; + +typedef ThreadQueue SDRThreadIQDataQueue; + +class SDRThread : public IOThread { +private: + void init(); + void deinit(); + void readStream(SDRThreadIQDataQueue* iqDataOutQueue); + void readLoop(); + +public: + SDRThread(); + ~SDRThread(); + enum SDRThreadState { SDR_THREAD_MESSAGE, SDR_THREAD_INITIALIZED, SDR_THREAD_TERMINATED, SDR_THREAD_FAILED }; + + void run(); + + SDRDeviceInfo *getDevice(); + void setDevice(SDRDeviceInfo *dev); + int getOptimalElementCount(long long sampleRate, int fps); + int getOptimalChannelCount(long long sampleRate); + + void setFrequency(long long freq); + long long getFrequency(); + + void setOffset(long long ofs); + long long getOffset(); + + void setSampleRate(int rate); + int getSampleRate(); + + void setPPM(int ppm); + int getPPM(); + + void setAGCMode(bool mode); + bool getAGCMode(); + + void setGain(std::string name, float value); + float getGain(std::string name); + + void writeSetting(std::string name, std::string value); + std::string readSetting(std::string name); + + void setStreamArgs(SoapySDR::Kwargs streamArgs); + +protected: + void updateGains(); + void updateSettings(); + SoapySDR::Kwargs combineArgs(SoapySDR::Kwargs a, SoapySDR::Kwargs b); + + SoapySDR::Stream *stream; + SoapySDR::Device *device; + void *buffs[1]; + ReBuffer buffers; + SDRThreadIQData inpBuffer; + std::atomic deviceConfig; + std::atomic deviceInfo; + + std::mutex setting_busy; + std::map settings; + std::map settingChanged; + + std::atomic sampleRate; + std::atomic_llong frequency, offset; + std::atomic_int ppm, numElems, numChannels; + std::atomic_bool hasPPM, hasHardwareDC; + std::atomic_bool agc_mode, rate_changed, freq_changed, offset_changed, + ppm_changed, device_changed, agc_mode_changed, gain_value_changed, setting_value_changed; + + std::mutex gain_busy; + std::map gainValues; + std::map gainChanged; + + SoapySDR::Kwargs streamArgs; +}; diff --git a/src/ui/GLPanel.cpp b/src/ui/GLPanel.cpp index 7d06c81..c61d626 100644 --- a/src/ui/GLPanel.cpp +++ b/src/ui/GLPanel.cpp @@ -5,7 +5,7 @@ using namespace CubicVR; -GLPanel::GLPanel() : fillType(GLPANEL_FILL_SOLID), contentsVisible(true), transform(mat4::identity()) { +GLPanel::GLPanel() : fillType(GLPANEL_FILL_SOLID), contentsVisible(true), visible(true), transform(mat4::identity()) { pos[0] = 0.0f; pos[1] = 0.0f; rot[0] = 0.0f; @@ -19,6 +19,8 @@ GLPanel::GLPanel() : fillType(GLPANEL_FILL_SOLID), contentsVisible(true), transf setCoordinateSystem(GLPANEL_Y_UP); setMarginPx(0); setBorderPx(0); + srcBlend = GL_SRC_ALPHA; + dstBlend = GL_ONE_MINUS_SRC_ALPHA; } void GLPanel::genArrays() { @@ -174,6 +176,19 @@ void GLPanel::setCoordinateSystem(GLPanelCoordinateSystem coord_in) { genArrays(); } +bool GLPanel::hitTest(CubicVR::vec2 pos, CubicVR::vec2 &result) { + CubicVR::vec4 hitPos = CubicVR::mat4::vec4_multiply(CubicVR::vec4(pos.x, pos.y, 0.0, 1.0), transformInverse); + + if (hitPos.x >= -1.0 && hitPos.x <= 1.0 && hitPos.y >= -1.0 && hitPos.y <= 1.0) { + result.x = hitPos.x; + result.y = hitPos.y; + return true; + } + + return false; +} + + void GLPanel::setFill(GLPanelFillType fill_mode) { fillType = fill_mode; genArrays(); @@ -210,6 +225,11 @@ void GLPanel::setBorderPx(float bordl, float bordr, float bordt, float bordb) { borderPx.bottom = bordb; } +void GLPanel::setBlend(GLuint src, GLuint dst) { + srcBlend = src; + dstBlend = dst; +} + void GLPanel::addChild(GLPanel *childPanel) { std::vector::iterator i = std::find(children.begin(), children.end(), childPanel); @@ -278,6 +298,8 @@ void GLPanel::calcTransform(mat4 transform_in) { if (marginPx) { transform *= mat4::scale(1.0 - marginPx * 2.0 * pvec.x / size[0], 1.0 - marginPx * 2.0 * pvec.y / size[1], 1); } + + transformInverse = CubicVR::mat4::inverse(transform); } void GLPanel::draw() { @@ -285,9 +307,9 @@ void GLPanel::draw() { glLoadMatrixf(transform); - if (fillType != GLPANEL_FILL_NONE) { + if (fillType != GLPANEL_FILL_NONE && visible) { glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFunc(srcBlend, dstBlend); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_COLOR_ARRAY); glVertexPointer(2, GL_FLOAT, 0, &glPoints[0]); diff --git a/src/ui/GLPanel.h b/src/ui/GLPanel.h index 0f3e3b6..ed329fe 100644 --- a/src/ui/GLPanel.h +++ b/src/ui/GLPanel.h @@ -45,8 +45,8 @@ public: GLPanelEdges borderPx; RGBA4f fill[2]; RGBA4f borderColor; - bool contentsVisible; - CubicVR::mat4 transform; + bool contentsVisible, visible; + CubicVR::mat4 transform, transformInverse; CubicVR::mat4 localTransform; float min, mid, max; // screen dimensions @@ -55,7 +55,8 @@ public: CubicVR::vec2 umin, umax, ucenter; // pixel dimensions CubicVR::vec2 pdim, pvec; - + GLuint srcBlend, dstBlend; + std::vector children; GLPanel(); @@ -68,6 +69,8 @@ public: float getHeightPx(); void setCoordinateSystem(GLPanelCoordinateSystem coord); + bool hitTest(CubicVR::vec2 pos, CubicVR::vec2 &result); + void setFill(GLPanelFillType fill_mode); void setFillColor(RGBA4f color1); void setFillColor(RGBA4f color1, RGBA4f color2); @@ -77,6 +80,8 @@ public: void setBorderPx(float bord); void setBorderPx(float bordl, float bordr, float bordt, float bordb); + void setBlend(GLuint src, GLuint dst); + void addChild(GLPanel *childPanel); void removeChild(GLPanel *childPanel); diff --git a/src/util/GLExt.cpp b/src/util/GLExt.cpp index 88785a9..efab5f0 100644 --- a/src/util/GLExt.cpp +++ b/src/util/GLExt.cpp @@ -35,10 +35,10 @@ void initGLExtensions() { // const GLubyte *extensions = glGetString(GL_EXTENSIONS); // std::cout << std::endl << "Supported GL Extensions: " << std::endl << extensions << std::endl << std::endl; -#ifdef __linux__ - const GLint interval = 2; -#else +#ifdef __APPLE__ const GLint interval = 1; +#else + const GLint interval = 2; #endif #ifdef _WIN32 diff --git a/src/visual/GainCanvas.cpp b/src/visual/GainCanvas.cpp new file mode 100644 index 0000000..a098d63 --- /dev/null +++ b/src/visual/GainCanvas.cpp @@ -0,0 +1,305 @@ +#include "GainCanvas.h" + +#include "wx/wxprec.h" + +#ifndef WX_PRECOMP +#include "wx/wx.h" +#endif + +#if !wxUSE_GLCANVAS +#error "OpenGL required: set wxUSE_GLCANVAS to 1 and rebuild the library" +#endif + +#include "CubicSDR.h" +#include "CubicSDRDefs.h" +#include "AppFrame.h" +#include + +wxBEGIN_EVENT_TABLE(GainCanvas, wxGLCanvas) EVT_PAINT(GainCanvas::OnPaint) +EVT_IDLE(GainCanvas::OnIdle) +EVT_MOTION(GainCanvas::OnMouseMoved) +EVT_LEFT_DOWN(GainCanvas::OnMouseDown) +EVT_LEFT_UP(GainCanvas::OnMouseReleased) +EVT_LEAVE_WINDOW(GainCanvas::OnMouseLeftWindow) +EVT_ENTER_WINDOW(GainCanvas::OnMouseEnterWindow) +wxEND_EVENT_TABLE() + +GainCanvas::GainCanvas(wxWindow *parent, int *attribList) : + InteractiveCanvas(parent, attribList) { + + glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this)); + bgPanel.setCoordinateSystem(GLPanel::GLPANEL_Y_UP); + bgPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_X); + + numGains = 1; + spacing = 2.0/numGains; + barWidth = (1.0/numGains)*0.8; + startPos = spacing/2.0; + barHeight = 0.8; +} + +GainCanvas::~GainCanvas() { + +} + +void GainCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { + wxPaintDC dc(this); + const wxSize ClientSize = GetClientSize(); + + glContext->SetCurrent(*this); + initGLExtensions(); + + glViewport(0, 0, ClientSize.x, ClientSize.y); + + float i = 0; + for (std::vector::iterator gi = gainInfo.begin(); gi != gainInfo.end(); gi++) { + GainInfo *gInfo = (*gi); + float midPos = -1.0+startPos+spacing*i; + + gInfo->labelPanel.setSize(spacing/2.0,(15.0/float(ClientSize.y))); + gInfo->labelPanel.setPosition(midPos, -barHeight-(20.0/float(ClientSize.y))); + + gInfo->valuePanel.setSize(spacing/2.0,(15.0/float(ClientSize.y))); + gInfo->valuePanel.setPosition(midPos, barHeight+(20.0/float(ClientSize.y))); + + i+=1.0; + } + + bgPanel.draw(); + + SwapBuffers(); +} + +void GainCanvas::OnIdle(wxIdleEvent &event) { + if (mouseTracker.mouseInView()) { + Refresh(); + } else { + event.Skip(); + } + + for (std::vector::iterator gi = gainInfo.begin(); gi != gainInfo.end(); gi++) { + GainInfo *gInfo = (*gi); + if (gInfo->changed) { + wxGetApp().setGain(gInfo->name, gInfo->current); + gInfo->changed = false; + } + } +} + +int GainCanvas::GetPanelHit(CubicVR::vec2 &result) { + std::vector::iterator gi; + + int i = 0; + for (gi = gainInfo.begin(); gi != gainInfo.end(); gi++) { + GainInfo *gInfo = (*gi); + + CubicVR::vec2 hitResult; + if (gInfo->panel.hitTest(CubicVR::vec2((mouseTracker.getMouseX()-0.5)*2.0, (mouseTracker.getMouseY()-0.5)*2.0), hitResult)) { +// std::cout << "Hit #" << i << " result: " << hitResult << std::endl; + result = (hitResult + CubicVR::vec2(1.0,1.0)) * 0.5; + return i; + } + i++; + } + return -1; +} + + +void GainCanvas::SetLevel() { + CubicVR::vec2 hitResult; + int panelHit = GetPanelHit(hitResult); + + if (panelHit >= 0) { + gainInfo[panelHit]->levelPanel.setSize(1.0, hitResult.y); + gainInfo[panelHit]->levelPanel.setPosition(0.0, (-1.0+(hitResult.y))); + gainInfo[panelHit]->current = gainInfo[panelHit]->low+(hitResult.y * (gainInfo[panelHit]->high-gainInfo[panelHit]->low)); + gainInfo[panelHit]->changed = true; + gainInfo[panelHit]->valuePanel.setText(std::to_string(int(gainInfo[panelHit]->current))); + } +} + +void GainCanvas::OnMouseMoved(wxMouseEvent& event) { + InteractiveCanvas::OnMouseMoved(event); + + CubicVR::vec2 hitResult; + int panelHit = GetPanelHit(hitResult); + + if (panelHit >= 0) { + gainInfo[panelHit]->highlightPanel.setSize(1.0, hitResult.y); + gainInfo[panelHit]->highlightPanel.setPosition(0.0, (-1.0+(hitResult.y))); + } + + int i = 0; + for (std::vector::iterator gi = gainInfo.begin(); gi != gainInfo.end(); gi++) { + (*gi)->highlightPanel.visible = (i==panelHit); + i++; + } + + if (mouseTracker.mouseDown()) { + SetLevel(); + } +} + +void GainCanvas::OnMouseDown(wxMouseEvent& event) { + InteractiveCanvas::OnMouseDown(event); + SetLevel(); +} + +void GainCanvas::OnMouseWheelMoved(wxMouseEvent& event) { + InteractiveCanvas::OnMouseWheelMoved(event); +// Refresh(); +} + +void GainCanvas::OnMouseReleased(wxMouseEvent& event) { + InteractiveCanvas::OnMouseReleased(event); +// Refresh(); +} + +void GainCanvas::OnMouseLeftWindow(wxMouseEvent& event) { + InteractiveCanvas::OnMouseLeftWindow(event); + SetCursor(wxCURSOR_CROSS); + + int i = 0; + for (std::vector::iterator gi = gainInfo.begin(); gi != gainInfo.end(); gi++) { + (*gi)->highlightPanel.visible = false; + i++; + } + Refresh(); +} + +void GainCanvas::OnMouseEnterWindow(wxMouseEvent& event) { + InteractiveCanvas::mouseTracker.OnMouseEnterWindow(event); + SetCursor(wxCURSOR_CROSS); +// Refresh(); +} + + + +void GainCanvas::setHelpTip(std::string tip) { + helpTip = tip; +} + +void GainCanvas::updateGainUI() { + const wxSize ClientSize = GetClientSize(); + + SDRDeviceInfo *devInfo = wxGetApp().getDevice(); + + std::vector &gains = devInfo->getRxChannel()->getGains(); + std::vector::iterator gi; + + numGains = gains.size(); + float i = 0; + + if (!numGains) { + return; + } + + spacing = 2.0/numGains; + barWidth = (1.0/numGains)*0.7; + startPos = spacing/2.0; + barHeight = 0.8; + + RGBA4f c1, c2; + + while (gainInfo.size()) { + GainInfo *giDel; + giDel = gainInfo.back(); + gainInfo.pop_back(); + + giDel->panel.removeChild(&giDel->levelPanel); + bgPanel.removeChild(&(giDel->labelPanel)); + bgPanel.removeChild(&(giDel->valuePanel)); + bgPanel.removeChild(&(giDel->panel)); + delete giDel; + } + + for (gi = gains.begin(); gi != gains.end(); gi++) { + GainInfo *gInfo = new GainInfo; + float midPos = -1.0+startPos+spacing*i; + + gInfo->name = (*gi).getName(); + gInfo->low = (*gi).getLow(); + gInfo->high = (*gi).getHigh(); + gInfo->current = wxGetApp().getGain(gInfo->name); + + gInfo->panel.setBorderPx(1); + gInfo->panel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X); + gInfo->panel.setPosition(midPos, 0); + gInfo->panel.setSize(barWidth, barHeight); + gInfo->panel.setBlend(GL_ONE, GL_ONE); + + gInfo->levelPanel.setBorderPx(0); + gInfo->levelPanel.setMarginPx(1); + gInfo->levelPanel.setSize(1.0,0.8); + float levelVal = float(gInfo->current-gInfo->low)/float(gInfo->high-gInfo->low); + gInfo->levelPanel.setSize(1.0, levelVal); + gInfo->levelPanel.setPosition(0.0, (-1.0+(levelVal))); + gInfo->levelPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X); + gInfo->levelPanel.setBlend(GL_ONE, GL_ONE); + + gInfo->panel.addChild(&gInfo->levelPanel); + + gInfo->highlightPanel.setBorderPx(0); + gInfo->highlightPanel.setMarginPx(1); + gInfo->highlightPanel.setSize(1.0,0.8); + gInfo->highlightPanel.setPosition(0.0,-0.2); + gInfo->highlightPanel.setFill(GLPanel::GLPANEL_FILL_GRAD_BAR_X); + gInfo->highlightPanel.setBlend(GL_ONE, GL_ONE); + gInfo->highlightPanel.visible = false; + + gInfo->panel.addChild(&gInfo->highlightPanel); + + gInfo->labelPanel.setSize(spacing/2.0,(15.0/float(ClientSize.y))); + gInfo->labelPanel.setPosition(midPos, -barHeight-(20.0/float(ClientSize.y))); + gInfo->labelPanel.setText((*gi).getName()); + gInfo->labelPanel.setFill(GLPanel::GLPANEL_FILL_NONE); + + bgPanel.addChild(&(gInfo->labelPanel)); + + gInfo->valuePanel.setSize(spacing/2.0,(15.0/float(ClientSize.y))); + gInfo->valuePanel.setPosition(midPos, barHeight+(20.0/float(ClientSize.y))); + gInfo->valuePanel.setText(std::to_string(int(gInfo->current))); + gInfo->valuePanel.setFill(GLPanel::GLPANEL_FILL_NONE); + + bgPanel.addChild(&(gInfo->valuePanel)); + + bgPanel.addChild(&(gInfo->panel)); + gainInfo.push_back(gInfo); + i++; + } + + setThemeColors(); +} + +void GainCanvas::setThemeColors() { + std::vector::iterator gi; + + RGBA4f c1, c2; + + c1 = ThemeMgr::mgr.currentTheme->generalBackground; + c2 = ThemeMgr::mgr.currentTheme->generalBackground * 0.5; + + bgPanel.setFillColor(c1, c2); + + for (gi = gainInfo.begin(); gi != gainInfo.end(); gi++) { + GainInfo *gInfo = (*gi); + + c1 = ThemeMgr::mgr.currentTheme->generalBackground; + c2 = ThemeMgr::mgr.currentTheme->generalBackground * 0.5; + c1.a = 1.0; + c2.a = 1.0; + gInfo->panel.setFillColor(c1, c2); + + c1 = ThemeMgr::mgr.currentTheme->meterLevel * 0.5; + c2 = ThemeMgr::mgr.currentTheme->meterLevel; + c1.a = 1.0; + c2.a = 1.0; + gInfo->levelPanel.setFillColor(c1, c2); + + c1 = RGBA4f(0.3,0.3,0.3,1.0); + c2 = RGBA4f(0.65,0.65,0.65,1.0);; + gInfo->highlightPanel.setFillColor(c1, c2); + } + Refresh(); +} + diff --git a/src/visual/GainCanvas.h b/src/visual/GainCanvas.h new file mode 100644 index 0000000..d1eb50d --- /dev/null +++ b/src/visual/GainCanvas.h @@ -0,0 +1,63 @@ +#pragma once + +#include "wx/glcanvas.h" +#include "wx/timer.h" + +#include +#include + +#include "InteractiveCanvas.h" +#include "MouseTracker.h" +#include "GLPanel.h" +#include "PrimaryGLContext.h" + +#include "fftw3.h" +#include "Timer.h" + +class GainInfo { +public: + std::string name; + float low, high, current; + bool changed; + GLPanel panel; + GLPanel levelPanel; + GLPanel highlightPanel; + GLTextPanel labelPanel; + GLTextPanel valuePanel; +}; + +class GainCanvas: public InteractiveCanvas { +public: + GainCanvas(wxWindow *parent, int *attribList = NULL); + ~GainCanvas(); + + void setHelpTip(std::string tip); + void updateGainUI(); + void setThemeColors(); + +private: + void OnPaint(wxPaintEvent& event); + void OnIdle(wxIdleEvent &event); + + int GetPanelHit(CubicVR::vec2 &result); + void SetLevel(); + + void OnShow(wxShowEvent& event); + void OnMouseMoved(wxMouseEvent& event); + void OnMouseDown(wxMouseEvent& event); + void OnMouseWheelMoved(wxMouseEvent& event); + void OnMouseReleased(wxMouseEvent& event); + void OnMouseEnterWindow(wxMouseEvent& event); + void OnMouseLeftWindow(wxMouseEvent& event); + + PrimaryGLContext *glContext; + std::string helpTip; + std::vector gainInfo; + GLPanel bgPanel; + + float spacing, barWidth, startPos, barHeight, numGains; + wxSize clientSize; + // +wxDECLARE_EVENT_TABLE(); +}; + diff --git a/src/visual/InteractiveCanvas.cpp b/src/visual/InteractiveCanvas.cpp index 19cc0bc..9015de9 100644 --- a/src/visual/InteractiveCanvas.cpp +++ b/src/visual/InteractiveCanvas.cpp @@ -19,7 +19,7 @@ InteractiveCanvas::InteractiveCanvas(wxWindow *parent, int *attribList) : wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize, - wxFULL_REPAINT_ON_RESIZE), parent(parent), shiftDown(false), altDown(false), ctrlDown(false), centerFreq(0), bandwidth(0), lastBandwidth(0), isView( + wxFULL_REPAINT_ON_RESIZE| wxWANTS_CHARS), parent(parent), shiftDown(false), altDown(false), ctrlDown(false), centerFreq(0), bandwidth(0), lastBandwidth(0), isView( false) { mouseTracker.setTarget(this); } @@ -150,11 +150,11 @@ void InteractiveCanvas::OnMouseEnterWindow(wxMouseEvent& event) { } void InteractiveCanvas::setStatusText(std::string statusText) { - ((wxFrame*) parent)->GetStatusBar()->SetStatusText(statusText); + wxGetApp().getAppFrame()->GetStatusBar()->SetStatusText(statusText); } void InteractiveCanvas::setStatusText(std::string statusText, int value) { - ((wxFrame*) parent)->GetStatusBar()->SetStatusText( + wxGetApp().getAppFrame()->GetStatusBar()->SetStatusText( wxString::Format(statusText.c_str(), wxNumberFormatter::ToString((long) value, wxNumberFormatter::Style_WithThousandsSep))); } diff --git a/src/visual/InteractiveCanvas.h b/src/visual/InteractiveCanvas.h index bbc4024..28072cf 100644 --- a/src/visual/InteractiveCanvas.h +++ b/src/visual/InteractiveCanvas.h @@ -13,8 +13,8 @@ public: long long getFrequencyAt(float x); - void setView(long long center_freq_in, int bandwidth_in); - void disableView(); + virtual void setView(long long center_freq_in, int bandwidth_in); + virtual void disableView(); bool getViewState(); void setCenterFrequency(long long center_freq_in); diff --git a/src/visual/SpectrumCanvas.cpp b/src/visual/SpectrumCanvas.cpp index adcc96c..53379de 100644 --- a/src/visual/SpectrumCanvas.cpp +++ b/src/visual/SpectrumCanvas.cpp @@ -25,6 +25,8 @@ EVT_LEFT_UP(SpectrumCanvas::OnMouseReleased) EVT_ENTER_WINDOW(SpectrumCanvas::OnMouseEnterWindow) EVT_LEAVE_WINDOW(SpectrumCanvas::OnMouseLeftWindow) EVT_MOUSEWHEEL(SpectrumCanvas::OnMouseWheelMoved) +EVT_RIGHT_DOWN(SpectrumCanvas::OnMouseRightDown) +EVT_RIGHT_UP(SpectrumCanvas::OnMouseRightReleased) wxEND_EVENT_TABLE() SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : @@ -32,10 +34,13 @@ SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) : glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this)); - mouseTracker.setVertDragLock(true); visualDataQueue.set_max_num_items(1); SetCursor(wxCURSOR_SIZEWE); + scaleFactor = 1.0; + resetScaleFactor = false; + scaleFactorEnabled = false; + bwChange = 0.0; } SpectrumCanvas::~SpectrumCanvas() { @@ -59,6 +64,15 @@ void SpectrumCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { } } + if (resetScaleFactor) { + scaleFactor += (1.0-scaleFactor)*0.05; + if (fabs(scaleFactor-1.0) < 0.01) { + scaleFactor = 1.0; + resetScaleFactor = false; + } + updateScaleFactor(scaleFactor); + } + glContext->SetCurrent(*this); initGLExtensions(); @@ -137,6 +151,34 @@ bool SpectrumCanvas::getShowDb() { return spectrumPanel.getShowDb(); } +void SpectrumCanvas::setView(long long center_freq_in, int bandwidth_in) { + bwChange += bandwidth_in-bandwidth; + #define BW_RESET_TH 400000 + if (bwChange > BW_RESET_TH || bwChange < -BW_RESET_TH) { + resetScaleFactor = true; + bwChange = 0; + } + InteractiveCanvas::setView(center_freq_in, bandwidth_in); +} + +void SpectrumCanvas::disableView() { + InteractiveCanvas::disableView(); +} + +void SpectrumCanvas::setScaleFactorEnabled(bool en) { + scaleFactorEnabled = en; +} + + +void SpectrumCanvas::updateScaleFactor(float factor) { + SpectrumVisualProcessor *sp = wxGetApp().getSpectrumProcessor(); + FFTVisualDataThread *wdt = wxGetApp().getAppFrame()->getWaterfallDataThread(); + SpectrumVisualProcessor *wp = wdt->getProcessor(); + + scaleFactor = factor; + sp->setScaleFactor(factor); + wp->setScaleFactor(factor); +} void SpectrumCanvas::OnMouseMoved(wxMouseEvent& event) { InteractiveCanvas::OnMouseMoved(event); @@ -146,12 +188,32 @@ void SpectrumCanvas::OnMouseMoved(wxMouseEvent& event) { if (freqChange != 0) { moveCenterFrequency(freqChange); } + } + else if (scaleFactorEnabled && mouseTracker.mouseRightDown()) { + + float yDelta = mouseTracker.getDeltaMouseY(); + + scaleFactor += yDelta*2.0; + if (scaleFactor < 0.25) { + scaleFactor = 0.25; + } + if (scaleFactor > 10.0) { + scaleFactor = 10.0; + } + + resetScaleFactor = false; + updateScaleFactor(scaleFactor); } else { - setStatusText("Click and drag to adjust center frequency. 'B' to toggle decibels display."); + if (scaleFactorEnabled) { + setStatusText("Drag horizontal to adjust center frequency. Right-drag or SHIFT+UP/DOWN to adjust vertical scale; right-click to reset. 'B' to toggle decibels display."); + } else { + setStatusText("Displaying spectrum of active demodulator."); + } } } void SpectrumCanvas::OnMouseDown(wxMouseEvent& event) { + mouseTracker.setVertDragLock(true); InteractiveCanvas::OnMouseDown(event); SetCursor(wxCURSOR_CROSS); } @@ -161,7 +223,8 @@ void SpectrumCanvas::OnMouseWheelMoved(wxMouseEvent& event) { } void SpectrumCanvas::OnMouseReleased(wxMouseEvent& event) { - InteractiveCanvas::OnMouseReleased(event); + mouseTracker.setVertDragLock(false); + InteractiveCanvas::OnMouseReleased(event); SetCursor(wxCURSOR_SIZEWE); } @@ -182,3 +245,17 @@ void SpectrumCanvas::attachWaterfallCanvas(WaterfallCanvas* canvas_in) { SpectrumVisualDataQueue *SpectrumCanvas::getVisualDataQueue() { return &visualDataQueue; } + +void SpectrumCanvas::OnMouseRightDown(wxMouseEvent& event) { + mouseTracker.setHorizDragLock(true); + mouseTracker.OnMouseRightDown(event); + scaleFactor = wxGetApp().getSpectrumProcessor()->getScaleFactor(); +} + +void SpectrumCanvas::OnMouseRightReleased(wxMouseEvent& event) { + mouseTracker.setHorizDragLock(false); + if (!mouseTracker.getOriginDeltaMouseY()) { + resetScaleFactor = true; + } + mouseTracker.OnMouseRightReleased(event); +} diff --git a/src/visual/SpectrumCanvas.h b/src/visual/SpectrumCanvas.h index 238c48f..e212565 100644 --- a/src/visual/SpectrumCanvas.h +++ b/src/visual/SpectrumCanvas.h @@ -22,6 +22,11 @@ public: void setShowDb(bool showDb); bool getShowDb(); + void setView(long long center_freq_in, int bandwidth_in); + void disableView(); + + void setScaleFactorEnabled(bool en); + SpectrumVisualDataQueue *getVisualDataQueue(); private: @@ -35,10 +40,17 @@ private: void OnMouseReleased(wxMouseEvent& event); void OnMouseEnterWindow(wxMouseEvent& event); void OnMouseLeftWindow(wxMouseEvent& event); + void OnMouseRightDown(wxMouseEvent& event); + void OnMouseRightReleased(wxMouseEvent& event); + void updateScaleFactor(float factor); + PrimaryGLContext *glContext; WaterfallCanvas *waterfallCanvas; SpectrumPanel spectrumPanel; + float scaleFactor; + int bwChange; + bool resetScaleFactor, scaleFactorEnabled; SpectrumVisualDataQueue visualDataQueue; diff --git a/src/visual/TuningCanvas.cpp b/src/visual/TuningCanvas.cpp index a9fe015..ffe2a36 100644 --- a/src/visual/TuningCanvas.cpp +++ b/src/visual/TuningCanvas.cpp @@ -190,8 +190,8 @@ void TuningCanvas::StepTuner(ActiveState state, int exponent, bool up) { bw += amount; } - if (bw > wxGetApp().getSampleRate()) { - bw = wxGetApp().getSampleRate(); + if (bw > CHANNELIZER_RATE_MAX) { + bw = CHANNELIZER_RATE_MAX; } wxGetApp().getDemodMgr().setLastBandwidth(bw); diff --git a/src/visual/WaterfallCanvas.cpp b/src/visual/WaterfallCanvas.cpp index 91e18d6..971552e 100644 --- a/src/visual/WaterfallCanvas.cpp +++ b/src/visual/WaterfallCanvas.cpp @@ -43,6 +43,7 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) : lpsIndex = 0; preBuf = false; SetCursor(wxCURSOR_CROSS); + scaleMove = 0; } WaterfallCanvas::~WaterfallCanvas() { @@ -72,19 +73,16 @@ void WaterfallCanvas::attachSpectrumCanvas(SpectrumCanvas *canvas_in) { } void WaterfallCanvas::processInputQueue() { - if (!glContext) { - return; - } - glContext->SetCurrent(*this); + tex_update.lock(); gTimer.update(); double targetVis = 1.0 / (double)linesPerSecond; lpsIndex += gTimer.lastUpdateSeconds(); + bool updated = false; if (linesPerSecond) { if (lpsIndex >= targetVis) { - tex_update.lock(); while (lpsIndex >= targetVis) { SpectrumVisualData *vData; if (!visualDataQueue.empty()) { @@ -94,21 +92,27 @@ void WaterfallCanvas::processInputQueue() { waterfallPanel.setPoints(vData->spectrum_points); waterfallPanel.step(); vData->decRefCount(); + updated = true; } lpsIndex-=targetVis; } else { break; } } - tex_update.unlock(); } - }} + } + if (updated) { + wxClientDC(this); + glContext->SetCurrent(*this); + waterfallPanel.update(); + } + tex_update.unlock(); +} void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { + tex_update.lock(); wxPaintDC dc(this); - processInputQueue(); - const wxSize ClientSize = GetClientSize(); long double currentZoom = zoom; @@ -120,9 +124,33 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { } } + if (scaleMove != 0) { + SpectrumVisualProcessor *sp = wxGetApp().getSpectrumProcessor(); + FFTVisualDataThread *wdt = wxGetApp().getAppFrame()->getWaterfallDataThread(); + SpectrumVisualProcessor *wp = wdt->getProcessor(); + float factor = sp->getScaleFactor(); + + factor += scaleMove * 0.02; + + if (factor < 0.25) { + factor = 0.25; + } + if (factor > 10.0) { + factor = 10.0; + } + + sp->setScaleFactor(factor); + wp->setScaleFactor(factor); + } + if (freqMove != 0.0) { long long newFreq = getCenterFrequency() + (long long)((long double)getBandwidth()*freqMove) * 0.01; + long long minFreq = bandwidth/2; + if (newFreq < minFreq) { + newFreq = minFreq; + } + updateCenterFrequency(newFreq); if (!freqMoving) { @@ -211,9 +239,7 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { glContext->BeginDraw(0,0,0); waterfallPanel.calcTransform(CubicVR::mat4::identity()); - tex_update.lock(); waterfallPanel.draw(); - tex_update.unlock(); std::vector &demods = wxGetApp().getDemodMgr().getDemodulators(); @@ -293,6 +319,7 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) { glContext->EndDraw(); SwapBuffers(); + tex_update.unlock(); } void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) { @@ -304,14 +331,20 @@ void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) { case 'A': case WXK_UP: case WXK_NUMPAD_UP: + scaleMove = 0.0; zoom = 1.0; - mouseZoom = 0.95; + if (mouseZoom != 1.0) { + mouseZoom = 0.95; + } break; case 'Z': case WXK_DOWN: case WXK_NUMPAD_DOWN: + scaleMove = 0.0; zoom = 1.0; - mouseZoom = 1.05; + if (mouseZoom != 1.0) { + mouseZoom = 1.05; + } break; case WXK_LEFT: case WXK_NUMPAD_LEFT: @@ -334,14 +367,22 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { case 'A': case WXK_UP: case WXK_NUMPAD_UP: - mouseZoom = 1.0; - zoom = 0.95; + if (!shiftDown) { + mouseZoom = 1.0; + zoom = 0.95; + } else { + scaleMove = 1.0; + } break; case 'Z': case WXK_DOWN: case WXK_NUMPAD_DOWN: - mouseZoom = 1.0; - zoom = 1.05; + if (!shiftDown) { + mouseZoom = 1.0; + zoom = 1.05; + } else { + scaleMove = -1.0; + } break; case WXK_RIGHT: case WXK_NUMPAD_RIGHT: @@ -398,7 +439,7 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { return; } - long long minFreq = wxGetApp().getSampleRate()/2; + long long minFreq = bandwidth/2; if (freq < minFreq) { freq = minFreq; } @@ -409,11 +450,9 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) { } void WaterfallCanvas::OnIdle(wxIdleEvent &event) { + processInputQueue(); Refresh(); event.RequestMore(); - if (visualDataQueue.size() > linesPerSecond) { - processInputQueue(); - } } void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { @@ -435,8 +474,8 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) { int currentBW = demod->getBandwidth(); currentBW = currentBW + bwDiff; - if (currentBW > wxGetApp().getSampleRate()) { - currentBW = wxGetApp().getSampleRate(); + if (currentBW > CHANNELIZER_RATE_MAX) { + currentBW = CHANNELIZER_RATE_MAX; } if (currentBW < MIN_BANDWIDTH) { currentBW = MIN_BANDWIDTH; @@ -802,6 +841,7 @@ void WaterfallCanvas::updateCenterFrequency(long long freq) { } void WaterfallCanvas::setLinesPerSecond(int lps) { + tex_update.lock(); linesPerSecond = lps; while (!visualDataQueue.empty()) { SpectrumVisualData *vData; @@ -811,7 +851,7 @@ void WaterfallCanvas::setLinesPerSecond(int lps) { vData->decRefCount(); } } - + tex_update.unlock(); } diff --git a/src/visual/WaterfallCanvas.h b/src/visual/WaterfallCanvas.h index 03fb07b..9efa8c4 100644 --- a/src/visual/WaterfallCanvas.h +++ b/src/visual/WaterfallCanvas.h @@ -67,6 +67,7 @@ private: long double freqMove; float hoverAlpha; int linesPerSecond; + float scaleMove; SpectrumVisualDataQueue visualDataQueue; Timer gTimer;