mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2024-11-21 11:21:47 -05:00
Update digital_lab
This commit is contained in:
commit
80e0d7019f
9
.gitignore
vendored
9
.gitignore
vendored
@ -1,10 +1,3 @@
|
||||
CMakeCache.txt
|
||||
CMakeFiles/
|
||||
Makefile
|
||||
cmake_install.cmake
|
||||
compile_commands.json
|
||||
nbproject/
|
||||
x64/
|
||||
build/
|
||||
cmake_build/
|
||||
cmake_build_msvc/
|
||||
dist/
|
||||
|
274
CMakeLists.txt
274
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 <cj@cubicproductions.com>")
|
||||
SET(CPACK_DEBIAN_PACKAGE_DEPENDS " libfftw3-single3, libwxgtk3.0-0, libpulse0")
|
||||
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "Charles J. Cliffe <cj@cubicproductions.com>")
|
||||
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")
|
||||
|
54
README.md
54
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:
|
||||
|
22
cmake/Modules/FindFFTW.cmake
Normal file
22
cmake/Modules/FindFFTW.cmake
Normal file
@ -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)
|
22
cmake/Modules/FindLiquid.cmake
Normal file
22
cmake/Modules/FindLiquid.cmake
Normal file
@ -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)
|
BIN
external/fftw-3.3.4/64/libfftw3f-3.exp
vendored
BIN
external/fftw-3.3.4/64/libfftw3f-3.exp
vendored
Binary file not shown.
BIN
external/fftw-3.3.4/64/libfftw3f-3.lib
vendored
Normal file
BIN
external/fftw-3.3.4/64/libfftw3f-3.lib
vendored
Normal file
Binary file not shown.
BIN
external/msvc/x64/vc_redist.x64.exe
vendored
Normal file
BIN
external/msvc/x64/vc_redist.x64.exe
vendored
Normal file
Binary file not shown.
BIN
external/msvc/x64/vcredist_x64.exe
vendored
BIN
external/msvc/x64/vcredist_x64.exe
vendored
Binary file not shown.
BIN
external/msvc/x86/vc_redist.x86.exe
vendored
Normal file
BIN
external/msvc/x86/vc_redist.x86.exe
vendored
Normal file
Binary file not shown.
BIN
external/msvc/x86/vcredist_x86.exe
vendored
BIN
external/msvc/x86/vcredist_x86.exe
vendored
Binary file not shown.
4
external/rtl-sdr-release/AUTHORS
vendored
4
external/rtl-sdr-release/AUTHORS
vendored
@ -1,4 +0,0 @@
|
||||
Steve Markgraf <steve@steve-m.de>
|
||||
Dimitri Stolnikov <horiz0n@gmx.net>
|
||||
Hoernchen <la@tfc-server.de>
|
||||
Kyle Keen <keenerd@gmail.com>
|
339
external/rtl-sdr-release/COPYING
vendored
339
external/rtl-sdr-release/COPYING
vendored
@ -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.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
<signature of Ty Coon>, 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.
|
504
external/rtl-sdr-release/COPYING.libusbx
vendored
504
external/rtl-sdr-release/COPYING.libusbx
vendored
@ -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.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
|
6
external/rtl-sdr-release/README
vendored
6
external/rtl-sdr-release/README
vendored
@ -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
|
10
external/rtl-sdr-release/README.windows.txt
vendored
10
external/rtl-sdr-release/README.windows.txt
vendored
@ -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
|
Binary file not shown.
BIN
external/rtl-sdr-release/gcc/32/libusb-1.0.dll
vendored
BIN
external/rtl-sdr-release/gcc/32/libusb-1.0.dll
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/gcc/32/rtlsdr.dll
vendored
BIN
external/rtl-sdr-release/gcc/32/rtlsdr.dll
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/gcc/32/rtlsdr.lib
vendored
BIN
external/rtl-sdr-release/gcc/32/rtlsdr.lib
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/gcc/32/rtlsdr_static.lib
vendored
BIN
external/rtl-sdr-release/gcc/32/rtlsdr_static.lib
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
external/rtl-sdr-release/gcc/64/librtlsdr.dll
vendored
BIN
external/rtl-sdr-release/gcc/64/librtlsdr.dll
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/gcc/64/librtlsdr.dll.a
vendored
BIN
external/rtl-sdr-release/gcc/64/librtlsdr.dll.a
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/gcc/64/librtlsdr_static.a
vendored
BIN
external/rtl-sdr-release/gcc/64/librtlsdr_static.a
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/gcc/64/libusb-1.0.dll
vendored
BIN
external/rtl-sdr-release/gcc/64/libusb-1.0.dll
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/msvc/32/libusb-1.0.dll
vendored
BIN
external/rtl-sdr-release/msvc/32/libusb-1.0.dll
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/msvc/32/rtlsdr.dll
vendored
BIN
external/rtl-sdr-release/msvc/32/rtlsdr.dll
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/msvc/32/rtlsdr.exp
vendored
BIN
external/rtl-sdr-release/msvc/32/rtlsdr.exp
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/msvc/32/rtlsdr.lib
vendored
BIN
external/rtl-sdr-release/msvc/32/rtlsdr.lib
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/msvc/32/rtlsdr_static.lib
vendored
BIN
external/rtl-sdr-release/msvc/32/rtlsdr_static.lib
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/msvc/64/libusb-1.0.dll
vendored
BIN
external/rtl-sdr-release/msvc/64/libusb-1.0.dll
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/msvc/64/rtlsdr.dll
vendored
BIN
external/rtl-sdr-release/msvc/64/rtlsdr.dll
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/msvc/64/rtlsdr.exp
vendored
BIN
external/rtl-sdr-release/msvc/64/rtlsdr.exp
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/msvc/64/rtlsdr.lib
vendored
BIN
external/rtl-sdr-release/msvc/64/rtlsdr.lib
vendored
Binary file not shown.
BIN
external/rtl-sdr-release/msvc/64/rtlsdr_static.lib
vendored
BIN
external/rtl-sdr-release/msvc/64/rtlsdr_static.lib
vendored
Binary file not shown.
378
external/rtl-sdr-release/rtl-sdr.h
vendored
378
external/rtl-sdr-release/rtl-sdr.h
vendored
@ -1,378 +0,0 @@
|
||||
/*
|
||||
* rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
|
||||
* Copyright (C) 2012-2013 by Steve Markgraf <steve@steve-m.de>
|
||||
* Copyright (C) 2012 by Dimitri Stolnikov <horiz0n@gmx.net>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RTL_SDR_H
|
||||
#define __RTL_SDR_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <rtl-sdr_export.h>
|
||||
|
||||
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 */
|
47
external/rtl-sdr-release/rtl-sdr_export.h
vendored
47
external/rtl-sdr-release/rtl-sdr_export.h
vendored
@ -1,47 +0,0 @@
|
||||
/*
|
||||
* rtl-sdr, turns your Realtek RTL2832 based DVB dongle into a SDR receiver
|
||||
* Copyright (C) 2012 by Hoernchen <la@tfc-server.de>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 */
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
508
src/AppFrame.cpp
508
src/AppFrame.cpp
@ -14,12 +14,11 @@
|
||||
#endif
|
||||
|
||||
#include <vector>
|
||||
#include "SDRThread.h"
|
||||
#include "DemodulatorMgr.h"
|
||||
#include "AudioThread.h"
|
||||
#include "CubicSDR.h"
|
||||
#include "DataTree.h"
|
||||
#include "ColorTheme.h"
|
||||
#include "DemodulatorMgr.h"
|
||||
|
||||
#include <thread>
|
||||
|
||||
@ -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,72 +153,98 @@ 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);
|
||||
@ -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;
|
||||
settingsMenu = 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"));
|
||||
menuBar->Append(settingsMenu, wxT("&Settings"));
|
||||
|
||||
menu = new wxMenu;
|
||||
|
||||
@ -297,54 +331,6 @@ AppFrame::AppFrame() :
|
||||
|
||||
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<SDRDeviceInfo *> *devs = wxGetApp().getDevices();
|
||||
std::vector<SDRDeviceInfo *>::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,6 +430,9 @@ 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");
|
||||
@ -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<std::string>::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<long>::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,71 +674,97 @@ 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<SoapySDR::ArgInfo>::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<SDRDeviceInfo *> *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) {
|
||||
@ -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,7 +1004,7 @@ 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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "wx/frame.h"
|
||||
#include <wx/frame.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/splitter.h>
|
||||
#include <wx/sizer.h>
|
||||
|
||||
#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 <map>
|
||||
@ -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,17 +59,21 @@ 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;
|
||||
@ -85,6 +90,10 @@ private:
|
||||
MeterCanvas *spectrumAvgMeter;
|
||||
MeterCanvas *waterfallSpeedMeter;
|
||||
ModeSelectorCanvas *demodMuteButton;
|
||||
GainCanvas *gainCanvas;
|
||||
wxSizerItem *gainSizerItem, *gainSpacerItem;
|
||||
wxSplitterWindow *mainVisSplitter, *mainSplitter;
|
||||
wxBoxSizer *demodTray;
|
||||
|
||||
DemodulatorInstance *activeDemodulator;
|
||||
|
||||
@ -95,13 +104,21 @@ private:
|
||||
std::map<int, wxMenuItem *> sampleRateMenuItems;
|
||||
std::map<int, wxMenuItem *> audioSampleRateMenuItems;
|
||||
std::map<int, wxMenuItem *> directSamplingMenuItems;
|
||||
wxMenuItem *iqSwapMenuItem;
|
||||
wxMenuBar *menuBar;
|
||||
wxMenu *sampleRateMenu;
|
||||
wxMenuItem *agcMenuItem;
|
||||
wxMenu *settingsMenu;
|
||||
SoapySDR::ArgInfoList settingArgs;
|
||||
int settingsIdMax;
|
||||
std::vector<long> sampleRates;
|
||||
|
||||
std::string currentSessionFile;
|
||||
|
||||
FFTVisualDataThread *waterfallDataThread;
|
||||
|
||||
std::thread *t_FFTData;
|
||||
SDRDeviceInfo *devInfo;
|
||||
std::atomic_bool deviceChanged;
|
||||
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
502
src/CubicSDR.cpp
502
src/CubicSDR.cpp
@ -12,6 +12,7 @@
|
||||
#endif
|
||||
|
||||
#include "CubicSDR.h"
|
||||
#include <iomanip>
|
||||
|
||||
#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<SDRDeviceInfo *>::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<DemodulatorThreadIQData> *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<SDRDeviceInfo*>* 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();
|
||||
}
|
@ -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 <wx/cmdline.h>
|
||||
|
||||
#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<SDRDeviceInfo *> *getDevices();
|
||||
void setDevice(int deviceId);
|
||||
int getDevice();
|
||||
void setDevice(SDRDeviceInfo *dev);
|
||||
SDRDeviceInfo * getDevice();
|
||||
|
||||
ScopeVisualProcessor *getScopeProcessor();
|
||||
SpectrumVisualProcessor *getSpectrumProcessor();
|
||||
SpectrumVisualProcessor *getDemodSpectrumProcessor();
|
||||
VisualDataReDistributor<DemodulatorThreadIQData> *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<SDRDeviceInfo *> devs;
|
||||
std::vector<SDRDeviceInfo *> *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<DemodulatorThreadIQData> 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)
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include "wx/clipbrd.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#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();
|
||||
|
@ -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()
|
||||
};
|
||||
|
@ -20,7 +20,7 @@ AudioThread::AudioThread() : IOThread(),
|
||||
outputDevice.store(-1);
|
||||
gain.store(1.0);
|
||||
|
||||
boundThreads = new std::vector<AudioThread *>;
|
||||
boundThreads.store(new std::vector<AudioThread *>);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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<AudioThreadInput> 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;
|
||||
|
267
src/forms/SDRDevices/SDRDevices.cpp
Normal file
267
src/forms/SDRDevices/SDRDevices.cpp
Normal file
@ -0,0 +1,267 @@
|
||||
#include "SDRDevices.h"
|
||||
|
||||
#include <wx/textdlg.h>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
#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<std::string>::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<std::string> remotes = SDREnumerator::getRemotes();
|
||||
std::vector<std::string>::iterator remotes_i;
|
||||
std::vector<SDRDeviceInfo *>::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;
|
||||
}
|
||||
}
|
945
src/forms/SDRDevices/SDRDevices.fbp
Normal file
945
src/forms/SDRDevices/SDRDevices.fbp
Normal file
@ -0,0 +1,945 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<wxFormBuilder_Project>
|
||||
<FileVersion major="1" minor="13" />
|
||||
<object class="Project" expanded="1">
|
||||
<property name="class_decoration"></property>
|
||||
<property name="code_generation">C++</property>
|
||||
<property name="disconnect_events">1</property>
|
||||
<property name="disconnect_mode">source_name</property>
|
||||
<property name="disconnect_php_events">0</property>
|
||||
<property name="disconnect_python_events">0</property>
|
||||
<property name="embedded_files_path">res</property>
|
||||
<property name="encoding">UTF-8</property>
|
||||
<property name="event_generation">connect</property>
|
||||
<property name="file">SDRDevicesForm</property>
|
||||
<property name="first_id">1000</property>
|
||||
<property name="help_provider">none</property>
|
||||
<property name="internationalize">0</property>
|
||||
<property name="name">SDRDevicesForm</property>
|
||||
<property name="namespace"></property>
|
||||
<property name="path">.</property>
|
||||
<property name="precompiled_header"></property>
|
||||
<property name="relative_path">1</property>
|
||||
<property name="skip_lua_events">1</property>
|
||||
<property name="skip_php_events">1</property>
|
||||
<property name="skip_python_events">1</property>
|
||||
<property name="ui_table">UI</property>
|
||||
<property name="use_enum">0</property>
|
||||
<property name="use_microsoft_bom">0</property>
|
||||
<object class="Frame" expanded="1">
|
||||
<property name="aui_managed">0</property>
|
||||
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
|
||||
<property name="bg"></property>
|
||||
<property name="center">wxBOTH</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="event_handler">impl_virtual</property>
|
||||
<property name="extra_style"></property>
|
||||
<property name="fg"></property>
|
||||
<property name="font"></property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">devFrame</property>
|
||||
<property name="pos"></property>
|
||||
<property name="size">700,467</property>
|
||||
<property name="style">wxDEFAULT_FRAME_STYLE</property>
|
||||
<property name="subclass"></property>
|
||||
<property name="title">CubicSDR :: SDR Devices</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style">wxTAB_TRAVERSAL</property>
|
||||
<property name="xrc_skip_sizer">1</property>
|
||||
<event name="OnActivate"></event>
|
||||
<event name="OnActivateApp"></event>
|
||||
<event name="OnAuiFindManager"></event>
|
||||
<event name="OnAuiPaneButton"></event>
|
||||
<event name="OnAuiPaneClose"></event>
|
||||
<event name="OnAuiPaneMaximize"></event>
|
||||
<event name="OnAuiPaneRestore"></event>
|
||||
<event name="OnAuiRender"></event>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnClose">OnClose</event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnHibernate"></event>
|
||||
<event name="OnIconize"></event>
|
||||
<event name="OnIdle"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
<object class="wxStatusBar" expanded="1">
|
||||
<property name="bg"></property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="fields">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">devStatusBar</property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pos"></property>
|
||||
<property name="size"></property>
|
||||
<property name="style">wxST_SIZEGRIP</property>
|
||||
<property name="subclass"></property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">devFrameSizer</property>
|
||||
<property name="orient">wxVERTICAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND | wxALL</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxPanel" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_panel3</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style">wxTAB_TRAVERSAL</property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">bSizer4</property>
|
||||
<property name="orient">wxHORIZONTAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND | wxALL</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxPanel" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_panel6</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style">wxTAB_TRAVERSAL</property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">bSizer6</property>
|
||||
<property name="orient">wxVERTICAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="0">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND|wxALIGN_RIGHT</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxTreeCtrl" expanded="0">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">0</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">devTree</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style">wxTR_DEFAULT_STYLE</property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick">OnTreeDoubleClick</event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnTreeBeginDrag"></event>
|
||||
<event name="OnTreeBeginLabelEdit"></event>
|
||||
<event name="OnTreeBeginRDrag"></event>
|
||||
<event name="OnTreeDeleteItem">OnDeleteItem</event>
|
||||
<event name="OnTreeEndDrag"></event>
|
||||
<event name="OnTreeEndLabelEdit"></event>
|
||||
<event name="OnTreeGetInfo"></event>
|
||||
<event name="OnTreeItemActivated"></event>
|
||||
<event name="OnTreeItemCollapsed"></event>
|
||||
<event name="OnTreeItemCollapsing"></event>
|
||||
<event name="OnTreeItemExpanded"></event>
|
||||
<event name="OnTreeItemExpanding"></event>
|
||||
<event name="OnTreeItemGetTooltip"></event>
|
||||
<event name="OnTreeItemMenu"></event>
|
||||
<event name="OnTreeItemMiddleClick"></event>
|
||||
<event name="OnTreeItemRightClick"></event>
|
||||
<event name="OnTreeKeyDown"></event>
|
||||
<event name="OnTreeSelChanged">OnSelectionChanged</event>
|
||||
<event name="OnTreeSelChanging"></event>
|
||||
<event name="OnTreeSetInfo"></event>
|
||||
<event name="OnTreeStateImageClick"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxPanel" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_panel4</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style">wxTAB_TRAVERSAL</property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">bSizer5</property>
|
||||
<property name="orient">wxHORIZONTAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="0">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxButton" expanded="0">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default">0</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">Add Remote</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_addRemoteButton</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="validator_data_type"></property>
|
||||
<property name="validator_style">wxFILTER_NONE</property>
|
||||
<property name="validator_type">wxDefaultValidator</property>
|
||||
<property name="validator_variable"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnButtonClick"></event>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp">OnAddRemote</event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="0">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxButton" expanded="0">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default">0</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">Use Selected</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_useSelectedButton</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="validator_data_type"></property>
|
||||
<property name="validator_style">wxFILTER_NONE</property>
|
||||
<property name="validator_type">wxDefaultValidator</property>
|
||||
<property name="validator_variable"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnButtonClick"></event>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp">OnUseSelected</event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxEXPAND | wxALL</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxPanel" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_panel61</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style">wxTAB_TRAVERSAL</property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
<object class="wxBoxSizer" expanded="1">
|
||||
<property name="minimum_size"></property>
|
||||
<property name="name">bSizer7</property>
|
||||
<property name="orient">wxVERTICAL</property>
|
||||
<property name="permission">none</property>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL</property>
|
||||
<property name="proportion">0</property>
|
||||
<object class="wxStaticText" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="label">SoapySDR Device Options</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_staticText1</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style"></property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<property name="wrap">-1</property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
<object class="sizeritem" expanded="1">
|
||||
<property name="border">5</property>
|
||||
<property name="flag">wxALL|wxEXPAND</property>
|
||||
<property name="proportion">1</property>
|
||||
<object class="wxPropertyGrid" expanded="1">
|
||||
<property name="BottomDockable">1</property>
|
||||
<property name="LeftDockable">1</property>
|
||||
<property name="RightDockable">1</property>
|
||||
<property name="TopDockable">1</property>
|
||||
<property name="aui_layer"></property>
|
||||
<property name="aui_name"></property>
|
||||
<property name="aui_position"></property>
|
||||
<property name="aui_row"></property>
|
||||
<property name="best_size"></property>
|
||||
<property name="bg"></property>
|
||||
<property name="bitmap"></property>
|
||||
<property name="caption"></property>
|
||||
<property name="caption_visible">1</property>
|
||||
<property name="center_pane">0</property>
|
||||
<property name="close_button">1</property>
|
||||
<property name="context_help"></property>
|
||||
<property name="context_menu">1</property>
|
||||
<property name="default_pane">0</property>
|
||||
<property name="dock">Dock</property>
|
||||
<property name="dock_fixed">0</property>
|
||||
<property name="docking">Left</property>
|
||||
<property name="enabled">1</property>
|
||||
<property name="extra_style"></property>
|
||||
<property name="fg"></property>
|
||||
<property name="floatable">1</property>
|
||||
<property name="font"></property>
|
||||
<property name="gripper">0</property>
|
||||
<property name="hidden">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="include_advanced">1</property>
|
||||
<property name="max_size"></property>
|
||||
<property name="maximize_button">0</property>
|
||||
<property name="maximum_size"></property>
|
||||
<property name="min_size"></property>
|
||||
<property name="minimize_button">0</property>
|
||||
<property name="minimum_size"></property>
|
||||
<property name="moveable">1</property>
|
||||
<property name="name">m_propertyGrid</property>
|
||||
<property name="pane_border">1</property>
|
||||
<property name="pane_position"></property>
|
||||
<property name="pane_size"></property>
|
||||
<property name="permission">protected</property>
|
||||
<property name="pin_button">1</property>
|
||||
<property name="pos"></property>
|
||||
<property name="resize">Resizable</property>
|
||||
<property name="show">1</property>
|
||||
<property name="size"></property>
|
||||
<property name="style">wxPG_DEFAULT_STYLE</property>
|
||||
<property name="subclass"></property>
|
||||
<property name="toolbar_pane">0</property>
|
||||
<property name="tooltip"></property>
|
||||
<property name="window_extra_style"></property>
|
||||
<property name="window_name"></property>
|
||||
<property name="window_style"></property>
|
||||
<event name="OnChar"></event>
|
||||
<event name="OnEnterWindow"></event>
|
||||
<event name="OnEraseBackground"></event>
|
||||
<event name="OnKeyDown"></event>
|
||||
<event name="OnKeyUp"></event>
|
||||
<event name="OnKillFocus"></event>
|
||||
<event name="OnLeaveWindow"></event>
|
||||
<event name="OnLeftDClick"></event>
|
||||
<event name="OnLeftDown"></event>
|
||||
<event name="OnLeftUp"></event>
|
||||
<event name="OnMiddleDClick"></event>
|
||||
<event name="OnMiddleDown"></event>
|
||||
<event name="OnMiddleUp"></event>
|
||||
<event name="OnMotion"></event>
|
||||
<event name="OnMouseEvents"></event>
|
||||
<event name="OnMouseWheel"></event>
|
||||
<event name="OnPaint"></event>
|
||||
<event name="OnPropertyGridChanged"></event>
|
||||
<event name="OnPropertyGridChanging"></event>
|
||||
<event name="OnRightDClick"></event>
|
||||
<event name="OnRightDown"></event>
|
||||
<event name="OnRightUp"></event>
|
||||
<event name="OnSetFocus"></event>
|
||||
<event name="OnSize"></event>
|
||||
<event name="OnUpdateUI"></event>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
<object class="wxTimer" expanded="1">
|
||||
<property name="enabled">0</property>
|
||||
<property name="id">wxID_ANY</property>
|
||||
<property name="name">m_deviceTimer</property>
|
||||
<property name="oneshot">0</property>
|
||||
<property name="period">5000</property>
|
||||
<property name="permission">protected</property>
|
||||
<event name="OnTimer">OnDeviceTimer</event>
|
||||
</object>
|
||||
</object>
|
||||
</object>
|
||||
</wxFormBuilder_Project>
|
34
src/forms/SDRDevices/SDRDevices.h
Normal file
34
src/forms/SDRDevices/SDRDevices.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#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<std::string, std::vector<SDRDeviceInfo *>* > devs;
|
||||
std::vector<SDRDeviceInfo *>::iterator devs_i;
|
||||
std::map<wxTreeItemId, SDRDeviceInfo *> devItems;
|
||||
std::map<wxTreeItemId, SDRDeviceInfo *>::iterator devItems_i;
|
||||
SDRDeviceInfo *dev = NULL;
|
||||
std::vector<wxPGProperty *> props;
|
||||
};
|
106
src/forms/SDRDevices/SDRDevicesForm.cpp
Normal file
106
src/forms/SDRDevices/SDRDevicesForm.cpp
Normal file
@ -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 ) );
|
||||
|
||||
}
|
73
src/forms/SDRDevices/SDRDevicesForm.h
Normal file
73
src/forms/SDRDevices/SDRDevicesForm.h
Normal file
@ -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 <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/statusbr.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/treectrl.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/panel.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/propgrid/propgrid.h>
|
||||
#include <wx/propgrid/advprops.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/frame.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// 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__
|
@ -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<float> &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++) {
|
||||
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);
|
||||
}
|
||||
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++) {
|
||||
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);
|
||||
|
@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "GLPanel.h"
|
||||
#include <atomic>
|
||||
|
||||
class WaterfallPanel : public GLPanel {
|
||||
public:
|
||||
@ -9,6 +10,7 @@ public:
|
||||
void refreshTheme();
|
||||
void setPoints(std::vector<float> &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<unsigned char> lineBuffer[2];
|
||||
std::vector<unsigned char> rLineBuffer[2];
|
||||
std::atomic_int lines_buffered;
|
||||
std::atomic_bool texInitialized, bufferInitialized;
|
||||
|
||||
ColorTheme *activeTheme;
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "VisualProcessor.h"
|
||||
#include "DemodDefs.h"
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
|
||||
class FFTDataDistributor : public VisualProcessor<DemodulatorThreadIQData, DemodulatorThreadIQData> {
|
||||
public:
|
||||
@ -19,4 +20,5 @@ protected:
|
||||
int fftSize;
|
||||
int linesPerSecond;
|
||||
double lineRateAccum;
|
||||
int bufferMax, bufferOffset, bufferedItems;
|
||||
};
|
||||
|
@ -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() {
|
||||
@ -67,8 +69,6 @@ void ScopeVisualProcessor::process() {
|
||||
return;
|
||||
}
|
||||
|
||||
audioInputData->busy_update.lock();
|
||||
|
||||
ScopeRenderData *renderData = NULL;
|
||||
|
||||
if (scopeEnabled) {
|
||||
@ -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) {
|
||||
@ -139,6 +137,13 @@ 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,8 +162,8 @@ 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];
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
output->fft_ceiling = fft_ceil_maa;
|
||||
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/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();
|
||||
}
|
||||
|
||||
|
@ -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<liquid_float_complex> resampleBuffer;
|
||||
std::atomic_int desiredInputSize;
|
||||
std::mutex busy_run;
|
||||
std::atomic_bool hideDC;
|
||||
std::atomic<float> scaleFactor;
|
||||
};
|
||||
|
@ -1,4 +1,163 @@
|
||||
#include "SDRDeviceInfo.h"
|
||||
#include <cstdlib>
|
||||
|
||||
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<SDRDeviceRange> &SDRDeviceChannel::getGains() {
|
||||
return gainInfo;
|
||||
}
|
||||
|
||||
void SDRDeviceChannel::addGain(std::string name, SoapySDR::Range range) {
|
||||
gainInfo.push_back(SDRDeviceRange(name,range.minimum(),range.maximum()));
|
||||
}
|
||||
|
||||
std::vector<long> &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<long>::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<long long> &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<std::string> SDRDeviceChannel::getStreamArgNames() {
|
||||
std::vector<std::string> 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<std::string> SDRDeviceInfo::getSettingNames() {
|
||||
std::vector<std::string> 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<SDRDeviceChannel *> &SDRDeviceInfo::getChannels() {
|
||||
return channels;
|
||||
}
|
||||
|
||||
SDRDeviceChannel * SDRDeviceInfo::getRxChannel() {
|
||||
std::vector<SDRDeviceChannel *>::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<SDRDeviceChannel *>::iterator channel_i;
|
||||
for (channel_i = channels.begin(); channel_i != channels.end(); channel_i++) {
|
||||
if ((*channel_i)->isTx()) {
|
||||
return (*channel_i);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,105 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <SoapySDR/Types.hpp>
|
||||
|
||||
/*
|
||||
----------------------------------------------------
|
||||
-- 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<SDRDeviceRange> &getGains();
|
||||
|
||||
SDRDeviceRange &getGain();
|
||||
SDRDeviceRange &getLNAGain();
|
||||
SDRDeviceRange &getFreqRange();
|
||||
SDRDeviceRange &getRFRange();
|
||||
|
||||
std::vector<long> &getSampleRates();
|
||||
long getSampleRateNear(long sampleRate_in);
|
||||
std::vector<long long> &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<std::string> getStreamArgNames();
|
||||
|
||||
private:
|
||||
int channel;
|
||||
bool fullDuplex, tx, rx, hardwareDC, hasCorr;
|
||||
SDRDeviceRange rangeGain, rangeLNA, rangeFull, rangeRF;
|
||||
std::vector<long> sampleRates;
|
||||
std::vector<long long> filterBandwidths;
|
||||
SoapySDR::ArgInfoList streamArgInfo;
|
||||
std::vector<SDRDeviceRange> 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);
|
||||
|
||||
@ -26,11 +128,38 @@ 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<SDRDeviceChannel *> &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<std::string> 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<SDRDeviceChannel *> channels;
|
||||
};
|
||||
|
298
src/sdr/SDREnumerator.cpp
Normal file
298
src/sdr/SDREnumerator.cpp
Normal file
@ -0,0 +1,298 @@
|
||||
#include "SDREnumerator.h"
|
||||
#include "CubicSDRDefs.h"
|
||||
#include <vector>
|
||||
#include "CubicSDR.h"
|
||||
#include <string>
|
||||
|
||||
|
||||
std::vector<std::string> SDREnumerator::factories;
|
||||
std::vector<std::string> SDREnumerator::modules;
|
||||
std::vector<std::string> SDREnumerator::remotes;
|
||||
std::map< std::string, std::vector<SDRDeviceInfo *> > SDREnumerator::devs;
|
||||
bool SDREnumerator::soapy_initialized = false;
|
||||
bool SDREnumerator::has_remote = false;
|
||||
|
||||
SDREnumerator::SDREnumerator() : IOThread() {
|
||||
|
||||
}
|
||||
|
||||
SDREnumerator::~SDREnumerator() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
std::vector<SDRDeviceInfo *> *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<std::string> localMods = SoapySDR::listModules(exePath.GetPath().ToStdString() + "/modules/");
|
||||
for (std::vector<std::string>::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<SoapySDR::Kwargs> 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<std::string> 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<double> rates = device->listSampleRates(SOAPY_SDR_RX, i);
|
||||
for (std::vector<double>::iterator i = rates.begin(); i != rates.end(); i++) {
|
||||
chan->getSampleRates().push_back((long)(*i));
|
||||
}
|
||||
|
||||
chan->setStreamArgsInfo(device->getStreamArgsInfo(SOAPY_SDR_RX, i));
|
||||
|
||||
std::vector<std::string> gainNames = device->listGains(SOAPY_SDR_RX, i);
|
||||
|
||||
for (std::vector<std::string>::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<std::string>::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<std::string>::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<std::string>::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<std::string> &SDREnumerator::getRemotes() {
|
||||
return remotes;
|
||||
}
|
||||
|
||||
bool SDREnumerator::hasRemoteModule() {
|
||||
return SDREnumerator::has_remote;
|
||||
}
|
39
src/sdr/SDREnumerator.h
Normal file
39
src/sdr/SDREnumerator.h
Normal file
@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "IOThread.h"
|
||||
#include "SDRDeviceInfo.h"
|
||||
#include "AppConfig.h"
|
||||
|
||||
#include <SoapySDR/Version.hpp>
|
||||
#include <SoapySDR/Modules.hpp>
|
||||
#include <SoapySDR/Registry.hpp>
|
||||
#include <SoapySDR/Device.hpp>
|
||||
|
||||
|
||||
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<SDRDeviceInfo *> *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<std::string> &getRemotes();
|
||||
static bool hasRemoteModule();
|
||||
|
||||
protected:
|
||||
static bool soapy_initialized, has_remote;
|
||||
static std::vector<std::string> factories;
|
||||
static std::vector<std::string> modules;
|
||||
static std::vector<std::string> remotes;
|
||||
static std::map< std::string, std::vector<SDRDeviceInfo *> > devs;
|
||||
};
|
@ -5,26 +5,22 @@
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
SDRPostThread::SDRPostThread() : IOThread(),
|
||||
iqDataInQueue(NULL), iqDataOutQueue(NULL), iqVisualQueue(NULL), dcFilter(NULL){
|
||||
SDRPostThread::SDRPostThread() : IOThread() {
|
||||
iqDataInQueue = NULL;
|
||||
iqDataOutQueue = NULL;
|
||||
iqVisualQueue = NULL;
|
||||
|
||||
swapIQ.store(false);
|
||||
numChannels = 0;
|
||||
channelizer = 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
|
||||
}
|
||||
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,165 +43,307 @@ 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<DemodulatorInstance *>::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<DemodulatorThreadIQData> buffers;
|
||||
std::vector<liquid_float_complex> fpData;
|
||||
std::vector<liquid_float_complex> dataOut;
|
||||
iqActiveDemodVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQActiveDemodVisualDataOutput");
|
||||
|
||||
iqDataInQueue->set_max_num_items(0);
|
||||
|
||||
std::vector<liquid_float_complex> dcBuf;
|
||||
|
||||
while (!terminated) {
|
||||
SDRThreadIQData *data_in;
|
||||
|
||||
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])];
|
||||
int dataSize = data_in->data.size();
|
||||
int outSize = data_in->data.size();
|
||||
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < dataSize; i++) {
|
||||
fpData[i] = _lut[*((uint16_t*)&data_in->data[2*i])];
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
busy_demod.lock();
|
||||
|
||||
int activeDemods = 0;
|
||||
bool pushedData = false;
|
||||
if (frequency != data_in->frequency) {
|
||||
frequency = data_in->frequency;
|
||||
doRefresh.store(true);
|
||||
}
|
||||
|
||||
if (demodulators.size() || iqDataOutQueue != NULL) {
|
||||
std::vector<DemodulatorInstance *>::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)) {
|
||||
if (doRefresh.load()) {
|
||||
updateActiveDemodulators();
|
||||
updateChannels();
|
||||
doRefresh.store(false);
|
||||
}
|
||||
|
||||
DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
||||
int activeDemodChannel = -1;
|
||||
|
||||
// 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();
|
||||
demodDataOut->setRefCount(demodChannelActive[i] + doVis + doDemodVis);
|
||||
demodDataOut->frequency = chanCenters[i];
|
||||
demodDataOut->sampleRate = chanBw;
|
||||
|
||||
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
|
||||
// Calculate channel buffer size
|
||||
int chanDataSize = (outSize/numChannels);
|
||||
|
||||
// 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);
|
||||
if (demodDataOut->data.size() != chanDataSize) {
|
||||
if (demodDataOut->data.capacity() < chanDataSize) {
|
||||
demodDataOut->data.reserve(chanDataSize);
|
||||
}
|
||||
demodDataOut->data.resize(chanDataSize);
|
||||
}
|
||||
|
||||
if (!demod->isActive()) {
|
||||
continue;
|
||||
}
|
||||
if (demod->isFollow()) {
|
||||
demod->setFollow(false);
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
|
@ -1,6 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#if USE_RTL_SDR
|
||||
#include "SDRThread.h"
|
||||
#else
|
||||
#include "SoapySDRThread.h"
|
||||
#endif
|
||||
#include <algorithm>
|
||||
|
||||
class SDRPostThread : public IOThread {
|
||||
@ -11,25 +15,43 @@ 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<DemodulatorInstance *> demodulators;
|
||||
iirfilt_crcf dcFilter;
|
||||
std::atomic_bool swapIQ;
|
||||
ReBuffer<DemodulatorThreadIQData> visualDataBuffers;
|
||||
|
||||
|
||||
private:
|
||||
std::vector<liquid_float_complex> _lut;
|
||||
std::vector<liquid_float_complex> _lut_swap;
|
||||
void initPFBChannelizer();
|
||||
void updateActiveDemodulators();
|
||||
void updateChannels();
|
||||
int getChannelAt(long long frequency);
|
||||
|
||||
ReBuffer<DemodulatorThreadIQData> buffers;
|
||||
std::vector<liquid_float_complex> fpData;
|
||||
std::vector<liquid_float_complex> dataOut;
|
||||
std::vector<long long> chanCenters;
|
||||
long long chanBw;
|
||||
|
||||
int nRunDemods;
|
||||
std::vector<DemodulatorInstance *> runDemods;
|
||||
std::vector<int> demodChannel;
|
||||
std::vector<int> demodChannelActive;
|
||||
|
||||
ReBuffer<DemodulatorThreadIQData> visualDataBuffers;
|
||||
atomic_bool doRefresh;
|
||||
atomic_llong visFrequency;
|
||||
atomic_int visBandwidth;
|
||||
int numChannels, sampleRate;
|
||||
long long frequency;
|
||||
firpfbch_crcf channelizer;
|
||||
iirfilt_crcf dcFilter;
|
||||
};
|
||||
|
@ -41,8 +41,12 @@ int SDRThread::enumerate_rtl(std::vector<SDRDeviceInfo *> *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)) {
|
||||
|
423
src/sdr/SoapySDRThread.cpp
Normal file
423
src/sdr/SoapySDRThread.cpp
Normal file
@ -0,0 +1,423 @@
|
||||
#include "SoapySDRThread.h"
|
||||
#include "CubicSDRDefs.h"
|
||||
#include <vector>
|
||||
#include "CubicSDR.h"
|
||||
#include <string>
|
||||
|
||||
|
||||
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<size_t>(), 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<SDRDeviceRange> gains = devInfo->getRxChannel()->getGains();
|
||||
for (std::vector<SDRDeviceRange>::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<std::string,bool>::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<std::string, bool>::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;
|
||||
}
|
112
src/sdr/SoapySDRThread.h
Normal file
112
src/sdr/SoapySDRThread.h
Normal file
@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "ThreadQueue.h"
|
||||
#include "DemodulatorMgr.h"
|
||||
#include "SDRDeviceInfo.h"
|
||||
#include "AppConfig.h"
|
||||
|
||||
#include <SoapySDR/Version.hpp>
|
||||
#include <SoapySDR/Modules.hpp>
|
||||
#include <SoapySDR/Registry.hpp>
|
||||
#include <SoapySDR/Device.hpp>
|
||||
|
||||
|
||||
class SDRThreadIQData: public ReferenceCounter {
|
||||
public:
|
||||
long long frequency;
|
||||
long long sampleRate;
|
||||
bool dcCorrected;
|
||||
int numChannels;
|
||||
std::vector<liquid_float_complex> data;
|
||||
|
||||
SDRThreadIQData() :
|
||||
frequency(0), sampleRate(DEFAULT_SAMPLE_RATE), dcCorrected(true), numChannels(0) {
|
||||
|
||||
}
|
||||
|
||||
SDRThreadIQData(long long bandwidth, long long frequency, std::vector<signed char> *data) :
|
||||
frequency(frequency), sampleRate(bandwidth) {
|
||||
|
||||
}
|
||||
|
||||
~SDRThreadIQData() {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
typedef ThreadQueue<SDRThreadIQData *> 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<SDRThreadIQData> buffers;
|
||||
SDRThreadIQData inpBuffer;
|
||||
std::atomic<DeviceConfig *> deviceConfig;
|
||||
std::atomic<SDRDeviceInfo *> deviceInfo;
|
||||
|
||||
std::mutex setting_busy;
|
||||
std::map<std::string, std::string> settings;
|
||||
std::map<std::string, bool> settingChanged;
|
||||
|
||||
std::atomic<uint32_t> 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<std::string, float> gainValues;
|
||||
std::map<std::string, bool> gainChanged;
|
||||
|
||||
SoapySDR::Kwargs streamArgs;
|
||||
};
|
@ -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<GLPanel *>::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]);
|
||||
|
@ -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,6 +55,7 @@ public:
|
||||
CubicVR::vec2 umin, umax, ucenter;
|
||||
// pixel dimensions
|
||||
CubicVR::vec2 pdim, pvec;
|
||||
GLuint srcBlend, dstBlend;
|
||||
|
||||
std::vector<GLPanel *> children;
|
||||
|
||||
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
305
src/visual/GainCanvas.cpp
Normal file
305
src/visual/GainCanvas.cpp
Normal file
@ -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 <algorithm>
|
||||
|
||||
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<GainInfo *>::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<GainInfo *>::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<GainInfo *>::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<GainInfo *>::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<GainInfo *>::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<SDRDeviceRange> &gains = devInfo->getRxChannel()->getGains();
|
||||
std::vector<SDRDeviceRange>::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<GainInfo *>::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();
|
||||
}
|
||||
|
63
src/visual/GainCanvas.h
Normal file
63
src/visual/GainCanvas.h
Normal file
@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include "wx/glcanvas.h"
|
||||
#include "wx/timer.h"
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#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 *> gainInfo;
|
||||
GLPanel bgPanel;
|
||||
|
||||
float spacing, barWidth, startPos, barHeight, numGains;
|
||||
wxSize clientSize;
|
||||
//
|
||||
wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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<DemodulatorInstance *> &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();
|
||||
}
|
||||
|
||||
|
||||
|
@ -67,6 +67,7 @@ private:
|
||||
long double freqMove;
|
||||
float hoverAlpha;
|
||||
int linesPerSecond;
|
||||
float scaleMove;
|
||||
|
||||
SpectrumVisualDataQueue visualDataQueue;
|
||||
Timer gTimer;
|
||||
|
Loading…
Reference in New Issue
Block a user