mirror of
https://github.com/cjcliffe/CubicSDR.git
synced 2024-11-22 11:49:38 -05:00
Merge pull request #182 from cjcliffe/soapysdr-support
SoapySDR Conversion and Updates
This commit is contained in:
commit
23acca93c7
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
|
build/
|
||||||
cmake_build/
|
cmake_build/
|
||||||
cmake_build_msvc/
|
cmake_build_msvc/
|
||||||
dist/
|
dist/
|
||||||
|
243
CMakeLists.txt
243
CMakeLists.txt
@ -2,8 +2,8 @@ cmake_minimum_required (VERSION 2.8)
|
|||||||
|
|
||||||
SET(CUBICSDR_VERSION_MAJOR "0")
|
SET(CUBICSDR_VERSION_MAJOR "0")
|
||||||
SET(CUBICSDR_VERSION_MINOR "1")
|
SET(CUBICSDR_VERSION_MINOR "1")
|
||||||
SET(CUBICSDR_VERSION_PATCH "6")
|
SET(CUBICSDR_VERSION_PATCH "16")
|
||||||
SET(CUBICSDR_VERSION_REL "beta-issue140")
|
SET(CUBICSDR_VERSION_REL "alpha")
|
||||||
SET(CUBICSDR_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}-${CUBICSDR_VERSION_REL}")
|
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}")
|
SET(CPACK_PACKAGE_VERSION "${CUBICSDR_VERSION_MAJOR}.${CUBICSDR_VERSION_MINOR}.${CUBICSDR_VERSION_PATCH}")
|
||||||
@ -15,9 +15,7 @@ ADD_DEFINITIONS(
|
|||||||
-DCUBICSDR_VERSION="${CUBICSDR_VERSION}"
|
-DCUBICSDR_VERSION="${CUBICSDR_VERSION}"
|
||||||
)
|
)
|
||||||
|
|
||||||
IF (NOT APPLE)
|
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
||||||
ENDIF(NOT APPLE)
|
|
||||||
|
|
||||||
|
|
||||||
macro(configure_files srcDir destDir globStr)
|
macro(configure_files srcDir destDir globStr)
|
||||||
@ -37,7 +35,6 @@ macro(configure_files srcDir destDir globStr)
|
|||||||
endforeach(templateFile)
|
endforeach(templateFile)
|
||||||
endmacro(configure_files)
|
endmacro(configure_files)
|
||||||
|
|
||||||
|
|
||||||
macro(configure_files_recurse srcDir destDir)
|
macro(configure_files_recurse srcDir destDir)
|
||||||
message(STATUS "Configuring directory ${destDir}")
|
message(STATUS "Configuring directory ${destDir}")
|
||||||
make_directory(${destDir})
|
make_directory(${destDir})
|
||||||
@ -67,24 +64,29 @@ else( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
|||||||
SET( EX_PLATFORM_NAME "x86" )
|
SET( EX_PLATFORM_NAME "x86" )
|
||||||
endif( CMAKE_SIZEOF_VOID_P EQUAL 8 )
|
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 ${PROJECT_BINARY_DIR}/${EX_PLATFORM_NAME})
|
||||||
SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${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})
|
SET( CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_BINARY_DIR}/${EX_PLATFORM_NAME})
|
||||||
|
|
||||||
find_package(OpenGL 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 base REQUIRED)
|
find_package(wxWidgets COMPONENTS gl core propgrid base REQUIRED)
|
||||||
set(wxWidgets_CONFIGURATION mswu)
|
set(wxWidgets_CONFIGURATION mswu)
|
||||||
include(${wxWidgets_USE_FILE})
|
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)
|
IF (WIN32)
|
||||||
set(wxWidgets_USE_STATIC ON)
|
set(wxWidgets_USE_STATIC ON)
|
||||||
@ -116,56 +118,31 @@ IF (WIN32)
|
|||||||
ENDIF (MSVC)
|
ENDIF (MSVC)
|
||||||
ENDIF(USE_AUDIO_DS)
|
ENDIF(USE_AUDIO_DS)
|
||||||
|
|
||||||
# ASIO?
|
SET(USE_MINGW_PATCH OFF CACHE BOOL "Add some missing functions when compiling against mingw liquid-dsp.")
|
||||||
#IF(USE_AUDIO_ASIO)
|
IF (USE_MINGW_PATCH)
|
||||||
#ENDIF(USE_AUDIO_ASIO)
|
SET(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} legacy_stdio_definitions.lib libgcc.a")
|
||||||
|
ADD_DEFINITIONS(
|
||||||
# FFTW
|
-DMINGW_PATCH=1
|
||||||
include_directories ( ${PROJECT_SOURCE_DIR}/external/fftw-3.3.4 ${PROJECT_SOURCE_DIR}/external/rtl-sdr-release )
|
)
|
||||||
set(FFTW_LIB libfftw3f-3)
|
SET (GCC_LINKDIR "" CACHE STRING "")
|
||||||
link_directories ( ${PROJECT_SOURCE_DIR}/external/fftw-3.3.4/${EX_PLATFORM} )
|
IF (GCC_LINKDIR)
|
||||||
configure_files(${PROJECT_SOURCE_DIR}/external/fftw-3.3.4/${EX_PLATFORM} ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll")
|
link_directories("${GCC_LINKDIR}")
|
||||||
|
ENDIF()
|
||||||
include_directories ( ${PROJECT_SOURCE_DIR}/external/liquid-dsp/include )
|
ENDIF()
|
||||||
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")
|
|
||||||
|
|
||||||
ENDIF (WIN32)
|
ENDIF (WIN32)
|
||||||
|
|
||||||
IF (UNIX AND NOT APPLE)
|
IF (UNIX AND NOT APPLE)
|
||||||
set(BUILD_DEB OFF CACHE BOOL "Build DEB")
|
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(USE_AUDIO_PULSE ON CACHE BOOL "Use Pulse Audio")
|
||||||
set(LIQUID_LIB liquid)
|
SET(USE_AUDIO_JACK OFF CACHE BOOL "Use Jack Audio")
|
||||||
set(OTHER_LIBRARIES dl)
|
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)
|
IF(USE_AUDIO_PULSE)
|
||||||
SET (OTHER_LIBRARIES ${OTHER_LIBRARIES} pulse-simple pulse)
|
SET (OTHER_LIBRARIES ${OTHER_LIBRARIES} pulse-simple pulse)
|
||||||
@ -203,21 +180,19 @@ ENDIF(USE_AUDIO_OSS)
|
|||||||
ENDIF(UNIX AND NOT APPLE)
|
ENDIF(UNIX AND NOT APPLE)
|
||||||
|
|
||||||
IF (APPLE)
|
IF (APPLE)
|
||||||
set(RTLSDR_INCLUDE "/opt/local/include" CACHE FILEPATH "RTL-SDR Include Path")
|
SET(CMAKE_OSX_DEPLOYMENT_TARGET, "10.10")
|
||||||
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(FFTW_LIB fftw3f)
|
SET(FFTW_LIB fftw3f)
|
||||||
set(LIQUID_LIB liquid)
|
SET(LIQUID_LIB liquid)
|
||||||
|
link_directories(/usr/local/lib)
|
||||||
|
link_directories(/opt/local/lib)
|
||||||
|
|
||||||
ADD_DEFINITIONS(
|
ADD_DEFINITIONS(
|
||||||
-D__MACOSX_CORE__
|
-D__MACOSX_CORE__
|
||||||
)
|
)
|
||||||
|
|
||||||
FIND_LIBRARY(COREAUDIO_LIBRARY CoreAudio)
|
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")
|
set(BUNDLE_APP OFF CACHE BOOL "Bundle Application")
|
||||||
|
|
||||||
ENDIF (APPLE)
|
ENDIF (APPLE)
|
||||||
@ -230,8 +205,9 @@ SET (cubicsdr_sources
|
|||||||
src/FrequencyDialog.cpp
|
src/FrequencyDialog.cpp
|
||||||
src/IOThread.cpp
|
src/IOThread.cpp
|
||||||
src/sdr/SDRDeviceInfo.cpp
|
src/sdr/SDRDeviceInfo.cpp
|
||||||
src/sdr/SDRThread.cpp
|
|
||||||
src/sdr/SDRPostThread.cpp
|
src/sdr/SDRPostThread.cpp
|
||||||
|
src/sdr/SDREnumerator.cpp
|
||||||
|
src/sdr/SoapySDRThread.h
|
||||||
src/demod/DemodulatorPreThread.cpp
|
src/demod/DemodulatorPreThread.cpp
|
||||||
src/demod/DemodulatorThread.cpp
|
src/demod/DemodulatorThread.cpp
|
||||||
src/demod/DemodulatorWorkerThread.cpp
|
src/demod/DemodulatorWorkerThread.cpp
|
||||||
@ -260,6 +236,7 @@ SET (cubicsdr_sources
|
|||||||
src/visual/ScopeContext.cpp
|
src/visual/ScopeContext.cpp
|
||||||
src/visual/SpectrumCanvas.cpp
|
src/visual/SpectrumCanvas.cpp
|
||||||
src/visual/WaterfallCanvas.cpp
|
src/visual/WaterfallCanvas.cpp
|
||||||
|
src/visual/GainCanvas.cpp
|
||||||
src/process/VisualProcessor.cpp
|
src/process/VisualProcessor.cpp
|
||||||
src/process/ScopeVisualProcessor.cpp
|
src/process/ScopeVisualProcessor.cpp
|
||||||
src/process/SpectrumVisualProcessor.cpp
|
src/process/SpectrumVisualProcessor.cpp
|
||||||
@ -267,6 +244,8 @@ SET (cubicsdr_sources
|
|||||||
src/process/FFTDataDistributor.cpp
|
src/process/FFTDataDistributor.cpp
|
||||||
src/process/SpectrumVisualDataThread.cpp
|
src/process/SpectrumVisualDataThread.cpp
|
||||||
src/ui/GLPanel.cpp
|
src/ui/GLPanel.cpp
|
||||||
|
src/forms/SDRDevices/SDRDevices.cpp
|
||||||
|
src/forms/SDRDevices/SDRDevicesForm.cpp
|
||||||
external/rtaudio/RtAudio.cpp
|
external/rtaudio/RtAudio.cpp
|
||||||
external/lodepng/lodepng.cpp
|
external/lodepng/lodepng.cpp
|
||||||
external/tinyxml/tinyxml.cpp
|
external/tinyxml/tinyxml.cpp
|
||||||
@ -284,8 +263,9 @@ SET (cubicsdr_headers
|
|||||||
src/FrequencyDialog.h
|
src/FrequencyDialog.h
|
||||||
src/IOThread.h
|
src/IOThread.h
|
||||||
src/sdr/SDRDeviceInfo.h
|
src/sdr/SDRDeviceInfo.h
|
||||||
src/sdr/SDRThread.h
|
|
||||||
src/sdr/SDRPostThread.h
|
src/sdr/SDRPostThread.h
|
||||||
|
src/sdr/SDREnumerator.h
|
||||||
|
src/sdr/SoapySDRThread.cpp
|
||||||
src/demod/DemodulatorPreThread.h
|
src/demod/DemodulatorPreThread.h
|
||||||
src/demod/DemodulatorThread.h
|
src/demod/DemodulatorThread.h
|
||||||
src/demod/DemodulatorWorkerThread.h
|
src/demod/DemodulatorWorkerThread.h
|
||||||
@ -316,6 +296,7 @@ SET (cubicsdr_headers
|
|||||||
src/visual/ScopeContext.h
|
src/visual/ScopeContext.h
|
||||||
src/visual/SpectrumCanvas.h
|
src/visual/SpectrumCanvas.h
|
||||||
src/visual/WaterfallCanvas.h
|
src/visual/WaterfallCanvas.h
|
||||||
|
src/visual/GainCanvas.h
|
||||||
src/process/VisualProcessor.h
|
src/process/VisualProcessor.h
|
||||||
src/process/ScopeVisualProcessor.h
|
src/process/ScopeVisualProcessor.h
|
||||||
src/process/SpectrumVisualProcessor.h
|
src/process/SpectrumVisualProcessor.h
|
||||||
@ -327,6 +308,8 @@ SET (cubicsdr_headers
|
|||||||
src/ui/UITestCanvas.h
|
src/ui/UITestCanvas.h
|
||||||
src/ui/UITestContext.cpp
|
src/ui/UITestContext.cpp
|
||||||
src/ui/UITestContext.h
|
src/ui/UITestContext.h
|
||||||
|
src/forms/SDRDevices/SDRDevices.h
|
||||||
|
src/forms/SDRDevices/SDRDevicesForm.h
|
||||||
external/rtaudio/RtAudio.h
|
external/rtaudio/RtAudio.h
|
||||||
external/lodepng/lodepng.h
|
external/lodepng/lodepng.h
|
||||||
external/tinyxml/tinyxml.h
|
external/tinyxml/tinyxml.h
|
||||||
@ -349,21 +332,23 @@ SET (cubicsdr_headers
|
|||||||
|
|
||||||
set(REG_EXT "[^/]*([.]cpp|[.]c|[.]h|[.]hpp)$")
|
set(REG_EXT "[^/]*([.]cpp|[.]c|[.]h|[.]hpp)$")
|
||||||
|
|
||||||
SOURCE_GROUP("Base" REGULAR_EXPRESSION src/${REG_EXT})
|
SOURCE_GROUP("Base" REGULAR_EXPRESSION "src/${REG_EXT}")
|
||||||
SOURCE_GROUP("SDR" REGULAR_EXPRESSION src/sdr/${REG_EXT})
|
SOURCE_GROUP("Forms\\SDRDevices" REGULAR_EXPRESSION "src/forms/SDRDevices/${REG_EXT}")
|
||||||
SOURCE_GROUP("Demodulator" REGULAR_EXPRESSION src/demod/${REG_EXT})
|
SOURCE_GROUP("SDR" REGULAR_EXPRESSION "src/sdr/${REG_EXT}")
|
||||||
SOURCE_GROUP("Audio" REGULAR_EXPRESSION src/audio/${REG_EXT})
|
SOURCE_GROUP("Demodulator" REGULAR_EXPRESSION "src/demod/${REG_EXT}")
|
||||||
SOURCE_GROUP("Utility" REGULAR_EXPRESSION src/util/${REG_EXT})
|
SOURCE_GROUP("Audio" REGULAR_EXPRESSION "src/audio/${REG_EXT}")
|
||||||
SOURCE_GROUP("Panel" REGULAR_EXPRESSION src/panel/${REG_EXT})
|
SOURCE_GROUP("Utility" REGULAR_EXPRESSION "src/util/${REG_EXT}")
|
||||||
SOURCE_GROUP("Visual" REGULAR_EXPRESSION src/visual/${REG_EXT})
|
SOURCE_GROUP("Visual" REGULAR_EXPRESSION "src/visual/${REG_EXT}")
|
||||||
SOURCE_GROUP("Process" REGULAR_EXPRESSION src/process/${REG_EXT})
|
SOURCE_GROUP("Panel" REGULAR_EXPRESSION "src/panel/${REG_EXT}")
|
||||||
SOURCE_GROUP("UI" REGULAR_EXPRESSION src/ui/${REG_EXT})
|
SOURCE_GROUP("Process" REGULAR_EXPRESSION "src/process/${REG_EXT}")
|
||||||
SOURCE_GROUP("_ext-RTAudio" REGULAR_EXPRESSION external/rtaudio/.*${REG_EXT})
|
SOURCE_GROUP("UI" REGULAR_EXPRESSION "src/ui/${REG_EXT}")
|
||||||
SOURCE_GROUP("_ext-LodePNG" REGULAR_EXPRESSION external/lodepng/.*${REG_EXT})
|
SOURCE_GROUP("_ext-RTAudio" REGULAR_EXPRESSION "external/rtaudio/.*${REG_EXT}")
|
||||||
SOURCE_GROUP("_ext-TinyXML" REGULAR_EXPRESSION external/tinyxml/.*${REG_EXT})
|
SOURCE_GROUP("_ext-LodePNG" REGULAR_EXPRESSION "external/lodepng/.*${REG_EXT}")
|
||||||
SOURCE_GROUP("_ext-CubicVR2" REGULAR_EXPRESSION external/cubicvr2/.*${REG_EXT})
|
SOURCE_GROUP("_ext-TinyXML" REGULAR_EXPRESSION "external/tinyxml/.*${REG_EXT}")
|
||||||
|
SOURCE_GROUP("_ext-CubicVR2" REGULAR_EXPRESSION "external/cubicvr2/.*${REG_EXT}")
|
||||||
|
|
||||||
include_directories (
|
include_directories (
|
||||||
|
${PROJECT_SOURCE_DIR}/src/forms/SDRDevices
|
||||||
${PROJECT_SOURCE_DIR}/src/sdr
|
${PROJECT_SOURCE_DIR}/src/sdr
|
||||||
${PROJECT_SOURCE_DIR}/src/demod
|
${PROJECT_SOURCE_DIR}/src/demod
|
||||||
${PROJECT_SOURCE_DIR}/src/audio
|
${PROJECT_SOURCE_DIR}/src/audio
|
||||||
@ -409,8 +394,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} "*.fnt")
|
||||||
configure_files(${PROJECT_SOURCE_DIR}/font ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.png")
|
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)
|
configure_files(${PROJECT_SOURCE_DIR}/icon ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} CubicSDR.ico)
|
||||||
|
IF(MSVC)
|
||||||
|
configure_files(${SOAPY_SDR_ROOT}/bin/ ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME} "*.dll")
|
||||||
|
configure_files(${SOAPY_SDR_ROOT}/lib/SoapySDR/modules/ ${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME}/modules/ "*.dll")
|
||||||
|
ENDIF()
|
||||||
add_executable(CubicSDR ${cubicsdr_sources} ${cubicsdr_headers} ${RES_FILES})
|
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)
|
ENDIF (NOT BUNDLE_APP)
|
||||||
|
|
||||||
IF (MSVC)
|
IF (MSVC)
|
||||||
@ -428,7 +417,7 @@ ENDIF(MSVC)
|
|||||||
IF (APPLE)
|
IF (APPLE)
|
||||||
ADD_DEFINITIONS(
|
ADD_DEFINITIONS(
|
||||||
-DHAVE_TYPE_TRAITS=1
|
-DHAVE_TYPE_TRAITS=1
|
||||||
-mmacosx-version-min=10.9
|
-mmacosx-version-min=10.10
|
||||||
)
|
)
|
||||||
ENDIF(APPLE)
|
ENDIF(APPLE)
|
||||||
|
|
||||||
@ -436,6 +425,14 @@ IF (APPLE AND BUNDLE_APP)
|
|||||||
PROJECT(CubicSDR)
|
PROJECT(CubicSDR)
|
||||||
SET(MACOSX_BUNDLE_BUNDLE_NAME CubicSDR)
|
SET(MACOSX_BUNDLE_BUNDLE_NAME CubicSDR)
|
||||||
|
|
||||||
|
set(BUNDLE_SOAPY_MODS OFF CACHE BOOL "Bundle local SoapySDR modules")
|
||||||
|
|
||||||
|
IF (BUNDLE_SOAPY_MODS)
|
||||||
|
ADD_DEFINITIONS(
|
||||||
|
-DBUNDLE_SOAPY_MODS=1
|
||||||
|
)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
ADD_DEFINITIONS(
|
ADD_DEFINITIONS(
|
||||||
-std=c++0x
|
-std=c++0x
|
||||||
-pthread
|
-pthread
|
||||||
@ -477,9 +474,9 @@ IF (APPLE AND BUNDLE_APP)
|
|||||||
${PROJECT_SOURCE_DIR}/icon/CubicSDR.icns
|
${PROJECT_SOURCE_DIR}/icon/CubicSDR.icns
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
MACOSX_PACKAGE_LOCATION Resources
|
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 MACOSX_BUNDLE TRUE)
|
||||||
|
|
||||||
SET_TARGET_PROPERTIES(CubicSDR PROPERTIES
|
SET_TARGET_PROPERTIES(CubicSDR PROPERTIES
|
||||||
@ -489,16 +486,49 @@ IF (APPLE AND BUNDLE_APP)
|
|||||||
# MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}"
|
# MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION}"
|
||||||
MACOSX_BUNDLE_GUI_IDENTIFIER "com.cubicproductions.cubicsdr"
|
MACOSX_BUNDLE_GUI_IDENTIFIER "com.cubicproductions.cubicsdr"
|
||||||
MACOSX_BUNDLE_ICON_FILE CubicSDR.icns
|
MACOSX_BUNDLE_ICON_FILE CubicSDR.icns
|
||||||
)
|
)
|
||||||
|
|
||||||
SET(APPS "${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME}/CubicSDR.app")
|
SET(APPS "${CMAKE_BINARY_DIR}/${EX_PLATFORM_NAME}/CubicSDR.app")
|
||||||
# SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
|
# SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)
|
||||||
# SET(CMAKE_BUILD_WITH_INSTALL_RPATH 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 "
|
INSTALL(CODE "
|
||||||
SET(BU_COPY_FULL_FRAMEWORK_CONTENTS ON)
|
SET(BU_COPY_FULL_FRAMEWORK_CONTENTS ON)
|
||||||
include(BundleUtilities)
|
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(${SOAPY_MOD_NAME} STREQUAL "libsdrPlaySupport.so") # prevent inclusion of libmirsdrapi-rsp.so
|
||||||
|
message(STATUS "Excluding libsdrPlaySupport.so")
|
||||||
|
CONTINUE()
|
||||||
|
ELSE()
|
||||||
|
message(STATUS "Bundling ${SOAPY_MOD_NAME} from ${SOAPY_MOD_FILE}")
|
||||||
|
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}\")
|
VERIFY_APP(\"${APPS}\")
|
||||||
" COMPONENT Runtime)
|
" COMPONENT Runtime)
|
||||||
|
|
||||||
@ -511,6 +541,8 @@ IF (APPLE AND BUNDLE_APP)
|
|||||||
ENDIF (APPLE AND BUNDLE_APP)
|
ENDIF (APPLE AND BUNDLE_APP)
|
||||||
|
|
||||||
IF (WIN32 AND BUILD_INSTALLER)
|
IF (WIN32 AND BUILD_INSTALLER)
|
||||||
|
set(BUNDLE_SOAPY_MODS OFF CACHE BOOL "Bundle local SoapySDR modules")
|
||||||
|
|
||||||
set(CPACK_GENERATOR NSIS)
|
set(CPACK_GENERATOR NSIS)
|
||||||
set(CPACK_PACKAGE_NAME "CubicSDR")
|
set(CPACK_PACKAGE_NAME "CubicSDR")
|
||||||
set(CPACK_PACKAGE_VENDOR "cubicsdr.com")
|
set(CPACK_PACKAGE_VENDOR "cubicsdr.com")
|
||||||
@ -533,6 +565,7 @@ IF (WIN32 AND BUILD_INSTALLER)
|
|||||||
|
|
||||||
set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
set(CPACK_NSIS_EXECUTABLES_DIRECTORY ".")
|
||||||
install(TARGETS CubicSDR RUNTIME DESTINATION .)
|
install(TARGETS CubicSDR RUNTIME DESTINATION .)
|
||||||
|
|
||||||
install(FILES
|
install(FILES
|
||||||
${PROJECT_SOURCE_DIR}/font/vera_sans_mono12.fnt
|
${PROJECT_SOURCE_DIR}/font/vera_sans_mono12.fnt
|
||||||
${PROJECT_SOURCE_DIR}/font/vera_sans_mono16.fnt
|
${PROJECT_SOURCE_DIR}/font/vera_sans_mono16.fnt
|
||||||
@ -548,20 +581,20 @@ IF (WIN32 AND BUILD_INSTALLER)
|
|||||||
${PROJECT_SOURCE_DIR}/font/vera_sans_mono48_0.png
|
${PROJECT_SOURCE_DIR}/font/vera_sans_mono48_0.png
|
||||||
${PROJECT_SOURCE_DIR}/icon/CubicSDR.ico
|
${PROJECT_SOURCE_DIR}/icon/CubicSDR.ico
|
||||||
${PROJECT_SOURCE_DIR}/external/fftw-3.3.4/${EX_PLATFORM}/libfftw3f-3.dll
|
${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/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 .)
|
DESTINATION .)
|
||||||
ELSE (MSVC)
|
|
||||||
install(FILES
|
IF (BUNDLE_SOAPY_MODS)
|
||||||
${PROJECT_SOURCE_DIR}/external/liquid-dsp/gcc/${EX_PLATFORM}/libliquid.dll
|
ADD_DEFINITIONS(
|
||||||
${PROJECT_SOURCE_DIR}/external/rtl-sdr-release/gcc/${EX_PLATFORM}/librtlsdr.dll
|
-DBUNDLE_SOAPY_MODS=1
|
||||||
${PROJECT_SOURCE_DIR}/external/rtl-sdr-release/gcc/${EX_PLATFORM}/libusb-1.0.dll
|
)
|
||||||
DESTINATION .)
|
file(GLOB SOAPY_BINS ${SOAPY_SDR_ROOT}/bin/*.dll)
|
||||||
ENDIF(MSVC)
|
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)
|
IF(MSVC AND EX_PLATFORM EQUAL 32)
|
||||||
install(FILES
|
install(FILES
|
||||||
@ -572,8 +605,8 @@ IF (WIN32 AND BUILD_INSTALLER)
|
|||||||
set(CPACK_PACKAGE_EXECUTABLES CubicSDR "CubicSDR")
|
set(CPACK_PACKAGE_EXECUTABLES CubicSDR "CubicSDR")
|
||||||
|
|
||||||
IF (MSVC)
|
IF (MSVC)
|
||||||
install(PROGRAMS ${CMAKE_CURRENT_SOURCE_DIR}/external/msvc/${EX_PLATFORM_NAME}/vcredist_${EX_PLATFORM_NAME}.exe DESTINATION vc_redist)
|
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\\\\vcredist_${EX_PLATFORM_NAME}.exe\\\" /q:a'")
|
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "ExecWait '\\\"$INSTDIR\\\\vc_redist\\\\vc_redist.${EX_PLATFORM_NAME}.exe\\\" /q:a'")
|
||||||
ENDIF (MSVC)
|
ENDIF (MSVC)
|
||||||
|
|
||||||
|
|
||||||
@ -583,10 +616,10 @@ ENDIF (WIN32 AND BUILD_INSTALLER)
|
|||||||
|
|
||||||
IF (UNIX AND BUILD_DEB)
|
IF (UNIX AND BUILD_DEB)
|
||||||
|
|
||||||
set(CPACK_GENERATOR DEB)
|
set(CPACK_GENERATOR DEB)
|
||||||
set(CPACK_PACKAGE_NAME "CubicSDR")
|
set(CPACK_PACKAGE_NAME "CubicSDR")
|
||||||
SET(CPACK_DEBIAN_PACKAGE_DEPENDS " librtlsdr0, libfftw3-single3, libwxgtk3.0-0, libpulse0")
|
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_MAINTAINER "Charles J. Cliffe <cj@cubicproductions.com>")
|
||||||
SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "CubicSDR Software Defined Radio application v${CUBICSDR_VERSION}")
|
SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "CubicSDR Software Defined Radio application v${CUBICSDR_VERSION}")
|
||||||
SET(CPACK_DEBIAN_PACKAGE_SECTION "comm")
|
SET(CPACK_DEBIAN_PACKAGE_SECTION "comm")
|
||||||
SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
|
SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
|
||||||
|
48
README.md
48
README.md
@ -7,12 +7,12 @@ Please see the [CubicSDR GitHub Wiki](https://github.com/cjcliffe/CubicSDR/wiki)
|
|||||||
|
|
||||||
Utilizes:
|
Utilizes:
|
||||||
--------
|
--------
|
||||||
- liquid-dsp (http://liquidsdr.org/ https://github.com/jgaeddert/liquid-dsp)
|
- liquid-dsp (http://liquidsdr.org/ -- https://github.com/jgaeddert/liquid-dsp)
|
||||||
- FFTW (http://www.fftw.org/ https://github.com/FFTW/fftw3)
|
- SoapySDR (http://www.pothosware.com/ -- https://github.com/pothosware/SoapySDR)
|
||||||
- RtAudio (http://www.music.mcgill.ca/~gary/rtaudio/ http://github.com/thestk/rtaudio/)
|
- FFTW (http://www.fftw.org/ -- https://github.com/FFTW/fftw3)
|
||||||
- Osmocom RTLSDR (http://sdr.osmocom.org/trac/wiki/rtl-sdr)
|
- RtAudio (http://www.music.mcgill.ca/~gary/rtaudio/ -- http://github.com/thestk/rtaudio/)
|
||||||
- LodePNG (http://lodev.org/lodepng/)
|
- 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)
|
- Bitstream Vera font (http://en.wikipedia.org/wiki/Bitstream_Vera)
|
||||||
- OpenGL (https://www.opengl.org/)
|
- OpenGL (https://www.opengl.org/)
|
||||||
- wxWidgets (https://www.wxwidgets.org/)
|
- wxWidgets (https://www.wxwidgets.org/)
|
||||||
@ -22,9 +22,20 @@ Features and Status:
|
|||||||
--------------------
|
--------------------
|
||||||
- Simple UI
|
- Simple UI
|
||||||
- Devices
|
- Devices
|
||||||
- [x] RTL-SDR
|
- [x] SoapySDR Device support (known working checked)
|
||||||
- [ ] rtl_tcp client
|
- [x] SoapySDRPlay for SDRPlay (Maintained by C.J.)
|
||||||
- [ ] gr-osmosdr
|
- [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
|
- Basic Features
|
||||||
- [x] Device Selection
|
- [x] Device Selection
|
||||||
- [x] Bandwidth
|
- [x] Bandwidth
|
||||||
@ -32,12 +43,15 @@ Features and Status:
|
|||||||
- [x] Load/Save session
|
- [x] Load/Save session
|
||||||
- [x] Audio sample rate
|
- [x] Audio sample rate
|
||||||
- [x] Device PPM
|
- [x] Device PPM
|
||||||
|
- [x] Waterfall speed
|
||||||
|
- [x] Spectrum average speed
|
||||||
|
- [x] Gain Controls
|
||||||
|
- [ ] Bookmarks
|
||||||
|
- [ ] History
|
||||||
- [ ] Default preferences
|
- [ ] Default preferences
|
||||||
- [ ] Audio defaults
|
- [ ] Audio defaults
|
||||||
- [x] Device defaults
|
- [x] Device defaults
|
||||||
- [ ] Bookmarks
|
- [ ] Run any device as rtl_tcp server and visualize control
|
||||||
- [ ] History
|
|
||||||
- [ ] Run as rtl_tcp server and visualize control
|
|
||||||
- Neat Visuals
|
- Neat Visuals
|
||||||
- [ ] 2D visuals
|
- [ ] 2D visuals
|
||||||
- [x] Y Scope
|
- [x] Y Scope
|
||||||
@ -61,7 +75,7 @@ Features and Status:
|
|||||||
- [x] USB
|
- [x] USB
|
||||||
- [x] DSB
|
- [x] DSB
|
||||||
- [x] I/Q
|
- [x] I/Q
|
||||||
- [ ] Controls
|
- [x] Controls
|
||||||
- [x] Display Frequency and allow manual adjustments
|
- [x] Display Frequency and allow manual adjustments
|
||||||
- [x] Allow selection of demodulation type
|
- [x] Allow selection of demodulation type
|
||||||
- [x] Display separate zoomed-in view of current waterfall and spectrum, allow adjustments
|
- [x] Display separate zoomed-in view of current waterfall and spectrum, allow adjustments
|
||||||
@ -70,8 +84,6 @@ Features and Status:
|
|||||||
- [x] Volume control
|
- [x] Volume control
|
||||||
- [x] Direct frequency input
|
- [x] Direct frequency input
|
||||||
- [x] Mute
|
- [x] Mute
|
||||||
- [x] Waterfall speed
|
|
||||||
- [ ] RTL-SDR Gain
|
|
||||||
- Basic Input Controls
|
- Basic Input Controls
|
||||||
- [x] Drag spectrum to change center frequency
|
- [x] Drag spectrum to change center frequency
|
||||||
- [x] Hold shift and click on waterfall to create a new demodulator
|
- [x] Hold shift and click on waterfall to create a new demodulator
|
||||||
@ -127,7 +139,7 @@ Features and Status:
|
|||||||
- [ ] Update visuals to OpenGL 3.x / OpenGL ES
|
- [ ] Update visuals to OpenGL 3.x / OpenGL ES
|
||||||
- [x] Resolve constant refresh on visuals that don't change often
|
- [x] Resolve constant refresh on visuals that don't change often
|
||||||
- [ ] Resolve all driver/platform vertical sync issues
|
- [ ] 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:
|
Advanced Goals and ideas:
|
||||||
@ -158,9 +170,9 @@ Advanced Goals and ideas:
|
|||||||
- Accessibility / Control
|
- Accessibility / Control
|
||||||
- USB/MIDI control surfaces
|
- USB/MIDI control surfaces
|
||||||
- Joystick / gamepad input
|
- Joystick / gamepad input
|
||||||
- Vibration / force-feedback
|
- Vibration / force-feeback
|
||||||
- Investigate compilation via emscripten using rtl_tcp for input
|
- Investigate compilation via emscripten using SoapyRemote for input
|
||||||
- Create web server+rtl_tcp bundle for embedded devices
|
- Create web server+SoapyRemote bundle for embedded devices
|
||||||
- Use emscripten compiled CubicSDR via embedded web server
|
- Use emscripten compiled CubicSDR via embedded web server
|
||||||
|
|
||||||
Target Platforms:
|
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 LIQUID3.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"
|
#include "CubicSDR.h"
|
||||||
|
|
||||||
DeviceConfig::DeviceConfig() : deviceId("") {
|
DeviceConfig::DeviceConfig() : deviceId("") {
|
||||||
iqSwap.store(0);
|
|
||||||
ppm.store(0);
|
ppm.store(0);
|
||||||
directSampling.store(false);
|
|
||||||
offset.store(0);
|
offset.store(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -20,14 +18,6 @@ int DeviceConfig::getPPM() {
|
|||||||
return ppm.load();
|
return ppm.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceConfig::setDirectSampling(int mode) {
|
|
||||||
directSampling.store(mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
int DeviceConfig::getDirectSampling() {
|
|
||||||
return directSampling.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeviceConfig::setOffset(long long offset) {
|
void DeviceConfig::setOffset(long long offset) {
|
||||||
this->offset.store(offset);
|
this->offset.store(offset);
|
||||||
}
|
}
|
||||||
@ -36,14 +26,6 @@ long long DeviceConfig::getOffset() {
|
|||||||
return offset.load();
|
return offset.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeviceConfig::setIQSwap(bool iqSwap) {
|
|
||||||
this->iqSwap.store(iqSwap);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DeviceConfig::getIQSwap() {
|
|
||||||
return iqSwap.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeviceConfig::setDeviceId(std::string deviceId) {
|
void DeviceConfig::setDeviceId(std::string deviceId) {
|
||||||
busy_lock.lock();
|
busy_lock.lock();
|
||||||
this->deviceId = deviceId;
|
this->deviceId = deviceId;
|
||||||
@ -64,8 +46,6 @@ void DeviceConfig::save(DataNode *node) {
|
|||||||
busy_lock.lock();
|
busy_lock.lock();
|
||||||
*node->newChild("id") = deviceId;
|
*node->newChild("id") = deviceId;
|
||||||
*node->newChild("ppm") = (int)ppm;
|
*node->newChild("ppm") = (int)ppm;
|
||||||
*node->newChild("iq_swap") = iqSwap;
|
|
||||||
*node->newChild("direct_sampling") = directSampling;
|
|
||||||
*node->newChild("offset") = offset;
|
*node->newChild("offset") = offset;
|
||||||
busy_lock.unlock();
|
busy_lock.unlock();
|
||||||
}
|
}
|
||||||
@ -79,32 +59,6 @@ void DeviceConfig::load(DataNode *node) {
|
|||||||
setPPM(ppmValue);
|
setPPM(ppmValue);
|
||||||
std::cout << "Loaded PPM for device '" << deviceId << "' at " << ppmValue << "ppm" << std::endl;
|
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")) {
|
if (node->hasAnother("offset")) {
|
||||||
DataNode *offset_node = node->getNext("offset");
|
DataNode *offset_node = node->getNext("offset");
|
||||||
long long offsetValue = 0;
|
long long offsetValue = 0;
|
||||||
|
@ -17,15 +17,9 @@ public:
|
|||||||
void setPPM(int ppm);
|
void setPPM(int ppm);
|
||||||
int getPPM();
|
int getPPM();
|
||||||
|
|
||||||
void setDirectSampling(int mode);
|
|
||||||
int getDirectSampling();
|
|
||||||
|
|
||||||
void setOffset(long long offset);
|
void setOffset(long long offset);
|
||||||
long long getOffset();
|
long long getOffset();
|
||||||
|
|
||||||
void setIQSwap(bool iqSwap);
|
|
||||||
bool getIQSwap();
|
|
||||||
|
|
||||||
void setDeviceId(std::string deviceId);
|
void setDeviceId(std::string deviceId);
|
||||||
std::string getDeviceId();
|
std::string getDeviceId();
|
||||||
|
|
||||||
@ -36,8 +30,7 @@ private:
|
|||||||
std::string deviceId;
|
std::string deviceId;
|
||||||
std::mutex busy_lock;
|
std::mutex busy_lock;
|
||||||
|
|
||||||
std::atomic_int ppm, directSampling;
|
std::atomic_int ppm;
|
||||||
std::atomic_bool iqSwap;
|
|
||||||
std::atomic_llong offset;
|
std::atomic_llong offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
524
src/AppFrame.cpp
524
src/AppFrame.cpp
@ -14,12 +14,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "SDRThread.h"
|
|
||||||
#include "DemodulatorMgr.h"
|
|
||||||
#include "AudioThread.h"
|
#include "AudioThread.h"
|
||||||
#include "CubicSDR.h"
|
#include "CubicSDR.h"
|
||||||
#include "DataTree.h"
|
#include "DataTree.h"
|
||||||
#include "ColorTheme.h"
|
#include "ColorTheme.h"
|
||||||
|
#include "DemodulatorMgr.h"
|
||||||
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@ -35,8 +34,11 @@ EVT_CLOSE(AppFrame::OnClose)
|
|||||||
EVT_MENU(wxID_ANY, AppFrame::OnMenu)
|
EVT_MENU(wxID_ANY, AppFrame::OnMenu)
|
||||||
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, AppFrame::OnThread)
|
EVT_COMMAND(wxID_ANY, wxEVT_THREAD, AppFrame::OnThread)
|
||||||
EVT_IDLE(AppFrame::OnIdle)
|
EVT_IDLE(AppFrame::OnIdle)
|
||||||
|
EVT_SPLITTER_DCLICK(wxID_ANY, AppFrame::OnDoubleClickSash)
|
||||||
|
EVT_SPLITTER_UNSPLIT(wxID_ANY, AppFrame::OnUnSplit)
|
||||||
wxEND_EVENT_TABLE()
|
wxEND_EVENT_TABLE()
|
||||||
|
|
||||||
|
|
||||||
AppFrame::AppFrame() :
|
AppFrame::AppFrame() :
|
||||||
wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) {
|
wxFrame(NULL, wxID_ANY, CUBICSDR_TITLE), activeDemodulator(NULL) {
|
||||||
|
|
||||||
@ -46,12 +48,25 @@ AppFrame::AppFrame() :
|
|||||||
|
|
||||||
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer *vbox = new wxBoxSizer(wxVERTICAL);
|
||||||
wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer *demodVisuals = new wxBoxSizer(wxVERTICAL);
|
||||||
wxBoxSizer *demodTray = new wxBoxSizer(wxHORIZONTAL);
|
demodTray = new wxBoxSizer(wxHORIZONTAL);
|
||||||
wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer *demodScopeTray = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
int attribList[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
|
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_FM, "FM");
|
||||||
demodModeSelector->addChoice(DEMOD_TYPE_AM, "AM");
|
demodModeSelector->addChoice(DEMOD_TYPE_AM, "AM");
|
||||||
demodModeSelector->addChoice(DEMOD_TYPE_LSB, "LSB");
|
demodModeSelector->addChoice(DEMOD_TYPE_LSB, "LSB");
|
||||||
@ -63,14 +78,14 @@ AppFrame::AppFrame() :
|
|||||||
demodTray->Add(demodModeSelector, 2, wxEXPAND | wxALL, 0);
|
demodTray->Add(demodModeSelector, 2, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
wxGetApp().getDemodSpectrumProcessor()->setup(1024);
|
wxGetApp().getDemodSpectrumProcessor()->setup(1024);
|
||||||
demodSpectrumCanvas = new SpectrumCanvas(this, attribList);
|
demodSpectrumCanvas = new SpectrumCanvas(demodPanel, attribList);
|
||||||
demodSpectrumCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
|
demodSpectrumCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
|
||||||
demodVisuals->Add(demodSpectrumCanvas, 3, wxEXPAND | wxALL, 0);
|
demodVisuals->Add(demodSpectrumCanvas, 3, wxEXPAND | wxALL, 0);
|
||||||
wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodSpectrumCanvas->getVisualDataQueue());
|
wxGetApp().getDemodSpectrumProcessor()->attachOutput(demodSpectrumCanvas->getVisualDataQueue());
|
||||||
|
|
||||||
demodVisuals->AddSpacer(1);
|
demodVisuals->AddSpacer(1);
|
||||||
|
|
||||||
demodWaterfallCanvas = new WaterfallCanvas(this, attribList);
|
demodWaterfallCanvas = new WaterfallCanvas(demodPanel, attribList);
|
||||||
demodWaterfallCanvas->setup(1024, 128);
|
demodWaterfallCanvas->setup(1024, 128);
|
||||||
demodWaterfallCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
|
demodWaterfallCanvas->setView(wxGetApp().getConfig()->getCenterFreq(), 300000);
|
||||||
demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas);
|
demodWaterfallCanvas->attachSpectrumCanvas(demodSpectrumCanvas);
|
||||||
@ -83,14 +98,16 @@ AppFrame::AppFrame() :
|
|||||||
|
|
||||||
demodTray->AddSpacer(1);
|
demodTray->AddSpacer(1);
|
||||||
|
|
||||||
demodSignalMeter = new MeterCanvas(this, attribList);
|
demodSignalMeter = new MeterCanvas(demodPanel, attribList);
|
||||||
demodSignalMeter->setMax(0.5);
|
demodSignalMeter->setMax(0.5);
|
||||||
demodSignalMeter->setHelpTip("Current Signal Level. Click / Drag to set Squelch level.");
|
demodSignalMeter->setHelpTip("Current Signal Level. Click / Drag to set Squelch level.");
|
||||||
|
demodSignalMeter->SetMinSize(wxSize(12,24));
|
||||||
demodTray->Add(demodSignalMeter, 1, wxEXPAND | wxALL, 0);
|
demodTray->Add(demodSignalMeter, 1, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
|
|
||||||
demodTray->AddSpacer(1);
|
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.");
|
scopeCanvas->setHelpTip("Audio Visuals, drag left/right to toggle Scope or Spectrum.");
|
||||||
demodScopeTray->Add(scopeCanvas, 8, wxEXPAND | wxALL, 0);
|
demodScopeTray->Add(scopeCanvas, 8, wxEXPAND | wxALL, 0);
|
||||||
wxGetApp().getScopeProcessor()->setup(2048);
|
wxGetApp().getScopeProcessor()->setup(2048);
|
||||||
@ -98,8 +115,9 @@ AppFrame::AppFrame() :
|
|||||||
|
|
||||||
demodScopeTray->AddSpacer(1);
|
demodScopeTray->AddSpacer(1);
|
||||||
|
|
||||||
demodTuner = new TuningCanvas(this, attribList);
|
demodTuner = new TuningCanvas(demodPanel, attribList);
|
||||||
demodTuner->setHelpTip("Testing tuner");
|
demodTuner->setHelpTip("Testing tuner");
|
||||||
|
demodTuner->SetMinClientSize(wxSize(200,24));
|
||||||
demodScopeTray->Add(demodTuner, 1, wxEXPAND | wxALL, 0);
|
demodScopeTray->Add(demodTuner, 1, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
demodTray->Add(demodScopeTray, 30, wxEXPAND | wxALL, 0);
|
demodTray->Add(demodScopeTray, 30, wxEXPAND | wxALL, 0);
|
||||||
@ -108,73 +126,99 @@ AppFrame::AppFrame() :
|
|||||||
|
|
||||||
wxBoxSizer *demodGainTray = new wxBoxSizer(wxVERTICAL);
|
wxBoxSizer *demodGainTray = new wxBoxSizer(wxVERTICAL);
|
||||||
|
|
||||||
demodGainMeter = new MeterCanvas(this, attribList);
|
demodGainMeter = new MeterCanvas(demodPanel, attribList);
|
||||||
demodGainMeter->setMax(2.0);
|
demodGainMeter->setMax(2.0);
|
||||||
demodGainMeter->setHelpTip("Current Demodulator Gain Level. Click / Drag to set Gain level.");
|
demodGainMeter->setHelpTip("Current Demodulator Gain Level. Click / Drag to set Gain level.");
|
||||||
demodGainMeter->setShowUserInput(false);
|
demodGainMeter->setShowUserInput(false);
|
||||||
|
demodGainMeter->SetMinSize(wxSize(12,24));
|
||||||
demodGainTray->Add(demodGainMeter, 8, wxEXPAND | wxALL, 0);
|
demodGainTray->Add(demodGainMeter, 8, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
|
|
||||||
demodGainTray->AddSpacer(1);
|
demodGainTray->AddSpacer(1);
|
||||||
|
|
||||||
demodMuteButton = new ModeSelectorCanvas(this, attribList);
|
demodMuteButton = new ModeSelectorCanvas(demodPanel, attribList);
|
||||||
demodMuteButton->addChoice(1, "M");
|
demodMuteButton->addChoice(1, "M");
|
||||||
demodMuteButton->setPadding(-1,-1);
|
demodMuteButton->setPadding(-1,-1);
|
||||||
demodMuteButton->setHighlightColor(RGBA4f(0.8,0.2,0.2));
|
demodMuteButton->setHighlightColor(RGBA4f(0.8,0.2,0.2));
|
||||||
demodMuteButton->setHelpTip("Demodulator Mute Toggle");
|
demodMuteButton->setHelpTip("Demodulator Mute Toggle");
|
||||||
demodMuteButton->setToggleMode(true);
|
demodMuteButton->setToggleMode(true);
|
||||||
demodMuteButton->setSelection(-1);
|
demodMuteButton->setSelection(-1);
|
||||||
|
demodMuteButton->SetMinSize(wxSize(12,24));
|
||||||
|
|
||||||
demodGainTray->Add(demodMuteButton, 1, wxEXPAND | wxALL, 0);
|
demodGainTray->Add(demodMuteButton, 1, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
demodTray->Add(demodGainTray, 1, wxEXPAND | wxALL, 0);
|
demodTray->Add(demodGainTray, 1, wxEXPAND | wxALL, 0);
|
||||||
|
|
||||||
vbox->Add(demodTray, 12, wxEXPAND | wxALL, 0);
|
demodPanel->SetSizer(demodTray);
|
||||||
vbox->AddSpacer(1);
|
|
||||||
|
|
||||||
|
// 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);
|
wxBoxSizer *spectrumSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
wxGetApp().getSpectrumProcessor()->setup(2048);
|
wxGetApp().getSpectrumProcessor()->setup(2048);
|
||||||
spectrumCanvas = new SpectrumCanvas(this, attribList);
|
spectrumCanvas = new SpectrumCanvas(spectrumPanel, attribList);
|
||||||
spectrumCanvas->setShowDb(true);
|
spectrumCanvas->setShowDb(true);
|
||||||
|
spectrumCanvas->setScaleFactorEnabled(true);
|
||||||
wxGetApp().getSpectrumProcessor()->attachOutput(spectrumCanvas->getVisualDataQueue());
|
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->setHelpTip("Spectrum averaging speed, click or drag to adjust.");
|
||||||
spectrumAvgMeter->setMax(1.0);
|
spectrumAvgMeter->setMax(1.0);
|
||||||
spectrumAvgMeter->setLevel(0.65);
|
spectrumAvgMeter->setLevel(0.65);
|
||||||
spectrumAvgMeter->setShowUserInput(false);
|
spectrumAvgMeter->setShowUserInput(false);
|
||||||
|
spectrumAvgMeter->SetMinSize(wxSize(12,24));
|
||||||
|
|
||||||
|
|
||||||
spectrumSizer->Add(spectrumCanvas, 63, wxEXPAND | wxALL, 0);
|
spectrumSizer->Add(spectrumCanvas, 63, wxEXPAND | wxALL, 0);
|
||||||
spectrumSizer->AddSpacer(1);
|
spectrumSizer->AddSpacer(1);
|
||||||
spectrumSizer->Add(spectrumAvgMeter, 1, wxEXPAND | wxALL, 0);
|
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);
|
wxBoxSizer *wfSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||||
|
|
||||||
waterfallCanvas = new WaterfallCanvas(this, attribList);
|
waterfallCanvas = new WaterfallCanvas(waterfallPanel, attribList);
|
||||||
waterfallCanvas->setup(2048, 512);
|
waterfallCanvas->setup(2048, 512);
|
||||||
|
|
||||||
waterfallDataThread = new FFTVisualDataThread();
|
waterfallDataThread = new FFTVisualDataThread();
|
||||||
|
|
||||||
waterfallDataThread->setInputQueue("IQDataInput", wxGetApp().getWaterfallVisualQueue());
|
waterfallDataThread->setInputQueue("IQDataInput", wxGetApp().getWaterfallVisualQueue());
|
||||||
waterfallDataThread->setOutputQueue("FFTDataOutput", waterfallCanvas->getVisualDataQueue());
|
waterfallDataThread->setOutputQueue("FFTDataOutput", waterfallCanvas->getVisualDataQueue());
|
||||||
|
waterfallDataThread->getProcessor()->setHideDC(true);
|
||||||
|
|
||||||
t_FFTData = new std::thread(&FFTVisualDataThread::threadMain, waterfallDataThread);
|
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->setHelpTip("Waterfall speed, click or drag to adjust (max 1024 lines per second)");
|
||||||
waterfallSpeedMeter->setMax(sqrt(1024));
|
waterfallSpeedMeter->setMax(sqrt(1024));
|
||||||
waterfallSpeedMeter->setLevel(sqrt(DEFAULT_WATERFALL_LPS));
|
waterfallSpeedMeter->setLevel(sqrt(DEFAULT_WATERFALL_LPS));
|
||||||
waterfallSpeedMeter->setShowUserInput(false);
|
waterfallSpeedMeter->setShowUserInput(false);
|
||||||
|
waterfallSpeedMeter->SetMinSize(wxSize(12,24));
|
||||||
|
|
||||||
wfSizer->Add(waterfallCanvas, 63, wxEXPAND | wxALL, 0);
|
wfSizer->Add(waterfallCanvas, 63, wxEXPAND | wxALL, 0);
|
||||||
wfSizer->AddSpacer(1);
|
wfSizer->AddSpacer(1);
|
||||||
wfSizer->Add(waterfallSpeedMeter, 1, wxEXPAND | wxALL, 0);
|
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..
|
// TODO: refactor these..
|
||||||
waterfallCanvas->attachSpectrumCanvas(spectrumCanvas);
|
waterfallCanvas->attachSpectrumCanvas(spectrumCanvas);
|
||||||
spectrumCanvas->attachWaterfallCanvas(waterfallCanvas);
|
spectrumCanvas->attachWaterfallCanvas(waterfallCanvas);
|
||||||
@ -193,9 +237,11 @@ AppFrame::AppFrame() :
|
|||||||
// SetIcon(wxICON(sample));
|
// SetIcon(wxICON(sample));
|
||||||
|
|
||||||
// Make a menubar
|
// Make a menubar
|
||||||
wxMenuBar *menuBar = new wxMenuBar;
|
menuBar = new wxMenuBar;
|
||||||
wxMenu *menu = new wxMenu;
|
wxMenu *menu = new wxMenu;
|
||||||
|
|
||||||
|
menu->Append(wxID_SDR_DEVICES, "SDR Devices");
|
||||||
|
menu->AppendSeparator();
|
||||||
menu->Append(wxID_OPEN, "&Open Session");
|
menu->Append(wxID_OPEN, "&Open Session");
|
||||||
menu->Append(wxID_SAVE, "&Save Session");
|
menu->Append(wxID_SAVE, "&Save Session");
|
||||||
menu->Append(wxID_SAVEAS, "Save Session &As..");
|
menu->Append(wxID_SAVEAS, "Save Session &As..");
|
||||||
@ -209,21 +255,9 @@ AppFrame::AppFrame() :
|
|||||||
|
|
||||||
menuBar->Append(menu, wxT("&File"));
|
menuBar->Append(menu, wxT("&File"));
|
||||||
|
|
||||||
menu = new wxMenu;
|
settingsMenu = new wxMenu;
|
||||||
|
|
||||||
menu->Append(wxID_SET_FREQ_OFFSET, "Frequency Offset");
|
menuBar->Append(settingsMenu, wxT("&Settings"));
|
||||||
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"));
|
|
||||||
|
|
||||||
menu = new wxMenu;
|
menu = new wxMenu;
|
||||||
|
|
||||||
@ -269,54 +303,6 @@ AppFrame::AppFrame() :
|
|||||||
menuBar->Append(menu, wxT("&Color Scheme"));
|
menuBar->Append(menu, wxT("&Color Scheme"));
|
||||||
|
|
||||||
menu = new wxMenu;
|
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
|
#define NUM_RATES_DEFAULT 4
|
||||||
int desired_rates[NUM_RATES_DEFAULT] = { 48000, 44100, 96000, 192000 };
|
int desired_rates[NUM_RATES_DEFAULT] = { 48000, 44100, 96000, 192000 };
|
||||||
@ -344,7 +330,6 @@ AppFrame::AppFrame() :
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (mdevices_i = outputDevices.begin(); mdevices_i != outputDevices.end(); mdevices_i++) {
|
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;
|
int menu_id = wxID_AUDIO_BANDWIDTH_BASE + wxID_AUDIO_DEVICE_MULTIPLIER * mdevices_i->first;
|
||||||
wxMenu *subMenu = new wxMenu;
|
wxMenu *subMenu = new wxMenu;
|
||||||
menu->AppendSubMenu(subMenu, mdevices_i->second.name, wxT("Description?"));
|
menu->AppendSubMenu(subMenu, mdevices_i->second.name, wxT("Description?"));
|
||||||
@ -365,6 +350,10 @@ AppFrame::AppFrame() :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sampleRateMenu = new wxMenu;
|
||||||
|
|
||||||
|
menuBar->Append(sampleRateMenu, wxT("&Input Bandwidth"));
|
||||||
|
|
||||||
menuBar->Append(menu, wxT("Audio &Bandwidth"));
|
menuBar->Append(menu, wxT("Audio &Bandwidth"));
|
||||||
|
|
||||||
SetMenuBar(menuBar);
|
SetMenuBar(menuBar);
|
||||||
@ -414,7 +403,10 @@ AppFrame::AppFrame() :
|
|||||||
|
|
||||||
wxAcceleratorTable accel(3, entries);
|
wxAcceleratorTable accel(3, entries);
|
||||||
SetAcceleratorTable(accel);
|
SetAcceleratorTable(accel);
|
||||||
|
deviceChanged.store(false);
|
||||||
|
devInfo = NULL;
|
||||||
|
wxGetApp().deviceSelector();
|
||||||
|
|
||||||
// static const int attribs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
|
// static const int attribs[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, 0 };
|
||||||
// wxLogStatus("Double-buffered display %s supported", wxGLCanvas::IsDisplaySupported(attribs) ? "is" : "not");
|
// wxLogStatus("Double-buffered display %s supported", wxGLCanvas::IsDisplaySupported(attribs) ? "is" : "not");
|
||||||
// ShowFullScreen(true);
|
// ShowFullScreen(true);
|
||||||
@ -425,19 +417,116 @@ AppFrame::~AppFrame() {
|
|||||||
t_FFTData->join();
|
t_FFTData->join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppFrame::initDeviceParams(SDRDeviceInfo *devInfo) {
|
||||||
|
this->devInfo = devInfo;
|
||||||
|
deviceChanged.store(true);
|
||||||
|
}
|
||||||
|
|
||||||
void AppFrame::initDeviceParams(std::string deviceId) {
|
void AppFrame::updateDeviceParams() {
|
||||||
DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(deviceId);
|
|
||||||
|
|
||||||
int dsMode = devConfig->getDirectSampling();
|
if (!deviceChanged.load()) {
|
||||||
|
return;
|
||||||
if (dsMode > 0 && dsMode <= 2) {
|
|
||||||
directSamplingMenuItems[devConfig->getDirectSampling()]->Check();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -455,19 +544,41 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
|
|||||||
wxGetApp().saveConfig();
|
wxGetApp().saveConfig();
|
||||||
}
|
}
|
||||||
} else if (event.GetId() == wxID_SET_DS_OFF) {
|
} else if (event.GetId() == wxID_SET_DS_OFF) {
|
||||||
wxGetApp().setDirectSampling(0);
|
// wxGetApp().setDirectSampling(0);
|
||||||
wxGetApp().saveConfig();
|
// wxGetApp().saveConfig();
|
||||||
} else if (event.GetId() == wxID_SET_DS_I) {
|
} else if (event.GetId() == wxID_SET_DS_I) {
|
||||||
wxGetApp().setDirectSampling(1);
|
// wxGetApp().setDirectSampling(1);
|
||||||
wxGetApp().saveConfig();
|
// wxGetApp().saveConfig();
|
||||||
} else if (event.GetId() == wxID_SET_DS_Q) {
|
} else if (event.GetId() == wxID_SET_DS_Q) {
|
||||||
wxGetApp().setDirectSampling(2);
|
// wxGetApp().setDirectSampling(2);
|
||||||
wxGetApp().saveConfig();
|
// wxGetApp().saveConfig();
|
||||||
} else if (event.GetId() == wxID_SET_SWAP_IQ) {
|
} else if (event.GetId() == wxID_SET_SWAP_IQ) {
|
||||||
bool swap_state = !wxGetApp().getSwapIQ();
|
// bool swap_state = !wxGetApp().getSwapIQ();
|
||||||
wxGetApp().setSwapIQ(swap_state);
|
// wxGetApp().setSwapIQ(swap_state);
|
||||||
wxGetApp().saveConfig();
|
// wxGetApp().saveConfig();
|
||||||
iqSwapMenuItem->Check(swap_state);
|
// 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) {
|
} 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)",
|
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);
|
"Frequency Correction", wxGetApp().getPPM(), -1000, 1000, this);
|
||||||
@ -536,73 +647,99 @@ void AppFrame::OnMenu(wxCommandEvent& event) {
|
|||||||
ThemeMgr::mgr.setTheme(COLOR_THEME_RADAR);
|
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) {
|
if (event.GetId() >= wxID_THEME_DEFAULT && event.GetId() <= wxID_THEME_RADAR) {
|
||||||
demodTuner->Refresh();
|
demodTuner->Refresh();
|
||||||
demodModeSelector->Refresh();
|
demodModeSelector->Refresh();
|
||||||
|
waterfallSpeedMeter->Refresh();
|
||||||
|
spectrumAvgMeter->Refresh();
|
||||||
|
gainCanvas->setThemeColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (event.GetId()) {
|
switch (event.GetId()) {
|
||||||
case wxID_BANDWIDTH_250K:
|
case wxID_BANDWIDTH_MANUAL:
|
||||||
wxGetApp().setSampleRate(250000);
|
int rateHigh, rateLow;
|
||||||
break;
|
|
||||||
case wxID_BANDWIDTH_1000M:
|
SDRDeviceInfo *dev = wxGetApp().getDevice();
|
||||||
wxGetApp().setSampleRate(1000000);
|
if (dev == NULL) {
|
||||||
break;
|
break;
|
||||||
case wxID_BANDWIDTH_1024M:
|
}
|
||||||
wxGetApp().setSampleRate(1024000);
|
|
||||||
break;
|
SDRDeviceChannel *chan = dev->getRxChannel();
|
||||||
case wxID_BANDWIDTH_1500M:
|
|
||||||
wxGetApp().setSampleRate(1500000);
|
rateLow = 2000000;
|
||||||
break;
|
rateHigh = 30000000;
|
||||||
case wxID_BANDWIDTH_1800M:
|
|
||||||
wxGetApp().setSampleRate(1800000);
|
if (chan->getSampleRates().size()) {
|
||||||
break;
|
rateLow = chan->getSampleRates()[0];
|
||||||
case wxID_BANDWIDTH_1920M:
|
rateHigh = chan->getSampleRates()[chan->getSampleRates().size()-1];
|
||||||
wxGetApp().setSampleRate(1920000);
|
}
|
||||||
break;
|
|
||||||
case wxID_BANDWIDTH_2000M:
|
long bw = wxGetNumberFromUser("\n" + dev->getName() + "\n\n "
|
||||||
wxGetApp().setSampleRate(2000000);
|
+ "min: " + std::to_string(rateLow) + " Hz"
|
||||||
break;
|
+ ", max: " + std::to_string(rateHigh) + " Hz\n",
|
||||||
case wxID_BANDWIDTH_2048M:
|
"Sample Rate in Hz",
|
||||||
wxGetApp().setSampleRate(2048000);
|
"Manual Bandwidth Entry",
|
||||||
break;
|
wxGetApp().getSampleRate(),
|
||||||
case wxID_BANDWIDTH_2160M:
|
rateLow,
|
||||||
wxGetApp().setSampleRate(2160000);
|
rateHigh,
|
||||||
break;
|
this);
|
||||||
case wxID_BANDWIDTH_2400M:
|
if (bw != -1) {
|
||||||
wxGetApp().setSampleRate(2400000);
|
wxGetApp().setSampleRate(bw);
|
||||||
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);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SDRDeviceInfo *> *devs = wxGetApp().getDevices();
|
if (event.GetId() >= wxID_BANDWIDTH_BASE && event.GetId() < wxID_BANDWIDTH_BASE+sampleRates.size()) {
|
||||||
if (event.GetId() >= wxID_DEVICE_ID && event.GetId() <= wxID_DEVICE_ID + devs->size()) {
|
wxGetApp().setSampleRate(sampleRates[event.GetId()-wxID_BANDWIDTH_BASE]);
|
||||||
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_AUDIO_BANDWIDTH_BASE) {
|
if (event.GetId() >= wxID_AUDIO_BANDWIDTH_BASE) {
|
||||||
int evId = event.GetId();
|
int evId = event.GetId();
|
||||||
std::vector<RtAudio::DeviceInfo>::iterator devices_i;
|
std::vector<RtAudio::DeviceInfo>::iterator devices_i;
|
||||||
@ -656,11 +793,13 @@ void AppFrame::OnThread(wxCommandEvent& event) {
|
|||||||
|
|
||||||
void AppFrame::OnIdle(wxIdleEvent& event) {
|
void AppFrame::OnIdle(wxIdleEvent& event) {
|
||||||
|
|
||||||
|
if (deviceChanged.load()) {
|
||||||
|
updateDeviceParams();
|
||||||
|
}
|
||||||
|
|
||||||
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
||||||
|
|
||||||
if (demod) {
|
if (demod) {
|
||||||
DemodulatorInstance *demod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
|
||||||
|
|
||||||
if (demod->isTracking()) {
|
if (demod->isTracking()) {
|
||||||
if (spectrumCanvas->getViewState()) {
|
if (spectrumCanvas->getViewState()) {
|
||||||
long long diff = abs(demod->getFrequency() - spectrumCanvas->getCenterFrequency()) + (demod->getBandwidth()/2) + (demod->getBandwidth()/4);
|
long long diff = abs(demod->getFrequency() - spectrumCanvas->getCenterFrequency()) + (demod->getBandwidth()/2) + (demod->getBandwidth()/4);
|
||||||
@ -790,8 +929,10 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
|||||||
if (!demodTuner->HasFocus()) {
|
if (!demodTuner->HasFocus()) {
|
||||||
demodTuner->SetFocus();
|
demodTuner->SetFocus();
|
||||||
}
|
}
|
||||||
} else if (!waterfallCanvas->HasFocus()) {
|
} else if (!wxGetApp().isDeviceSelectorOpen()) {
|
||||||
waterfallCanvas->SetFocus();
|
if (!waterfallCanvas->HasFocus()) {
|
||||||
|
waterfallCanvas->SetFocus();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scopeCanvas->setPPMMode(demodTuner->isAltDown());
|
scopeCanvas->setPPMMode(demodTuner->isAltDown());
|
||||||
@ -802,10 +943,10 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
|||||||
wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0));
|
wxGetApp().getAudioVisualQueue()->set_max_num_items((scopeCanvas->scopeVisible()?1:0) + (scopeCanvas->spectrumVisible()?1:0));
|
||||||
|
|
||||||
wxGetApp().getScopeProcessor()->run();
|
wxGetApp().getScopeProcessor()->run();
|
||||||
wxGetApp().getSpectrumDistributor()->run();
|
// wxGetApp().getSpectrumDistributor()->run();
|
||||||
|
|
||||||
SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor();
|
SpectrumVisualProcessor *proc = wxGetApp().getSpectrumProcessor();
|
||||||
|
|
||||||
if (spectrumAvgMeter->inputChanged()) {
|
if (spectrumAvgMeter->inputChanged()) {
|
||||||
float val = spectrumAvgMeter->getInputValue();
|
float val = spectrumAvgMeter->getInputValue();
|
||||||
if (val < 0.01) {
|
if (val < 0.01) {
|
||||||
@ -831,7 +972,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
|||||||
dproc->setCenterFrequency(demodWaterfallCanvas->getCenterFrequency());
|
dproc->setCenterFrequency(demodWaterfallCanvas->getCenterFrequency());
|
||||||
|
|
||||||
SpectrumVisualProcessor *wproc = waterfallDataThread->getProcessor();
|
SpectrumVisualProcessor *wproc = waterfallDataThread->getProcessor();
|
||||||
|
|
||||||
if (waterfallSpeedMeter->inputChanged()) {
|
if (waterfallSpeedMeter->inputChanged()) {
|
||||||
float val = waterfallSpeedMeter->getInputValue();
|
float val = waterfallSpeedMeter->getInputValue();
|
||||||
waterfallSpeedMeter->setLevel(val);
|
waterfallSpeedMeter->setLevel(val);
|
||||||
@ -843,6 +984,7 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
|||||||
wproc->setView(waterfallCanvas->getViewState());
|
wproc->setView(waterfallCanvas->getViewState());
|
||||||
wproc->setBandwidth(waterfallCanvas->getBandwidth());
|
wproc->setBandwidth(waterfallCanvas->getBandwidth());
|
||||||
wproc->setCenterFrequency(waterfallCanvas->getCenterFrequency());
|
wproc->setCenterFrequency(waterfallCanvas->getCenterFrequency());
|
||||||
|
wxGetApp().getSDRPostThread()->setIQVisualRange(waterfallCanvas->getCenterFrequency(), waterfallCanvas->getBandwidth());
|
||||||
|
|
||||||
// waterfallCanvas->processInputQueue();
|
// waterfallCanvas->processInputQueue();
|
||||||
// waterfallCanvas->Refresh();
|
// waterfallCanvas->Refresh();
|
||||||
@ -856,6 +998,39 @@ void AppFrame::OnIdle(wxIdleEvent& event) {
|
|||||||
event.RequestMore();
|
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) {
|
void AppFrame::saveSession(std::string fileName) {
|
||||||
DataTree s("cubicsdr_session");
|
DataTree s("cubicsdr_session");
|
||||||
DataNode *header = s.rootNode()->newChild("header");
|
DataNode *header = s.rootNode()->newChild("header");
|
||||||
@ -993,3 +1168,8 @@ bool AppFrame::loadSession(std::string fileName) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FFTVisualDataThread *AppFrame::getWaterfallDataThread() {
|
||||||
|
return waterfallDataThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#pragma once
|
#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 "PrimaryGLContext.h"
|
||||||
|
|
||||||
#include "ScopeCanvas.h"
|
#include "ScopeCanvas.h"
|
||||||
@ -9,7 +13,9 @@
|
|||||||
#include "MeterCanvas.h"
|
#include "MeterCanvas.h"
|
||||||
#include "TuningCanvas.h"
|
#include "TuningCanvas.h"
|
||||||
#include "ModeSelectorCanvas.h"
|
#include "ModeSelectorCanvas.h"
|
||||||
|
#include "GainCanvas.h"
|
||||||
#include "FFTVisualDataThread.h"
|
#include "FFTVisualDataThread.h"
|
||||||
|
#include "SDRDeviceInfo.h"
|
||||||
//#include "UITestCanvas.h"
|
//#include "UITestCanvas.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
@ -22,6 +28,11 @@
|
|||||||
#define wxID_SET_DS_I 2005
|
#define wxID_SET_DS_I 2005
|
||||||
#define wxID_SET_DS_Q 2006
|
#define wxID_SET_DS_Q 2006
|
||||||
#define wxID_SET_SWAP_IQ 2007
|
#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_DEFAULT 2100
|
||||||
#define wxID_THEME_SHARP 2101
|
#define wxID_THEME_SHARP 2101
|
||||||
@ -31,20 +42,10 @@
|
|||||||
#define wxID_THEME_HD 2105
|
#define wxID_THEME_HD 2105
|
||||||
#define wxID_THEME_RADAR 2106
|
#define wxID_THEME_RADAR 2106
|
||||||
|
|
||||||
#define wxID_BANDWIDTH_250K 2150
|
#define wxID_BANDWIDTH_BASE 2150
|
||||||
#define wxID_BANDWIDTH_1000M 2151
|
#define wxID_BANDWIDTH_MANUAL 2200
|
||||||
#define wxID_BANDWIDTH_1024M 2152
|
|
||||||
#define wxID_BANDWIDTH_1500M 2153
|
#define wxID_SETTINGS_BASE 2300
|
||||||
#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_DEVICE_ID 3500
|
#define wxID_DEVICE_ID 3500
|
||||||
|
|
||||||
@ -58,17 +59,22 @@ public:
|
|||||||
~AppFrame();
|
~AppFrame();
|
||||||
void OnThread(wxCommandEvent& event);
|
void OnThread(wxCommandEvent& event);
|
||||||
void OnEventInput(wxThreadEvent& event);
|
void OnEventInput(wxThreadEvent& event);
|
||||||
void initDeviceParams(std::string deviceId);
|
void initDeviceParams(SDRDeviceInfo *devInfo);
|
||||||
|
void updateDeviceParams();
|
||||||
|
|
||||||
void saveSession(std::string fileName);
|
void saveSession(std::string fileName);
|
||||||
bool loadSession(std::string fileName);
|
bool loadSession(std::string fileName);
|
||||||
|
|
||||||
|
FFTVisualDataThread *getWaterfallDataThread();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void OnMenu(wxCommandEvent& event);
|
void OnMenu(wxCommandEvent& event);
|
||||||
void OnClose(wxCloseEvent& event);
|
void OnClose(wxCloseEvent& event);
|
||||||
void OnNewWindow(wxCommandEvent& event);
|
void OnNewWindow(wxCommandEvent& event);
|
||||||
void OnIdle(wxIdleEvent& event);
|
void OnIdle(wxIdleEvent& event);
|
||||||
|
void OnDoubleClickSash(wxSplitterEvent& event);
|
||||||
|
void OnUnSplit(wxSplitterEvent& event);
|
||||||
|
|
||||||
ScopeCanvas *scopeCanvas;
|
ScopeCanvas *scopeCanvas;
|
||||||
SpectrumCanvas *spectrumCanvas;
|
SpectrumCanvas *spectrumCanvas;
|
||||||
WaterfallCanvas *waterfallCanvas;
|
WaterfallCanvas *waterfallCanvas;
|
||||||
@ -82,7 +88,11 @@ private:
|
|||||||
MeterCanvas *spectrumAvgMeter;
|
MeterCanvas *spectrumAvgMeter;
|
||||||
MeterCanvas *waterfallSpeedMeter;
|
MeterCanvas *waterfallSpeedMeter;
|
||||||
ModeSelectorCanvas *demodMuteButton;
|
ModeSelectorCanvas *demodMuteButton;
|
||||||
|
GainCanvas *gainCanvas;
|
||||||
|
wxSizerItem *gainSizerItem, *gainSpacerItem;
|
||||||
|
wxSplitterWindow *mainVisSplitter, *mainSplitter;
|
||||||
|
wxBoxSizer *demodTray;
|
||||||
|
|
||||||
DemodulatorInstance *activeDemodulator;
|
DemodulatorInstance *activeDemodulator;
|
||||||
|
|
||||||
std::vector<RtAudio::DeviceInfo> devices;
|
std::vector<RtAudio::DeviceInfo> devices;
|
||||||
@ -92,13 +102,21 @@ private:
|
|||||||
std::map<int, wxMenuItem *> sampleRateMenuItems;
|
std::map<int, wxMenuItem *> sampleRateMenuItems;
|
||||||
std::map<int, wxMenuItem *> audioSampleRateMenuItems;
|
std::map<int, wxMenuItem *> audioSampleRateMenuItems;
|
||||||
std::map<int, wxMenuItem *> directSamplingMenuItems;
|
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;
|
std::string currentSessionFile;
|
||||||
|
|
||||||
FFTVisualDataThread *waterfallDataThread;
|
FFTVisualDataThread *waterfallDataThread;
|
||||||
|
|
||||||
std::thread *t_FFTData;
|
std::thread *t_FFTData;
|
||||||
|
SDRDeviceInfo *devInfo;
|
||||||
|
std::atomic_bool deviceChanged;
|
||||||
|
|
||||||
wxDECLARE_EVENT_TABLE();
|
wxDECLARE_EVENT_TABLE();
|
||||||
};
|
};
|
||||||
|
503
src/CubicSDR.cpp
503
src/CubicSDR.cpp
@ -13,6 +13,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "CubicSDR.h"
|
#include "CubicSDR.h"
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
#ifdef _OSX_APP_
|
#ifdef _OSX_APP_
|
||||||
#include "CoreFoundation/CoreFoundation.h"
|
#include "CoreFoundation/CoreFoundation.h"
|
||||||
@ -20,9 +21,101 @@
|
|||||||
|
|
||||||
IMPLEMENT_APP(CubicSDR)
|
IMPLEMENT_APP(CubicSDR)
|
||||||
|
|
||||||
CubicSDR::CubicSDR() : appframe(NULL), m_glContext(NULL), frequency(0), offset(0), ppm(0), snap(1), sampleRate(DEFAULT_SAMPLE_RATE), directSamplingMode(0),
|
#ifdef MINGW_PATCH
|
||||||
sdrThread(NULL), sdrPostThread(NULL), spectrumVisualThread(NULL), demodVisualThread(NULL), pipeSDRCommand(NULL), pipeSDRIQData(NULL), pipeIQVisualData(NULL), pipeAudioVisualData(NULL), t_SDR(NULL), t_PostSDR(NULL) {
|
FILE _iob[] = { *stdin, *stdout, *stderr };
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -48,7 +141,8 @@ bool CubicSDR::OnInit() {
|
|||||||
frequency = wxGetApp().getConfig()->getCenterFreq();
|
frequency = wxGetApp().getConfig()->getCenterFreq();
|
||||||
offset = 0;
|
offset = 0;
|
||||||
ppm = 0;
|
ppm = 0;
|
||||||
directSamplingMode = 0;
|
devicesReady.store(false);
|
||||||
|
deviceSelectorOpen.store(false);
|
||||||
|
|
||||||
// Visual Data
|
// Visual Data
|
||||||
spectrumVisualThread = new SpectrumVisualDataThread();
|
spectrumVisualThread = new SpectrumVisualDataThread();
|
||||||
@ -57,22 +151,15 @@ bool CubicSDR::OnInit() {
|
|||||||
pipeIQVisualData = new DemodulatorThreadInputQueue();
|
pipeIQVisualData = new DemodulatorThreadInputQueue();
|
||||||
pipeIQVisualData->set_max_num_items(1);
|
pipeIQVisualData->set_max_num_items(1);
|
||||||
|
|
||||||
spectrumDistributor.setInput(pipeIQVisualData);
|
|
||||||
|
|
||||||
pipeDemodIQVisualData = new DemodulatorThreadInputQueue();
|
pipeDemodIQVisualData = new DemodulatorThreadInputQueue();
|
||||||
pipeDemodIQVisualData->set_max_num_items(1);
|
pipeDemodIQVisualData->set_max_num_items(1);
|
||||||
|
|
||||||
pipeSpectrumIQVisualData = new DemodulatorThreadInputQueue();
|
|
||||||
pipeSpectrumIQVisualData->set_max_num_items(1);
|
|
||||||
|
|
||||||
pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue();
|
pipeWaterfallIQVisualData = new DemodulatorThreadInputQueue();
|
||||||
pipeWaterfallIQVisualData->set_max_num_items(128);
|
pipeWaterfallIQVisualData->set_max_num_items(128);
|
||||||
|
|
||||||
spectrumDistributor.attachOutput(pipeDemodIQVisualData);
|
|
||||||
spectrumDistributor.attachOutput(pipeSpectrumIQVisualData);
|
|
||||||
|
|
||||||
getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData);
|
getDemodSpectrumProcessor()->setInput(pipeDemodIQVisualData);
|
||||||
getSpectrumProcessor()->setInput(pipeSpectrumIQVisualData);
|
getSpectrumProcessor()->setInput(pipeIQVisualData);
|
||||||
|
getSpectrumProcessor()->setHideDC(true);
|
||||||
|
|
||||||
pipeAudioVisualData = new DemodulatorThreadOutputQueue();
|
pipeAudioVisualData = new DemodulatorThreadOutputQueue();
|
||||||
pipeAudioVisualData->set_max_num_items(1);
|
pipeAudioVisualData->set_max_num_items(1);
|
||||||
@ -81,83 +168,35 @@ bool CubicSDR::OnInit() {
|
|||||||
|
|
||||||
// I/Q Data
|
// I/Q Data
|
||||||
pipeSDRIQData = new SDRThreadIQDataQueue();
|
pipeSDRIQData = new SDRThreadIQDataQueue();
|
||||||
pipeSDRCommand = new SDRThreadCommandQueue();
|
|
||||||
|
|
||||||
pipeSDRIQData->set_max_num_items(100);
|
pipeSDRIQData->set_max_num_items(100);
|
||||||
|
|
||||||
sdrThread = new SDRThread();
|
sdrThread = new SDRThread();
|
||||||
sdrThread->setInputQueue("SDRCommandQueue",pipeSDRCommand);
|
|
||||||
sdrThread->setOutputQueue("IQDataOutput",pipeSDRIQData);
|
sdrThread->setOutputQueue("IQDataOutput",pipeSDRIQData);
|
||||||
|
|
||||||
sdrPostThread = new SDRPostThread();
|
sdrPostThread = new SDRPostThread();
|
||||||
// sdrPostThread->setNumVisSamples(BUF_SIZE);
|
|
||||||
sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData);
|
sdrPostThread->setInputQueue("IQDataInput", pipeSDRIQData);
|
||||||
sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData);
|
sdrPostThread->setOutputQueue("IQVisualDataOutput", pipeIQVisualData);
|
||||||
sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData);
|
sdrPostThread->setOutputQueue("IQDataOutput", pipeWaterfallIQVisualData);
|
||||||
|
sdrPostThread->setOutputQueue("IQActiveDemodVisualDataOutput", pipeDemodIQVisualData);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
t_PostSDR = new std::thread(&SDRPostThread::threadMain, sdrPostThread);
|
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_SpectrumVisual = new std::thread(&SpectrumVisualDataThread::threadMain, spectrumVisualThread);
|
||||||
t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread);
|
t_DemodVisual = new std::thread(&SpectrumVisualDataThread::threadMain, demodVisualThread);
|
||||||
|
|
||||||
|
sdrEnum = new SDREnumerator();
|
||||||
|
|
||||||
appframe = new AppFrame();
|
appframe = new AppFrame();
|
||||||
if (dev != NULL) {
|
t_SDREnum = new std::thread(&SDREnumerator::threadMain, sdrEnum);
|
||||||
appframe->initDeviceParams(dev->getDeviceId());
|
|
||||||
DeviceConfig *devConfig = wxGetApp().getConfig()->getDevice(dev->getDeviceId());
|
|
||||||
ppm = devConfig->getPPM();
|
|
||||||
offset = devConfig->getOffset();
|
|
||||||
directSamplingMode = devConfig->getDirectSampling();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __APPLE__
|
//#ifdef __APPLE__
|
||||||
int main_policy;
|
// int main_policy;
|
||||||
struct sched_param main_param;
|
// struct sched_param main_param;
|
||||||
|
//
|
||||||
main_policy = SCHED_RR;
|
// main_policy = SCHED_RR;
|
||||||
main_param.sched_priority = sched_get_priority_min(SCHED_RR)+2;
|
// main_param.sched_priority = sched_get_priority_min(SCHED_RR)+2;
|
||||||
|
//
|
||||||
pthread_setschedparam(pthread_self(), main_policy, &main_param);
|
// pthread_setschedparam(pthread_self(), main_policy, &main_param);
|
||||||
#endif
|
//#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -166,9 +205,12 @@ int CubicSDR::OnExit() {
|
|||||||
demodMgr.terminateAll();
|
demodMgr.terminateAll();
|
||||||
|
|
||||||
std::cout << "Terminating SDR thread.." << std::endl;
|
std::cout << "Terminating SDR thread.." << std::endl;
|
||||||
sdrThread->terminate();
|
if (!sdrThread->isTerminated()) {
|
||||||
t_SDR->join();
|
sdrThread->terminate();
|
||||||
|
if (t_SDR) {
|
||||||
|
t_SDR->join();
|
||||||
|
}
|
||||||
|
}
|
||||||
std::cout << "Terminating SDR post-processing thread.." << std::endl;
|
std::cout << "Terminating SDR post-processing thread.." << std::endl;
|
||||||
sdrPostThread->terminate();
|
sdrPostThread->terminate();
|
||||||
t_PostSDR->join();
|
t_PostSDR->join();
|
||||||
@ -181,7 +223,6 @@ int CubicSDR::OnExit() {
|
|||||||
t_DemodVisual->join();
|
t_DemodVisual->join();
|
||||||
|
|
||||||
delete sdrThread;
|
delete sdrThread;
|
||||||
delete t_SDR;
|
|
||||||
|
|
||||||
delete sdrPostThread;
|
delete sdrPostThread;
|
||||||
delete t_PostSDR;
|
delete t_PostSDR;
|
||||||
@ -191,8 +232,6 @@ int CubicSDR::OnExit() {
|
|||||||
delete t_DemodVisual;
|
delete t_DemodVisual;
|
||||||
delete demodVisualThread;
|
delete demodVisualThread;
|
||||||
|
|
||||||
delete pipeSDRCommand;
|
|
||||||
|
|
||||||
delete pipeIQVisualData;
|
delete pipeIQVisualData;
|
||||||
delete pipeAudioVisualData;
|
delete pipeAudioVisualData;
|
||||||
delete pipeSDRIQData;
|
delete pipeSDRIQData;
|
||||||
@ -231,17 +270,87 @@ bool CubicSDR::OnCmdLineParsed(wxCmdLineParser& parser) {
|
|||||||
|
|
||||||
config.load();
|
config.load();
|
||||||
|
|
||||||
|
#ifdef BUNDLE_SOAPY_MODS
|
||||||
|
if (parser.Found("l")) {
|
||||||
|
useLocalMod.store(true);
|
||||||
|
} else {
|
||||||
|
useLocalMod.store(false);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
useLocalMod.store(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
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) {
|
void CubicSDR::setFrequency(long long freq) {
|
||||||
if (freq < sampleRate / 2) {
|
if (freq < sampleRate / 2) {
|
||||||
freq = sampleRate / 2;
|
freq = sampleRate / 2;
|
||||||
}
|
}
|
||||||
frequency = freq;
|
frequency = freq;
|
||||||
SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_TUNE);
|
sdrThread->setFrequency(freq);
|
||||||
command.llong_value = freq;
|
|
||||||
pipeSDRCommand->push(command);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
long long CubicSDR::getOffset() {
|
long long CubicSDR::getOffset() {
|
||||||
@ -250,42 +359,93 @@ long long CubicSDR::getOffset() {
|
|||||||
|
|
||||||
void CubicSDR::setOffset(long long ofs) {
|
void CubicSDR::setOffset(long long ofs) {
|
||||||
offset = ofs;
|
offset = ofs;
|
||||||
SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_SET_OFFSET);
|
sdrThread->setOffset(offset);
|
||||||
command.llong_value = ofs;
|
SDRDeviceInfo *dev = getDevice();
|
||||||
pipeSDRCommand->push(command);
|
|
||||||
|
|
||||||
SDRDeviceInfo *dev = (*getDevices())[getDevice()];
|
|
||||||
config.getDevice(dev->getDeviceId())->setOffset(ofs);
|
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() {
|
long long CubicSDR::getFrequency() {
|
||||||
return frequency;
|
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() {
|
ScopeVisualProcessor *CubicSDR::getScopeProcessor() {
|
||||||
return &scopeProcessor;
|
return &scopeProcessor;
|
||||||
}
|
}
|
||||||
@ -298,11 +458,6 @@ SpectrumVisualProcessor *CubicSDR::getDemodSpectrumProcessor() {
|
|||||||
return demodVisualThread->getProcessor();
|
return demodVisualThread->getProcessor();
|
||||||
}
|
}
|
||||||
|
|
||||||
VisualDataReDistributor<DemodulatorThreadIQData> *CubicSDR::getSpectrumDistributor() {
|
|
||||||
return &spectrumDistributor;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
DemodulatorThreadOutputQueue* CubicSDR::getAudioVisualQueue() {
|
DemodulatorThreadOutputQueue* CubicSDR::getAudioVisualQueue() {
|
||||||
return pipeAudioVisualData;
|
return pipeAudioVisualData;
|
||||||
}
|
}
|
||||||
@ -319,6 +474,15 @@ DemodulatorMgr &CubicSDR::getDemodMgr() {
|
|||||||
return demodMgr;
|
return demodMgr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDRPostThread *CubicSDR::getSDRPostThread() {
|
||||||
|
return sdrPostThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDRThread *CubicSDR::getSDRThread() {
|
||||||
|
return sdrThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CubicSDR::bindDemodulator(DemodulatorInstance *demod) {
|
void CubicSDR::bindDemodulator(DemodulatorInstance *demod) {
|
||||||
if (!demod) {
|
if (!demod) {
|
||||||
return;
|
return;
|
||||||
@ -326,14 +490,6 @@ void CubicSDR::bindDemodulator(DemodulatorInstance *demod) {
|
|||||||
sdrPostThread->bindDemodulator(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() {
|
long long CubicSDR::getSampleRate() {
|
||||||
return sampleRate;
|
return sampleRate;
|
||||||
}
|
}
|
||||||
@ -347,27 +503,9 @@ void CubicSDR::removeDemodulator(DemodulatorInstance *demod) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<SDRDeviceInfo*>* CubicSDR::getDevices() {
|
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() {
|
AppConfig *CubicSDR::getConfig() {
|
||||||
return &config;
|
return &config;
|
||||||
@ -378,29 +516,20 @@ void CubicSDR::saveConfig() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CubicSDR::setPPM(int ppm_in) {
|
void CubicSDR::setPPM(int ppm_in) {
|
||||||
if (sdrThread->getDeviceId() < 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ppm = ppm_in;
|
ppm = ppm_in;
|
||||||
|
sdrThread->setPPM(ppm);
|
||||||
|
|
||||||
SDRThreadCommand command(SDRThreadCommand::SDR_THREAD_CMD_SET_PPM);
|
SDRDeviceInfo *dev = getDevice();
|
||||||
command.llong_value = ppm;
|
if (dev) {
|
||||||
pipeSDRCommand->push(command);
|
config.getDevice(dev->getDeviceId())->setPPM(ppm_in);
|
||||||
|
}
|
||||||
SDRDeviceInfo *dev = (*getDevices())[getDevice()];
|
|
||||||
|
|
||||||
config.getDevice(dev->getDeviceId())->setPPM(ppm_in);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int CubicSDR::getPPM() {
|
int CubicSDR::getPPM() {
|
||||||
if (sdrThread->getDeviceId() < 0) {
|
SDRDeviceInfo *dev = sdrThread->getDevice();
|
||||||
return 0;
|
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;
|
return ppm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,3 +570,57 @@ void CubicSDR::setFrequencySnap(int snap) {
|
|||||||
int CubicSDR::getFrequencySnap() {
|
int CubicSDR::getFrequencySnap() {
|
||||||
return snap;
|
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 "PrimaryGLContext.h"
|
||||||
|
|
||||||
#include "ThreadQueue.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 "SDRPostThread.h"
|
||||||
#include "AudioThread.h"
|
#include "AudioThread.h"
|
||||||
#include "DemodulatorMgr.h"
|
#include "DemodulatorMgr.h"
|
||||||
@ -21,11 +26,16 @@
|
|||||||
#include "ScopeVisualProcessor.h"
|
#include "ScopeVisualProcessor.h"
|
||||||
#include "SpectrumVisualProcessor.h"
|
#include "SpectrumVisualProcessor.h"
|
||||||
#include "SpectrumVisualDataThread.h"
|
#include "SpectrumVisualDataThread.h"
|
||||||
|
#include "SDRDevices.h"
|
||||||
|
|
||||||
#include <wx/cmdline.h>
|
#include <wx/cmdline.h>
|
||||||
|
|
||||||
#define NUM_DEMODULATORS 1
|
#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 {
|
class CubicSDR: public wxApp {
|
||||||
public:
|
public:
|
||||||
CubicSDR();
|
CubicSDR();
|
||||||
@ -38,35 +48,36 @@ public:
|
|||||||
virtual void OnInitCmdLine(wxCmdLineParser& parser);
|
virtual void OnInitCmdLine(wxCmdLineParser& parser);
|
||||||
virtual bool OnCmdLineParsed(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);
|
void setFrequency(long long freq);
|
||||||
long long getFrequency();
|
long long getFrequency();
|
||||||
|
|
||||||
void setOffset(long long ofs);
|
void setOffset(long long ofs);
|
||||||
long long getOffset();
|
long long getOffset();
|
||||||
|
|
||||||
void setDirectSampling(int mode);
|
|
||||||
int getDirectSampling();
|
|
||||||
|
|
||||||
void setSwapIQ(bool swapIQ);
|
|
||||||
bool getSwapIQ();
|
|
||||||
|
|
||||||
void setSampleRate(long long rate_in);
|
void setSampleRate(long long rate_in);
|
||||||
long long getSampleRate();
|
long long getSampleRate();
|
||||||
|
|
||||||
std::vector<SDRDeviceInfo *> *getDevices();
|
std::vector<SDRDeviceInfo *> *getDevices();
|
||||||
void setDevice(int deviceId);
|
void setDevice(SDRDeviceInfo *dev);
|
||||||
int getDevice();
|
SDRDeviceInfo * getDevice();
|
||||||
|
|
||||||
ScopeVisualProcessor *getScopeProcessor();
|
ScopeVisualProcessor *getScopeProcessor();
|
||||||
SpectrumVisualProcessor *getSpectrumProcessor();
|
SpectrumVisualProcessor *getSpectrumProcessor();
|
||||||
SpectrumVisualProcessor *getDemodSpectrumProcessor();
|
SpectrumVisualProcessor *getDemodSpectrumProcessor();
|
||||||
VisualDataReDistributor<DemodulatorThreadIQData> *getSpectrumDistributor();
|
|
||||||
|
|
||||||
DemodulatorThreadOutputQueue* getAudioVisualQueue();
|
DemodulatorThreadOutputQueue* getAudioVisualQueue();
|
||||||
DemodulatorThreadInputQueue* getIQVisualQueue();
|
DemodulatorThreadInputQueue* getIQVisualQueue();
|
||||||
DemodulatorThreadInputQueue* getWaterfallVisualQueue();
|
DemodulatorThreadInputQueue* getWaterfallVisualQueue();
|
||||||
|
DemodulatorThreadInputQueue* getActiveDemodVisualQueue();
|
||||||
DemodulatorMgr &getDemodMgr();
|
DemodulatorMgr &getDemodMgr();
|
||||||
|
|
||||||
|
SDRPostThread *getSDRPostThread();
|
||||||
|
SDRThread *getSDRThread();
|
||||||
|
|
||||||
void bindDemodulator(DemodulatorInstance *demod);
|
void bindDemodulator(DemodulatorInstance *demod);
|
||||||
void removeDemodulator(DemodulatorInstance *demod);
|
void removeDemodulator(DemodulatorInstance *demod);
|
||||||
|
|
||||||
@ -82,11 +93,31 @@ public:
|
|||||||
void showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetMode = FrequencyDialog::FDIALOG_TARGET_DEFAULT);
|
void showFrequencyInput(FrequencyDialog::FrequencyDialogTarget targetMode = FrequencyDialog::FDIALOG_TARGET_DEFAULT);
|
||||||
AppFrame *getAppFrame();
|
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:
|
private:
|
||||||
AppFrame *appframe;
|
AppFrame *appframe;
|
||||||
AppConfig config;
|
AppConfig config;
|
||||||
PrimaryGLContext *m_glContext;
|
PrimaryGLContext *m_glContext;
|
||||||
std::vector<SDRDeviceInfo *> devs;
|
std::vector<SDRDeviceInfo *> *devs;
|
||||||
|
|
||||||
DemodulatorMgr demodMgr;
|
DemodulatorMgr demodMgr;
|
||||||
|
|
||||||
@ -94,36 +125,52 @@ private:
|
|||||||
long long offset;
|
long long offset;
|
||||||
int ppm, snap;
|
int ppm, snap;
|
||||||
long long sampleRate;
|
long long sampleRate;
|
||||||
int directSamplingMode;
|
std::atomic_bool agcMode;
|
||||||
|
|
||||||
SDRThread *sdrThread;
|
SDRThread *sdrThread;
|
||||||
|
SDREnumerator *sdrEnum;
|
||||||
SDRPostThread *sdrPostThread;
|
SDRPostThread *sdrPostThread;
|
||||||
SpectrumVisualDataThread *spectrumVisualThread;
|
SpectrumVisualDataThread *spectrumVisualThread;
|
||||||
SpectrumVisualDataThread *demodVisualThread;
|
SpectrumVisualDataThread *demodVisualThread;
|
||||||
|
|
||||||
SDRThreadCommandQueue* pipeSDRCommand;
|
|
||||||
SDRThreadIQDataQueue* pipeSDRIQData;
|
SDRThreadIQDataQueue* pipeSDRIQData;
|
||||||
DemodulatorThreadInputQueue* pipeIQVisualData;
|
DemodulatorThreadInputQueue* pipeIQVisualData;
|
||||||
DemodulatorThreadOutputQueue* pipeAudioVisualData;
|
DemodulatorThreadOutputQueue* pipeAudioVisualData;
|
||||||
DemodulatorThreadInputQueue* pipeDemodIQVisualData;
|
DemodulatorThreadInputQueue* pipeDemodIQVisualData;
|
||||||
DemodulatorThreadInputQueue* pipeSpectrumIQVisualData;
|
|
||||||
DemodulatorThreadInputQueue* pipeWaterfallIQVisualData;
|
DemodulatorThreadInputQueue* pipeWaterfallIQVisualData;
|
||||||
|
DemodulatorThreadInputQueue* pipeActiveDemodIQVisualData;
|
||||||
|
|
||||||
ScopeVisualProcessor scopeProcessor;
|
ScopeVisualProcessor scopeProcessor;
|
||||||
|
|
||||||
VisualDataReDistributor<DemodulatorThreadIQData> spectrumDistributor;
|
SDRDevicesDialog *deviceSelectorDialog;
|
||||||
|
|
||||||
std::thread *t_SDR;
|
SoapySDR::Kwargs streamArgs;
|
||||||
std::thread *t_PostSDR;
|
SoapySDR::Kwargs settingArgs;
|
||||||
std::thread *t_SpectrumVisual;
|
|
||||||
std::thread *t_DemodVisual;
|
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, "l", "localmod", "Check local SoapySDR modules instead of bundled first." },
|
||||||
|
{ wxCMD_LINE_NONE }
|
||||||
|
};
|
||||||
|
#else
|
||||||
static const wxCmdLineEntryDesc commandLineInfo [] =
|
static const wxCmdLineEntryDesc commandLineInfo [] =
|
||||||
{
|
{
|
||||||
{ wxCMD_LINE_SWITCH, "h", "help", "Command line parameter help", wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP },
|
{ 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_OPTION, "c", "config", "Specify a named configuration to use, i.e. '-c ham'" },
|
||||||
{ wxCMD_LINE_NONE }
|
{ wxCMD_LINE_NONE }
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
DECLARE_APP(CubicSDR)
|
DECLARE_APP(CubicSDR)
|
||||||
|
@ -27,10 +27,14 @@ const char filePathSeparator =
|
|||||||
|
|
||||||
#define BUF_SIZE (16384*6)
|
#define BUF_SIZE (16384*6)
|
||||||
|
|
||||||
#define DEFAULT_SAMPLE_RATE 2400000
|
#define DEFAULT_SAMPLE_RATE 2500000
|
||||||
#define DEFAULT_FFT_SIZE 2048
|
#define DEFAULT_FFT_SIZE 2048
|
||||||
|
|
||||||
#define DEFAULT_DEMOD_TYPE 1
|
#define DEFAULT_DEMOD_TYPE 1
|
||||||
#define DEFAULT_DEMOD_BW 200000
|
#define DEFAULT_DEMOD_BW 200000
|
||||||
|
|
||||||
#define DEFAULT_WATERFALL_LPS 30
|
#define DEFAULT_WATERFALL_LPS 30
|
||||||
|
|
||||||
|
#define CHANNELIZER_RATE_MAX 400000
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
#include "wx/clipbrd.h"
|
#include "wx/clipbrd.h"
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
|
||||||
#include "CubicSDR.h"
|
#include "CubicSDR.h"
|
||||||
|
|
||||||
wxBEGIN_EVENT_TABLE(FrequencyDialog, wxDialog)
|
wxBEGIN_EVENT_TABLE(FrequencyDialog, wxDialog)
|
||||||
@ -37,75 +36,6 @@ FrequencyDialog::FrequencyDialog(wxWindow * parent, wxWindowID id, const wxStrin
|
|||||||
dialogText->SetSelection(-1, -1);
|
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) {
|
void FrequencyDialog::OnChar(wxKeyEvent& event) {
|
||||||
int c = event.GetKeyCode();
|
int c = event.GetKeyCode();
|
||||||
|
@ -21,14 +21,10 @@ public:
|
|||||||
|
|
||||||
wxTextCtrl * dialogText;
|
wxTextCtrl * dialogText;
|
||||||
|
|
||||||
long long strToFrequency(std::string freqStr);
|
|
||||||
std::string frequencyToStr(long long freq);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DemodulatorInstance *activeDemod;
|
DemodulatorInstance *activeDemod;
|
||||||
void OnEnter ( wxCommandEvent &event );
|
void OnEnter ( wxCommandEvent &event );
|
||||||
void OnChar ( wxKeyEvent &event );
|
void OnChar ( wxKeyEvent &event );
|
||||||
std::string& filterChars(std::string& s, const std::string& allowed);
|
|
||||||
FrequencyDialogTarget targetMode;
|
FrequencyDialogTarget targetMode;
|
||||||
DECLARE_EVENT_TABLE()
|
DECLARE_EVENT_TABLE()
|
||||||
};
|
};
|
||||||
|
@ -20,7 +20,7 @@ AudioThread::AudioThread() : IOThread(),
|
|||||||
outputDevice.store(-1);
|
outputDevice.store(-1);
|
||||||
gain.store(1.0);
|
gain.store(1.0);
|
||||||
|
|
||||||
boundThreads = new std::vector<AudioThread *>;
|
boundThreads.store(new std::vector<AudioThread *>);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioThread::~AudioThread() {
|
AudioThread::~AudioThread() {
|
||||||
|
@ -255,7 +255,9 @@ void DemodulatorInstance::setOutputDevice(int device_id) {
|
|||||||
|
|
||||||
int DemodulatorInstance::getOutputDevice() {
|
int DemodulatorInstance::getOutputDevice() {
|
||||||
if (currentOutputDevice == -1) {
|
if (currentOutputDevice == -1) {
|
||||||
currentOutputDevice = audioThread->getOutputDevice();
|
if (audioThread) {
|
||||||
|
currentOutputDevice = audioThread->getOutputDevice();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return currentOutputDevice;
|
return currentOutputDevice;
|
||||||
@ -308,7 +310,7 @@ void DemodulatorInstance::setBandwidth(int bw) {
|
|||||||
bw = AudioThread::deviceSampleRate[getOutputDevice()];
|
bw = AudioThread::deviceSampleRate[getOutputDevice()];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!active) {
|
if (!active && demodulatorPreThread != NULL) {
|
||||||
currentBandwidth = bw;
|
currentBandwidth = bw;
|
||||||
checkBandwidth();
|
checkBandwidth();
|
||||||
demodulatorPreThread->getParams().bandwidth = currentBandwidth;
|
demodulatorPreThread->getParams().bandwidth = currentBandwidth;
|
||||||
|
@ -155,18 +155,20 @@ void DemodulatorPreThread::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
|
inp->decRefCount();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Requested frequency is not center, shift it into the center!
|
// Requested frequency is not center, shift it into the center!
|
||||||
if ((params.frequency - inp->frequency) != shiftFrequency || rateChanged) {
|
if ((params.frequency - inp->frequency) != shiftFrequency || rateChanged) {
|
||||||
shiftFrequency = params.frequency - inp->frequency;
|
shiftFrequency = params.frequency - inp->frequency;
|
||||||
if (abs(shiftFrequency) <= (int) ((double) (wxGetApp().getSampleRate() / 2) * 1.5)) {
|
if (abs(shiftFrequency) <= (int) ((double) (inp->sampleRate / 2) * 1.5)) {
|
||||||
nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) wxGetApp().getSampleRate())));
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,14 +59,13 @@ void DemodulatorThread::run() {
|
|||||||
nco_crcf_pll_set_bandwidth(stereoPilot, 0.25f);
|
nco_crcf_pll_set_bandwidth(stereoPilot, 0.25f);
|
||||||
|
|
||||||
// half band filter used for side-band elimination
|
// 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
|
// Automatic IQ gain
|
||||||
iqAutoGain = agc_crcf_create();
|
iqAutoGain = agc_crcf_create();
|
||||||
agc_crcf_set_bandwidth(iqAutoGain, 0.1);
|
agc_crcf_set_bandwidth(iqAutoGain, 0.1);
|
||||||
|
|
||||||
AudioThreadInput *ati_vis = new AudioThreadInput;
|
ReBuffer<AudioThreadInput> audioVisBuffers;
|
||||||
ati_vis->data.reserve(DEMOD_VIS_SIZE);
|
|
||||||
|
|
||||||
std::cout << "Demodulator thread started.." << std::endl;
|
std::cout << "Demodulator thread started.." << std::endl;
|
||||||
|
|
||||||
@ -192,13 +191,13 @@ void DemodulatorThread::run() {
|
|||||||
switch (demodulatorType.load()) {
|
switch (demodulatorType.load()) {
|
||||||
case DEMOD_TYPE_LSB:
|
case DEMOD_TYPE_LSB:
|
||||||
for (int i = 0; i < bufSize; i++) { // Reject upper band
|
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]);
|
ampmodem_demodulate(demodAM, x, &demodOutputData[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DEMOD_TYPE_USB:
|
case DEMOD_TYPE_USB:
|
||||||
for (int i = 0; i < bufSize; i++) { // Reject lower band
|
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]);
|
ampmodem_demodulate(demodAM, y, &demodOutputData[i]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -358,8 +357,8 @@ void DemodulatorThread::run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ati && audioVisOutputQueue != NULL && audioVisOutputQueue->empty()) {
|
if (ati && audioVisOutputQueue != NULL && audioVisOutputQueue->empty()) {
|
||||||
|
AudioThreadInput *ati_vis = audioVisBuffers.getBuffer();
|
||||||
ati_vis->busy_update.lock();
|
ati_vis->setRefCount(1);
|
||||||
ati_vis->sampleRate = inp->sampleRate;
|
ati_vis->sampleRate = inp->sampleRate;
|
||||||
ati_vis->inputRate = inp->sampleRate;
|
ati_vis->inputRate = inp->sampleRate;
|
||||||
|
|
||||||
@ -404,7 +403,6 @@ void DemodulatorThread::run() {
|
|||||||
// std::cout << "Signal: " << agc_crcf_get_signal_level(agc) << " -- " << agc_crcf_get_rssi(agc) << "dB " << std::endl;
|
// 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);
|
audioVisOutputQueue->push(ati_vis);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -487,7 +485,7 @@ void DemodulatorThread::run() {
|
|||||||
firhilbf_destroy(firStereoR2C);
|
firhilbf_destroy(firStereoR2C);
|
||||||
firhilbf_destroy(firStereoC2R);
|
firhilbf_destroy(firStereoC2R);
|
||||||
nco_crcf_destroy(stereoPilot);
|
nco_crcf_destroy(stereoPilot);
|
||||||
resamp2_cccf_destroy(ssbFilt);
|
resamp2_crcf_destroy(ssbFilt);
|
||||||
|
|
||||||
outputBuffers.purge();
|
outputBuffers.purge();
|
||||||
|
|
||||||
@ -495,7 +493,7 @@ void DemodulatorThread::run() {
|
|||||||
AudioThreadInput *dummy_vis;
|
AudioThreadInput *dummy_vis;
|
||||||
audioVisOutputQueue->pop(dummy_vis);
|
audioVisOutputQueue->pop(dummy_vis);
|
||||||
}
|
}
|
||||||
delete ati_vis;
|
audioVisBuffers.purge();
|
||||||
|
|
||||||
DemodulatorThreadCommand tCmd(DemodulatorThreadCommand::DEMOD_THREAD_CMD_DEMOD_TERMINATED);
|
DemodulatorThreadCommand tCmd(DemodulatorThreadCommand::DEMOD_THREAD_CMD_DEMOD_TERMINATED);
|
||||||
tCmd.context = this;
|
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) {
|
void WaterfallPanel::setup(int fft_size_in, int num_waterfall_lines_in) {
|
||||||
waterfall_lines = num_waterfall_lines_in;
|
waterfall_lines = num_waterfall_lines_in;
|
||||||
fft_size = fft_size_in;
|
fft_size = fft_size_in;
|
||||||
|
lines_buffered.store(0);
|
||||||
|
|
||||||
if (points.size() != fft_size) {
|
if (points.size() != fft_size) {
|
||||||
points.resize(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;
|
waterfall_ofs[i] = waterfall_lines - 1;
|
||||||
}
|
}
|
||||||
|
texInitialized.store(false);
|
||||||
|
bufferInitialized.store(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallPanel::refreshTheme() {
|
void WaterfallPanel::refreshTheme() {
|
||||||
@ -52,7 +55,47 @@ void WaterfallPanel::setPoints(std::vector<float> &points) {
|
|||||||
void WaterfallPanel::step() {
|
void WaterfallPanel::step() {
|
||||||
int half_fft_size = fft_size / 2;
|
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);
|
glGenTextures(2, waterfall);
|
||||||
|
|
||||||
unsigned char *waterfall_tex;
|
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);
|
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;
|
delete[] waterfall_tex;
|
||||||
|
|
||||||
|
texInitialized.store(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (points.size()) {
|
for (int i = 0, iMax = lines_buffered.load(); i < iMax; i++) {
|
||||||
|
for (int j = 0; j < 2; j++) {
|
||||||
|
memcpy(&(rLineBuffer[j][i*half_fft_size]),
|
||||||
|
&(lineBuffer[j][((iMax-1)*half_fft_size)-(i*half_fft_size)]), sizeof(unsigned char) * half_fft_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int run_ofs = 0;
|
||||||
|
while (lines_buffered.load()) {
|
||||||
|
int run_lines = lines_buffered.load();
|
||||||
|
if (run_lines > waterfall_ofs[0]) {
|
||||||
|
run_lines = waterfall_ofs[0];
|
||||||
|
}
|
||||||
for (int j = 0; j < 2; j++) {
|
for (int j = 0; j < 2; j++) {
|
||||||
for (int i = 0, iMax = half_fft_size; i < iMax; i++) {
|
|
||||||
float v = points[j * half_fft_size + i];
|
|
||||||
|
|
||||||
float wv = v < 0 ? 0 : (v > 0.99 ? 0.99 : v);
|
|
||||||
|
|
||||||
waterfall_slice[i] = (unsigned char) floor(wv * 255.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, waterfall[j]);
|
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) {
|
if (waterfall_ofs[j] == 0) {
|
||||||
waterfall_ofs[j] = waterfall_lines;
|
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() {
|
void WaterfallPanel::drawPanelContents() {
|
||||||
if (!waterfall[0]) {
|
if (!texInitialized.load()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +177,7 @@ void WaterfallPanel::drawPanelContents() {
|
|||||||
float half_pixel = 1.0 / viewWidth;
|
float half_pixel = 1.0 / viewWidth;
|
||||||
float half_texel = 1.0 / (float) half_fft_size;
|
float half_texel = 1.0 / (float) half_fft_size;
|
||||||
float vtexel = 1.0 / (float) waterfall_lines;
|
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]);
|
glBindTexture(GL_TEXTURE_2D, waterfall[0]);
|
||||||
glBegin (GL_QUADS);
|
glBegin (GL_QUADS);
|
||||||
@ -144,7 +191,7 @@ void WaterfallPanel::drawPanelContents() {
|
|||||||
glVertex3f(-1.0, 1.0, 0.0);
|
glVertex3f(-1.0, 1.0, 0.0);
|
||||||
glEnd();
|
glEnd();
|
||||||
|
|
||||||
vofs = (float) (waterfall_ofs[1] + 1) * vtexel;
|
vofs = (float) (waterfall_ofs[1]) * vtexel;
|
||||||
glBindTexture(GL_TEXTURE_2D, waterfall[1]);
|
glBindTexture(GL_TEXTURE_2D, waterfall[1]);
|
||||||
glBegin(GL_QUADS);
|
glBegin(GL_QUADS);
|
||||||
glTexCoord2f(0.0 + half_texel, 1.0 + vofs);
|
glTexCoord2f(0.0 + half_texel, 1.0 + vofs);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "GLPanel.h"
|
#include "GLPanel.h"
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
class WaterfallPanel : public GLPanel {
|
class WaterfallPanel : public GLPanel {
|
||||||
public:
|
public:
|
||||||
@ -9,7 +10,8 @@ public:
|
|||||||
void refreshTheme();
|
void refreshTheme();
|
||||||
void setPoints(std::vector<float> &points);
|
void setPoints(std::vector<float> &points);
|
||||||
void step();
|
void step();
|
||||||
|
void update();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void drawPanelContents();
|
void drawPanelContents();
|
||||||
|
|
||||||
@ -21,6 +23,10 @@ private:
|
|||||||
int fft_size;
|
int fft_size;
|
||||||
int waterfall_lines;
|
int waterfall_lines;
|
||||||
unsigned char *waterfall_slice;
|
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;
|
ColorTheme *activeTheme;
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "FFTDataDistributor.h"
|
#include "FFTDataDistributor.h"
|
||||||
|
|
||||||
FFTDataDistributor::FFTDataDistributor() : linesPerSecond(DEFAULT_WATERFALL_LPS), lineRateAccum(0.0), fftSize(DEFAULT_FFT_SIZE) {
|
FFTDataDistributor::FFTDataDistributor() : linesPerSecond(DEFAULT_WATERFALL_LPS), lineRateAccum(0.0), fftSize(DEFAULT_FFT_SIZE) {
|
||||||
|
bufferedItems = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFTDataDistributor::setFFTSize(int fftSize) {
|
void FFTDataDistributor::setFFTSize(int fftSize) {
|
||||||
@ -25,34 +26,44 @@ void FFTDataDistributor::process() {
|
|||||||
|
|
||||||
if (inp) {
|
if (inp) {
|
||||||
if (inputBuffer.sampleRate != inp->sampleRate || inputBuffer.frequency != inp->frequency) {
|
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.sampleRate = inp->sampleRate;
|
||||||
inputBuffer.frequency = inp->frequency;
|
inputBuffer.frequency = inp->frequency;
|
||||||
inputBuffer.data.assign(inp->data.begin(), inp->data.end());
|
inputBuffer.data.resize(bufferMax);
|
||||||
} else {
|
|
||||||
inputBuffer.data.insert(inputBuffer.data.end(), inp->data.begin(), inp->data.end());
|
|
||||||
}
|
}
|
||||||
|
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();
|
inp->decRefCount();
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// number of seconds contained in input
|
// 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
|
// 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
|
// ratio required to achieve the desired rate
|
||||||
double lineRateStep = ((double)linesPerSecond * inputTime)/(double)inputLines;
|
double lineRateStep = ((double)linesPerSecond * inputTime)/(double)inputLines;
|
||||||
|
|
||||||
if (inputBuffer.data.size() >= fftSize) {
|
if (bufferedItems >= fftSize) {
|
||||||
int numProcessed = 0;
|
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..
|
// move along, nothing to see here..
|
||||||
lineRateAccum += (lineRateStep * ((double)inputBuffer.data.size()/(double)fftSize));
|
lineRateAccum += (lineRateStep * ((double)bufferedItems/(double)fftSize));
|
||||||
numProcessed = inputBuffer.data.size();
|
numProcessed = bufferedItems;
|
||||||
} else {
|
} 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) {
|
if ((i + fftSize) > iMax) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -62,7 +73,7 @@ void FFTDataDistributor::process() {
|
|||||||
DemodulatorThreadIQData *outp = outputBuffers.getBuffer();
|
DemodulatorThreadIQData *outp = outputBuffers.getBuffer();
|
||||||
outp->frequency = inputBuffer.frequency;
|
outp->frequency = inputBuffer.frequency;
|
||||||
outp->sampleRate = inputBuffer.sampleRate;
|
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);
|
distribute(outp);
|
||||||
|
|
||||||
while (lineRateAccum >= 1.0) {
|
while (lineRateAccum >= 1.0) {
|
||||||
@ -74,8 +85,13 @@ void FFTDataDistributor::process() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (numProcessed) {
|
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 "VisualProcessor.h"
|
||||||
#include "DemodDefs.h"
|
#include "DemodDefs.h"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
class FFTDataDistributor : public VisualProcessor<DemodulatorThreadIQData, DemodulatorThreadIQData> {
|
class FFTDataDistributor : public VisualProcessor<DemodulatorThreadIQData, DemodulatorThreadIQData> {
|
||||||
public:
|
public:
|
||||||
@ -19,4 +20,5 @@ protected:
|
|||||||
int fftSize;
|
int fftSize;
|
||||||
int linesPerSecond;
|
int linesPerSecond;
|
||||||
double lineRateAccum;
|
double lineRateAccum;
|
||||||
|
int bufferMax, bufferOffset, bufferedItems;
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,8 @@ ScopeVisualProcessor::ScopeVisualProcessor(): fftInData(NULL), fftwOutput(NULL),
|
|||||||
scopeEnabled.store(true);
|
scopeEnabled.store(true);
|
||||||
spectrumEnabled.store(true);
|
spectrumEnabled.store(true);
|
||||||
fft_average_rate = 0.65;
|
fft_average_rate = 0.65;
|
||||||
|
fft_ceil_ma = fft_ceil_maa = 0;
|
||||||
|
fft_floor_ma = fft_floor_maa = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScopeVisualProcessor::~ScopeVisualProcessor() {
|
ScopeVisualProcessor::~ScopeVisualProcessor() {
|
||||||
@ -66,9 +68,7 @@ void ScopeVisualProcessor::process() {
|
|||||||
audioInputData->decRefCount();
|
audioInputData->decRefCount();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
audioInputData->busy_update.lock();
|
|
||||||
|
|
||||||
ScopeRenderData *renderData = NULL;
|
ScopeRenderData *renderData = NULL;
|
||||||
|
|
||||||
if (scopeEnabled) {
|
if (scopeEnabled) {
|
||||||
@ -81,7 +81,7 @@ void ScopeVisualProcessor::process() {
|
|||||||
renderData->channels = audioInputData->channels;
|
renderData->channels = audioInputData->channels;
|
||||||
renderData->inputRate = audioInputData->inputRate;
|
renderData->inputRate = audioInputData->inputRate;
|
||||||
renderData->sampleRate = audioInputData->sampleRate;
|
renderData->sampleRate = audioInputData->sampleRate;
|
||||||
|
|
||||||
if (renderData->waveform_points.size() != iMax * 2) {
|
if (renderData->waveform_points.size() != iMax * 2) {
|
||||||
renderData->waveform_points.resize(iMax * 2);
|
renderData->waveform_points.resize(iMax * 2);
|
||||||
}
|
}
|
||||||
@ -112,12 +112,10 @@ void ScopeVisualProcessor::process() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderData->spectrum = false;
|
renderData->spectrum = false;
|
||||||
|
|
||||||
distribute(renderData);
|
distribute(renderData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spectrumEnabled) {
|
if (spectrumEnabled) {
|
||||||
renderData = outputBuffers.getBuffer();
|
|
||||||
iMax = audioInputData->data.size();
|
iMax = audioInputData->data.size();
|
||||||
|
|
||||||
if (audioInputData->channels==1) {
|
if (audioInputData->channels==1) {
|
||||||
@ -138,7 +136,14 @@ void ScopeVisualProcessor::process() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderData = outputBuffers.getBuffer();
|
||||||
|
|
||||||
|
renderData->channels = audioInputData->channels;
|
||||||
|
renderData->inputRate = audioInputData->inputRate;
|
||||||
|
renderData->sampleRate = audioInputData->sampleRate;
|
||||||
|
|
||||||
|
audioInputData->decRefCount();
|
||||||
|
|
||||||
fftwf_execute(fftw_plan);
|
fftwf_execute(fftw_plan);
|
||||||
|
|
||||||
@ -157,9 +162,9 @@ void ScopeVisualProcessor::process() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < (fftSize/2); i++) {
|
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_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) {
|
if (fft_result_maa[i] > fft_ceil) {
|
||||||
fft_ceil = fft_result_maa[i];
|
fft_ceil = fft_result_maa[i];
|
||||||
}
|
}
|
||||||
@ -167,7 +172,7 @@ void ScopeVisualProcessor::process() {
|
|||||||
fft_floor = fft_result_maa[i];
|
fft_floor = fft_result_maa[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05;
|
fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05;
|
||||||
fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05;
|
fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05;
|
||||||
|
|
||||||
@ -176,8 +181,8 @@ void ScopeVisualProcessor::process() {
|
|||||||
|
|
||||||
int outSize = fftSize/2;
|
int outSize = fftSize/2;
|
||||||
|
|
||||||
if (audioInputData->sampleRate != audioInputData->inputRate) {
|
if (renderData->sampleRate != renderData->inputRate) {
|
||||||
outSize = (int)floor((float)outSize * ((float)audioInputData->sampleRate/(float)audioInputData->inputRate));
|
outSize = (int)floor((float)outSize * ((float)renderData->sampleRate/(float)renderData->inputRate));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renderData->waveform_points.size() != outSize*2) {
|
if (renderData->waveform_points.size() != outSize*2) {
|
||||||
@ -193,12 +198,10 @@ void ScopeVisualProcessor::process() {
|
|||||||
renderData->fft_floor = fft_floor_maa;
|
renderData->fft_floor = fft_floor_maa;
|
||||||
renderData->fft_ceil = fft_ceil_maa;
|
renderData->fft_ceil = fft_ceil_maa;
|
||||||
renderData->fft_size = fftSize/2;
|
renderData->fft_size = fftSize/2;
|
||||||
renderData->inputRate = audioInputData->inputRate;
|
|
||||||
renderData->sampleRate = audioInputData->sampleRate;
|
|
||||||
renderData->spectrum = true;
|
renderData->spectrum = true;
|
||||||
distribute(renderData);
|
distribute(renderData);
|
||||||
|
} else {
|
||||||
|
audioInputData->decRefCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
audioInputData->busy_update.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : lastInputBandwidth(0), last
|
|||||||
fftSize.store(0);
|
fftSize.store(0);
|
||||||
centerFreq.store(0);
|
centerFreq.store(0);
|
||||||
bandwidth.store(0);
|
bandwidth.store(0);
|
||||||
|
hideDC.store(false);
|
||||||
|
|
||||||
freqShifter = nco_crcf_create(LIQUID_NCO);
|
freqShifter = nco_crcf_create(LIQUID_NCO);
|
||||||
shiftFrequency = 0;
|
shiftFrequency = 0;
|
||||||
@ -16,6 +17,7 @@ SpectrumVisualProcessor::SpectrumVisualProcessor() : lastInputBandwidth(0), last
|
|||||||
fft_floor_ma = fft_floor_maa = 0.0;
|
fft_floor_ma = fft_floor_maa = 0.0;
|
||||||
desiredInputSize.store(0);
|
desiredInputSize.store(0);
|
||||||
fft_average_rate = 0.65;
|
fft_average_rate = 0.65;
|
||||||
|
scaleFactor.store(1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
SpectrumVisualProcessor::~SpectrumVisualProcessor() {
|
SpectrumVisualProcessor::~SpectrumVisualProcessor() {
|
||||||
@ -95,6 +97,11 @@ void SpectrumVisualProcessor::setup(int fftSize_in) {
|
|||||||
busy_run.unlock();
|
busy_run.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpectrumVisualProcessor::setHideDC(bool hideDC) {
|
||||||
|
this->hideDC.store(hideDC);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void SpectrumVisualProcessor::process() {
|
void SpectrumVisualProcessor::process() {
|
||||||
if (!isOutputEmpty()) {
|
if (!isOutputEmpty()) {
|
||||||
return;
|
return;
|
||||||
@ -301,13 +308,51 @@ void SpectrumVisualProcessor::process() {
|
|||||||
fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05;
|
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;
|
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++) {
|
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)));
|
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] = ((float) i / (float) iMax);
|
||||||
output->spectrum_points[i * 2 + 1] = v;
|
output->spectrum_points[i * 2 + 1] = v*sf;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hideDC.load()) { // DC-spike removal
|
||||||
|
long long freqMin = centerFreq-(bandwidth/2);
|
||||||
|
long long freqMax = centerFreq+(bandwidth/2);
|
||||||
|
long long zeroPt = (iqData->frequency-freqMin);
|
||||||
|
|
||||||
|
if (freqMin < iqData->frequency && freqMax > iqData->frequency) {
|
||||||
|
int freqRange = int(freqMax-freqMin);
|
||||||
|
int freqStep = freqRange/fftSize;
|
||||||
|
int fftStart = (zeroPt/freqStep)-(2000/freqStep);
|
||||||
|
int fftEnd = (zeroPt/freqStep)+(2000/freqStep);
|
||||||
|
|
||||||
|
// std::cout << "range:" << freqRange << ", step: " << freqStep << ", start: " << fftStart << ", end: " << fftEnd << std::endl;
|
||||||
|
|
||||||
|
if (fftEnd-fftStart < 2) {
|
||||||
|
fftEnd++;
|
||||||
|
fftStart--;
|
||||||
|
}
|
||||||
|
|
||||||
|
int numSteps = (fftEnd-fftStart);
|
||||||
|
int halfWay = fftStart+(numSteps/2);
|
||||||
|
|
||||||
|
if ((fftEnd+numSteps/2+1 < fftSize) && (fftStart-numSteps/2-1 >= 0) && (fftEnd > fftStart)) {
|
||||||
|
int n = 1;
|
||||||
|
for (int i = fftStart; i < halfWay; i++) {
|
||||||
|
output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftStart - n) * 2 + 1];
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
n = 1;
|
||||||
|
for (int i = halfWay; i < fftEnd; i++) {
|
||||||
|
output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftEnd + n) * 2 + 1];
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output->fft_ceiling = fft_ceil_maa;
|
output->fft_ceiling = fft_ceil_maa/sf;
|
||||||
output->fft_floor = fft_floor_maa;
|
output->fft_floor = fft_floor_maa;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,3 +364,13 @@ void SpectrumVisualProcessor::process() {
|
|||||||
busy_run.unlock();
|
busy_run.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SpectrumVisualProcessor::setScaleFactor(float sf) {
|
||||||
|
scaleFactor.store(sf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float SpectrumVisualProcessor::getScaleFactor() {
|
||||||
|
return scaleFactor.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,10 @@ public:
|
|||||||
int getDesiredInputSize();
|
int getDesiredInputSize();
|
||||||
|
|
||||||
void setup(int fftSize);
|
void setup(int fftSize);
|
||||||
|
void setHideDC(bool hideDC);
|
||||||
|
|
||||||
|
void setScaleFactor(float sf);
|
||||||
|
float getScaleFactor();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void process();
|
void process();
|
||||||
@ -68,4 +72,6 @@ private:
|
|||||||
std::vector<liquid_float_complex> resampleBuffer;
|
std::vector<liquid_float_complex> resampleBuffer;
|
||||||
std::atomic_int desiredInputSize;
|
std::atomic_int desiredInputSize;
|
||||||
std::mutex busy_run;
|
std::mutex busy_run;
|
||||||
|
std::atomic_bool hideDC;
|
||||||
|
std::atomic<float> scaleFactor;
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,163 @@
|
|||||||
#include "SDRDeviceInfo.h"
|
#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) {
|
SDRDeviceInfo::SDRDeviceInfo() : name(""), serial(""), available(false) {
|
||||||
@ -9,12 +168,20 @@ std::string SDRDeviceInfo::getDeviceId() {
|
|||||||
std::string deviceId;
|
std::string deviceId;
|
||||||
|
|
||||||
deviceId.append(getName());
|
deviceId.append(getName());
|
||||||
deviceId.append(" :: ");
|
// deviceId.append(" :: ");
|
||||||
deviceId.append(getSerial());
|
// deviceId.append(getSerial());
|
||||||
|
|
||||||
return deviceId;
|
return deviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int SDRDeviceInfo::getIndex() const {
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDRDeviceInfo::setIndex(const int index) {
|
||||||
|
this->index = index;
|
||||||
|
}
|
||||||
|
|
||||||
bool SDRDeviceInfo::isAvailable() const {
|
bool SDRDeviceInfo::isAvailable() const {
|
||||||
return available;
|
return available;
|
||||||
}
|
}
|
||||||
@ -63,3 +230,88 @@ void SDRDeviceInfo::setProduct(const std::string& product) {
|
|||||||
this->product = 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
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#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 {
|
class SDRDeviceInfo {
|
||||||
public:
|
public:
|
||||||
@ -8,6 +107,9 @@ public:
|
|||||||
|
|
||||||
std::string getDeviceId();
|
std::string getDeviceId();
|
||||||
|
|
||||||
|
const int getIndex() const;
|
||||||
|
void setIndex(const int index);
|
||||||
|
|
||||||
bool isAvailable() const;
|
bool isAvailable() const;
|
||||||
void setAvailable(bool available);
|
void setAvailable(bool available);
|
||||||
|
|
||||||
@ -25,12 +127,39 @@ public:
|
|||||||
|
|
||||||
const std::string& getProduct() const;
|
const std::string& getProduct() const;
|
||||||
void setProduct(const std::string& product);
|
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:
|
private:
|
||||||
std::string name;
|
int index;
|
||||||
std::string serial;
|
std::string name, serial, product, manufacturer, tuner;
|
||||||
std::string product;
|
std::string driver, hardware;
|
||||||
std::string manufacturer;
|
bool timestamps, available;
|
||||||
std::string tuner;
|
|
||||||
bool 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
|
||||||
|
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "done.");
|
||||||
|
std::cout << "done." << std::endl;
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, "Querying remote " + remoteAddr + " device #" + std::to_string(i));
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
wxGetApp().sdrEnumThreadNotify(SDREnumerator::SDR_ENUM_MESSAGE, std::string("Found local device #") + std::to_string(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
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 making 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 <vector>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
|
||||||
SDRPostThread::SDRPostThread() : IOThread(),
|
SDRPostThread::SDRPostThread() : IOThread() {
|
||||||
iqDataInQueue(NULL), iqDataOutQueue(NULL), iqVisualQueue(NULL), dcFilter(NULL){
|
iqDataInQueue = NULL;
|
||||||
|
iqDataOutQueue = NULL;
|
||||||
swapIQ.store(false);
|
iqVisualQueue = NULL;
|
||||||
|
|
||||||
// create a lookup table
|
numChannels = 0;
|
||||||
for (unsigned int i = 0; i <= 0xffff; i++) {
|
channelizer = NULL;
|
||||||
liquid_float_complex tmp,tmp_swap;
|
|
||||||
# if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
sampleRate = 0;
|
||||||
tmp_swap.imag = tmp.real = (float(i & 0xff) - 127.4f) * (1.0f/128.0f);
|
nRunDemods = 0;
|
||||||
tmp_swap.real = tmp.imag = (float(i >> 8) - 127.4f) * (1.0f/128.0f);
|
|
||||||
_lut.push_back(tmp);
|
visFrequency.store(0);
|
||||||
_lut_swap.push_back(tmp_swap);
|
visBandwidth.store(0);
|
||||||
#else // BIG_ENDIAN
|
|
||||||
tmp_swap.imag = tmp.real = (float(i >> 8) - 127.4f) * (1.0f/128.0f);
|
doRefresh.store(false);
|
||||||
tmp_swap.real = tmp.imag = (float(i & 0xff) - 127.4f) * (1.0f/128.0f);
|
dcFilter = iirfilt_crcf_create_dc_blocker(0.0005);
|
||||||
_lut.push_back(tmp);
|
|
||||||
_lut_swap.push_back(tmp_swap);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SDRPostThread::~SDRPostThread() {
|
SDRPostThread::~SDRPostThread() {
|
||||||
@ -33,6 +29,7 @@ SDRPostThread::~SDRPostThread() {
|
|||||||
void SDRPostThread::bindDemodulator(DemodulatorInstance *demod) {
|
void SDRPostThread::bindDemodulator(DemodulatorInstance *demod) {
|
||||||
busy_demod.lock();
|
busy_demod.lock();
|
||||||
demodulators.push_back(demod);
|
demodulators.push_back(demod);
|
||||||
|
doRefresh.store(true);
|
||||||
busy_demod.unlock();
|
busy_demod.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,39 +43,121 @@ void SDRPostThread::removeDemodulator(DemodulatorInstance *demod) {
|
|||||||
|
|
||||||
if (i != demodulators.end()) {
|
if (i != demodulators.end()) {
|
||||||
demodulators.erase(i);
|
demodulators.erase(i);
|
||||||
|
doRefresh.store(true);
|
||||||
}
|
}
|
||||||
busy_demod.unlock();
|
busy_demod.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SDRPostThread::setSwapIQ(bool swapIQ) {
|
void SDRPostThread::initPFBChannelizer() {
|
||||||
this->swapIQ.store(swapIQ);
|
// 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() {
|
void SDRPostThread::updateActiveDemodulators() {
|
||||||
return this->swapIQ.load();
|
// 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() {
|
void SDRPostThread::run() {
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
pthread_t tID = pthread_self(); // ID of this thread
|
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
|
sched_param prio = {priority}; // scheduling priority of thread
|
||||||
pthread_setschedparam(tID, SCHED_FIFO, &prio);
|
pthread_setschedparam(tID, SCHED_FIFO, &prio);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
dcFilter = iirfilt_crcf_create_dc_blocker(0.0005);
|
|
||||||
|
|
||||||
std::cout << "SDR post-processing thread started.." << std::endl;
|
std::cout << "SDR post-processing thread started.." << std::endl;
|
||||||
|
|
||||||
iqDataInQueue = (SDRThreadIQDataQueue*)getInputQueue("IQDataInput");
|
iqDataInQueue = (SDRThreadIQDataQueue*)getInputQueue("IQDataInput");
|
||||||
iqDataOutQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQDataOutput");
|
iqDataOutQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQDataOutput");
|
||||||
iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOutput");
|
iqVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQVisualDataOutput");
|
||||||
|
iqActiveDemodVisualQueue = (DemodulatorThreadInputQueue*)getOutputQueue("IQActiveDemodVisualDataOutput");
|
||||||
ReBuffer<DemodulatorThreadIQData> buffers;
|
|
||||||
std::vector<liquid_float_complex> fpData;
|
|
||||||
std::vector<liquid_float_complex> dataOut;
|
|
||||||
|
|
||||||
iqDataInQueue->set_max_num_items(0);
|
iqDataInQueue->set_max_num_items(0);
|
||||||
|
|
||||||
|
std::vector<liquid_float_complex> dcBuf;
|
||||||
|
|
||||||
while (!terminated) {
|
while (!terminated) {
|
||||||
SDRThreadIQData *data_in;
|
SDRThreadIQData *data_in;
|
||||||
@ -86,125 +165,185 @@ void SDRPostThread::run() {
|
|||||||
iqDataInQueue->pop(data_in);
|
iqDataInQueue->pop(data_in);
|
||||||
// std::lock_guard < std::mutex > lock(data_in->m_mutex);
|
// std::lock_guard < std::mutex > lock(data_in->m_mutex);
|
||||||
|
|
||||||
if (data_in && data_in->data.size()) {
|
if (data_in && data_in->data.size() && data_in->numChannels) {
|
||||||
int dataSize = data_in->data.size()/2;
|
if (numChannels != data_in->numChannels || sampleRate != data_in->sampleRate) {
|
||||||
if (dataSize > fpData.capacity()) {
|
numChannels = data_in->numChannels;
|
||||||
fpData.reserve(dataSize);
|
sampleRate = data_in->sampleRate;
|
||||||
dataOut.reserve(dataSize);
|
initPFBChannelizer();
|
||||||
}
|
doRefresh.store(true);
|
||||||
if (dataSize != fpData.size()) {
|
|
||||||
fpData.resize(dataSize);
|
|
||||||
dataOut.resize(dataSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (swapIQ) {
|
int dataSize = data_in->data.size();
|
||||||
for (int i = 0; i < dataSize; i++) {
|
int outSize = data_in->data.size();
|
||||||
fpData[i] = _lut_swap[*((uint16_t*)&data_in->data[2*i])];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < dataSize; i++) {
|
|
||||||
fpData[i] = _lut[*((uint16_t*)&data_in->data[2*i])];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
iirfilt_crcf_execute_block(dcFilter, &fpData[0], dataSize, &dataOut[0]);
|
|
||||||
|
|
||||||
if (iqVisualQueue != NULL && !iqVisualQueue->full()) {
|
|
||||||
DemodulatorThreadIQData *visualDataOut = visualDataBuffers.getBuffer();
|
|
||||||
visualDataOut->setRefCount(1);
|
|
||||||
|
|
||||||
int num_vis_samples = dataOut.size();
|
|
||||||
|
|
||||||
// if (visualDataOut->data.size() < num_vis_samples) {
|
if (outSize > dataOut.capacity()) {
|
||||||
// if (visualDataOut->data.capacity() < num_vis_samples) {
|
dataOut.reserve(outSize);
|
||||||
// visualDataOut->data.reserve(num_vis_samples);
|
|
||||||
// }
|
|
||||||
// visualDataOut->data.resize(num_vis_samples);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
visualDataOut->frequency = data_in->frequency;
|
|
||||||
visualDataOut->sampleRate = data_in->sampleRate;
|
|
||||||
visualDataOut->data.assign(dataOut.begin(), dataOut.begin() + num_vis_samples);
|
|
||||||
|
|
||||||
iqVisualQueue->push(visualDataOut);
|
|
||||||
}
|
}
|
||||||
|
if (outSize != dataOut.size()) {
|
||||||
|
dataOut.resize(outSize);
|
||||||
|
}
|
||||||
|
int activeVisChannel = -1;
|
||||||
|
|
||||||
|
// if (visBandwidth.load() && visBandwidth.load() < (chanBw/2)) {
|
||||||
|
// activeVisChannel = getChannelAt(visFrequency);
|
||||||
|
// }
|
||||||
|
|
||||||
|
if (iqDataOutQueue != NULL && !iqDataOutQueue->full() && activeVisChannel < 0) {
|
||||||
|
DemodulatorThreadIQData *iqDataOut = visualDataBuffers.getBuffer();
|
||||||
|
|
||||||
|
bool doVis = false;
|
||||||
|
|
||||||
|
if (iqVisualQueue != NULL && !iqVisualQueue->full()) {
|
||||||
|
doVis = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
iqDataOut->setRefCount(1 + (doVis?1:0));
|
||||||
|
|
||||||
|
iqDataOut->frequency = data_in->frequency;
|
||||||
|
iqDataOut->sampleRate = data_in->sampleRate;
|
||||||
|
iqDataOut->data.assign(data_in->data.begin(), data_in->data.begin() + dataSize);
|
||||||
|
|
||||||
|
iqDataOutQueue->push(iqDataOut);
|
||||||
|
if (doVis) {
|
||||||
|
iqVisualQueue->push(iqDataOut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
busy_demod.lock();
|
busy_demod.lock();
|
||||||
|
|
||||||
int activeDemods = 0;
|
if (frequency != data_in->frequency) {
|
||||||
bool pushedData = false;
|
frequency = data_in->frequency;
|
||||||
|
doRefresh.store(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (doRefresh.load()) {
|
||||||
|
updateActiveDemodulators();
|
||||||
|
updateChannels();
|
||||||
|
doRefresh.store(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
DemodulatorInstance *activeDemod = wxGetApp().getDemodMgr().getLastActiveDemodulator();
|
||||||
|
int activeDemodChannel = -1;
|
||||||
|
|
||||||
if (demodulators.size() || iqDataOutQueue != NULL) {
|
// Find active demodulators
|
||||||
std::vector<DemodulatorInstance *>::iterator demod_i;
|
if (nRunDemods || (activeVisChannel >= 0)) {
|
||||||
for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) {
|
|
||||||
DemodulatorInstance *demod = *demod_i;
|
// for (int i = 0; i < numChannels; i++) {
|
||||||
if (demod->getFrequency() != data_in->frequency
|
// firpfbch_crcf_set_channel_state(channelizer, i, (demodChannelActive[i]>0)?1:0);
|
||||||
&& abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) {
|
// }
|
||||||
|
|
||||||
|
// 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;
|
continue;
|
||||||
}
|
}
|
||||||
activeDemods++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iqDataOutQueue != NULL) {
|
|
||||||
activeDemods++;
|
|
||||||
}
|
|
||||||
|
|
||||||
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
|
|
||||||
|
|
||||||
// std::lock_guard < std::mutex > lock(demodDataOut->m_mutex);
|
|
||||||
demodDataOut->frequency = data_in->frequency;
|
|
||||||
demodDataOut->sampleRate = data_in->sampleRate;
|
|
||||||
demodDataOut->setRefCount(activeDemods);
|
|
||||||
demodDataOut->data.assign(dataOut.begin(), dataOut.end());
|
|
||||||
|
|
||||||
for (demod_i = demodulators.begin(); demod_i != demodulators.end(); demod_i++) {
|
|
||||||
DemodulatorInstance *demod = *demod_i;
|
|
||||||
DemodulatorThreadInputQueue *demodQueue = demod->getIQInputDataPipe();
|
|
||||||
|
|
||||||
if (abs(data_in->frequency - demod->getFrequency()) > (wxGetApp().getSampleRate() / 2)) {
|
DemodulatorThreadIQData *demodDataOut = buffers.getBuffer();
|
||||||
if (demod->isActive() && !demod->isFollow() && !demod->isTracking()) {
|
demodDataOut->setRefCount(demodChannelActive[i] + doVis + doDemodVis);
|
||||||
demod->setActive(false);
|
demodDataOut->frequency = chanCenters[i];
|
||||||
DemodulatorThreadIQData *dummyDataOut = new DemodulatorThreadIQData;
|
demodDataOut->sampleRate = chanBw;
|
||||||
dummyDataOut->frequency = data_in->frequency;
|
|
||||||
dummyDataOut->sampleRate = data_in->sampleRate;
|
// Calculate channel buffer size
|
||||||
demodQueue->push(dummyDataOut);
|
int chanDataSize = (outSize/numChannels);
|
||||||
}
|
|
||||||
|
if (demodDataOut->data.size() != chanDataSize) {
|
||||||
if (demod->isFollow() && wxGetApp().getFrequency() != demod->getFrequency()) {
|
if (demodDataOut->data.capacity() < chanDataSize) {
|
||||||
wxGetApp().setFrequency(demod->getFrequency());
|
demodDataOut->data.reserve(chanDataSize);
|
||||||
}
|
|
||||||
} else if (!demod->isActive()) {
|
|
||||||
demod->setActive(true);
|
|
||||||
if (wxGetApp().getDemodMgr().getLastActiveDemodulator() == NULL) {
|
|
||||||
wxGetApp().getDemodMgr().setActiveDemodulator(demod);
|
|
||||||
}
|
}
|
||||||
|
demodDataOut->data.resize(chanDataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
int idx = i;
|
||||||
|
|
||||||
|
// Extra channel wraps lower side band of lowest channel
|
||||||
|
// to fix frequency gap on upper side of spectrum
|
||||||
|
if (i == numChannels) {
|
||||||
|
idx = (numChannels/2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!demod->isActive()) {
|
// prepare channel data buffer
|
||||||
continue;
|
if (i == 0) { // Channel 0 requires DC correction
|
||||||
}
|
if (dcBuf.size() != chanDataSize) {
|
||||||
if (demod->isFollow()) {
|
dcBuf.resize(chanDataSize);
|
||||||
demod->setFollow(false);
|
}
|
||||||
}
|
for (int j = 0; j < chanDataSize; j++) {
|
||||||
|
dcBuf[j] = dataOut[idx];
|
||||||
demodQueue->push(demodDataOut);
|
idx += numChannels;
|
||||||
pushedData = true;
|
}
|
||||||
}
|
iirfilt_crcf_execute_block(dcFilter, &dcBuf[0], chanDataSize, &demodDataOut->data[0]);
|
||||||
|
|
||||||
if (iqDataOutQueue != NULL) {
|
|
||||||
if (!iqDataOutQueue->full()) {
|
|
||||||
iqDataOutQueue->push(demodDataOut);
|
|
||||||
pushedData = true;
|
|
||||||
} else {
|
} 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();
|
busy_demod.unlock();
|
||||||
}
|
}
|
||||||
data_in->decRefCount();
|
data_in->decRefCount();
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#if USE_RTL_SDR
|
||||||
#include "SDRThread.h"
|
#include "SDRThread.h"
|
||||||
|
#else
|
||||||
|
#include "SoapySDRThread.h"
|
||||||
|
#endif
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
class SDRPostThread : public IOThread {
|
class SDRPostThread : public IOThread {
|
||||||
@ -10,26 +14,44 @@ public:
|
|||||||
|
|
||||||
void bindDemodulator(DemodulatorInstance *demod);
|
void bindDemodulator(DemodulatorInstance *demod);
|
||||||
void removeDemodulator(DemodulatorInstance *demod);
|
void removeDemodulator(DemodulatorInstance *demod);
|
||||||
|
|
||||||
void setSwapIQ(bool swapIQ);
|
|
||||||
bool getSwapIQ();
|
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
|
void setIQVisualRange(long long frequency, int bandwidth);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SDRThreadIQDataQueue *iqDataInQueue;
|
SDRThreadIQDataQueue *iqDataInQueue;
|
||||||
DemodulatorThreadInputQueue *iqDataOutQueue;
|
DemodulatorThreadInputQueue *iqDataOutQueue;
|
||||||
DemodulatorThreadInputQueue *iqVisualQueue;
|
DemodulatorThreadInputQueue *iqVisualQueue;
|
||||||
|
DemodulatorThreadInputQueue *iqActiveDemodVisualQueue;
|
||||||
|
|
||||||
std::mutex busy_demod;
|
std::mutex busy_demod;
|
||||||
std::vector<DemodulatorInstance *> demodulators;
|
std::vector<DemodulatorInstance *> demodulators;
|
||||||
iirfilt_crcf dcFilter;
|
|
||||||
std::atomic_bool swapIQ;
|
|
||||||
ReBuffer<DemodulatorThreadIQData> visualDataBuffers;
|
|
||||||
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<liquid_float_complex> _lut;
|
void initPFBChannelizer();
|
||||||
std::vector<liquid_float_complex> _lut_swap;
|
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;
|
||||||
};
|
};
|
||||||
|
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;
|
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[0] = 0.0f;
|
||||||
pos[1] = 0.0f;
|
pos[1] = 0.0f;
|
||||||
rot[0] = 0.0f;
|
rot[0] = 0.0f;
|
||||||
@ -19,6 +19,8 @@ GLPanel::GLPanel() : fillType(GLPANEL_FILL_SOLID), contentsVisible(true), transf
|
|||||||
setCoordinateSystem(GLPANEL_Y_UP);
|
setCoordinateSystem(GLPANEL_Y_UP);
|
||||||
setMarginPx(0);
|
setMarginPx(0);
|
||||||
setBorderPx(0);
|
setBorderPx(0);
|
||||||
|
srcBlend = GL_SRC_ALPHA;
|
||||||
|
dstBlend = GL_ONE_MINUS_SRC_ALPHA;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLPanel::genArrays() {
|
void GLPanel::genArrays() {
|
||||||
@ -174,6 +176,19 @@ void GLPanel::setCoordinateSystem(GLPanelCoordinateSystem coord_in) {
|
|||||||
genArrays();
|
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) {
|
void GLPanel::setFill(GLPanelFillType fill_mode) {
|
||||||
fillType = fill_mode;
|
fillType = fill_mode;
|
||||||
genArrays();
|
genArrays();
|
||||||
@ -210,6 +225,11 @@ void GLPanel::setBorderPx(float bordl, float bordr, float bordt, float bordb) {
|
|||||||
borderPx.bottom = bordb;
|
borderPx.bottom = bordb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GLPanel::setBlend(GLuint src, GLuint dst) {
|
||||||
|
srcBlend = src;
|
||||||
|
dstBlend = dst;
|
||||||
|
}
|
||||||
|
|
||||||
void GLPanel::addChild(GLPanel *childPanel) {
|
void GLPanel::addChild(GLPanel *childPanel) {
|
||||||
std::vector<GLPanel *>::iterator i = std::find(children.begin(), children.end(), 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) {
|
if (marginPx) {
|
||||||
transform *= mat4::scale(1.0 - marginPx * 2.0 * pvec.x / size[0], 1.0 - marginPx * 2.0 * pvec.y / size[1], 1);
|
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() {
|
void GLPanel::draw() {
|
||||||
@ -285,9 +307,9 @@ void GLPanel::draw() {
|
|||||||
|
|
||||||
glLoadMatrixf(transform);
|
glLoadMatrixf(transform);
|
||||||
|
|
||||||
if (fillType != GLPANEL_FILL_NONE) {
|
if (fillType != GLPANEL_FILL_NONE && visible) {
|
||||||
glEnable(GL_BLEND);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(srcBlend, dstBlend);
|
||||||
glEnableClientState(GL_VERTEX_ARRAY);
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
glEnableClientState(GL_COLOR_ARRAY);
|
glEnableClientState(GL_COLOR_ARRAY);
|
||||||
glVertexPointer(2, GL_FLOAT, 0, &glPoints[0]);
|
glVertexPointer(2, GL_FLOAT, 0, &glPoints[0]);
|
||||||
|
@ -45,8 +45,8 @@ public:
|
|||||||
GLPanelEdges borderPx;
|
GLPanelEdges borderPx;
|
||||||
RGBA4f fill[2];
|
RGBA4f fill[2];
|
||||||
RGBA4f borderColor;
|
RGBA4f borderColor;
|
||||||
bool contentsVisible;
|
bool contentsVisible, visible;
|
||||||
CubicVR::mat4 transform;
|
CubicVR::mat4 transform, transformInverse;
|
||||||
CubicVR::mat4 localTransform;
|
CubicVR::mat4 localTransform;
|
||||||
float min, mid, max;
|
float min, mid, max;
|
||||||
// screen dimensions
|
// screen dimensions
|
||||||
@ -55,7 +55,8 @@ public:
|
|||||||
CubicVR::vec2 umin, umax, ucenter;
|
CubicVR::vec2 umin, umax, ucenter;
|
||||||
// pixel dimensions
|
// pixel dimensions
|
||||||
CubicVR::vec2 pdim, pvec;
|
CubicVR::vec2 pdim, pvec;
|
||||||
|
GLuint srcBlend, dstBlend;
|
||||||
|
|
||||||
std::vector<GLPanel *> children;
|
std::vector<GLPanel *> children;
|
||||||
|
|
||||||
GLPanel();
|
GLPanel();
|
||||||
@ -68,6 +69,8 @@ public:
|
|||||||
float getHeightPx();
|
float getHeightPx();
|
||||||
void setCoordinateSystem(GLPanelCoordinateSystem coord);
|
void setCoordinateSystem(GLPanelCoordinateSystem coord);
|
||||||
|
|
||||||
|
bool hitTest(CubicVR::vec2 pos, CubicVR::vec2 &result);
|
||||||
|
|
||||||
void setFill(GLPanelFillType fill_mode);
|
void setFill(GLPanelFillType fill_mode);
|
||||||
void setFillColor(RGBA4f color1);
|
void setFillColor(RGBA4f color1);
|
||||||
void setFillColor(RGBA4f color1, RGBA4f color2);
|
void setFillColor(RGBA4f color1, RGBA4f color2);
|
||||||
@ -77,6 +80,8 @@ public:
|
|||||||
void setBorderPx(float bord);
|
void setBorderPx(float bord);
|
||||||
void setBorderPx(float bordl, float bordr, float bordt, float bordb);
|
void setBorderPx(float bordl, float bordr, float bordt, float bordb);
|
||||||
|
|
||||||
|
void setBlend(GLuint src, GLuint dst);
|
||||||
|
|
||||||
void addChild(GLPanel *childPanel);
|
void addChild(GLPanel *childPanel);
|
||||||
void removeChild(GLPanel *childPanel);
|
void removeChild(GLPanel *childPanel);
|
||||||
|
|
||||||
|
@ -35,10 +35,10 @@ void initGLExtensions() {
|
|||||||
// const GLubyte *extensions = glGetString(GL_EXTENSIONS);
|
// const GLubyte *extensions = glGetString(GL_EXTENSIONS);
|
||||||
// std::cout << std::endl << "Supported GL Extensions: " << std::endl << extensions << std::endl << std::endl;
|
// std::cout << std::endl << "Supported GL Extensions: " << std::endl << extensions << std::endl << std::endl;
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __APPLE__
|
||||||
const GLint interval = 2;
|
|
||||||
#else
|
|
||||||
const GLint interval = 1;
|
const GLint interval = 1;
|
||||||
|
#else
|
||||||
|
const GLint interval = 2;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#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) :
|
InteractiveCanvas::InteractiveCanvas(wxWindow *parent, int *attribList) :
|
||||||
wxGLCanvas(parent, wxID_ANY, attribList, wxDefaultPosition, wxDefaultSize,
|
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) {
|
false) {
|
||||||
mouseTracker.setTarget(this);
|
mouseTracker.setTarget(this);
|
||||||
}
|
}
|
||||||
@ -150,11 +150,11 @@ void InteractiveCanvas::OnMouseEnterWindow(wxMouseEvent& event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void InteractiveCanvas::setStatusText(std::string statusText) {
|
void InteractiveCanvas::setStatusText(std::string statusText) {
|
||||||
((wxFrame*) parent)->GetStatusBar()->SetStatusText(statusText);
|
wxGetApp().getAppFrame()->GetStatusBar()->SetStatusText(statusText);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InteractiveCanvas::setStatusText(std::string statusText, int value) {
|
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)));
|
wxString::Format(statusText.c_str(), wxNumberFormatter::ToString((long) value, wxNumberFormatter::Style_WithThousandsSep)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,8 +13,8 @@ public:
|
|||||||
|
|
||||||
long long getFrequencyAt(float x);
|
long long getFrequencyAt(float x);
|
||||||
|
|
||||||
void setView(long long center_freq_in, int bandwidth_in);
|
virtual void setView(long long center_freq_in, int bandwidth_in);
|
||||||
void disableView();
|
virtual void disableView();
|
||||||
bool getViewState();
|
bool getViewState();
|
||||||
|
|
||||||
void setCenterFrequency(long long center_freq_in);
|
void setCenterFrequency(long long center_freq_in);
|
||||||
|
@ -25,6 +25,8 @@ EVT_LEFT_UP(SpectrumCanvas::OnMouseReleased)
|
|||||||
EVT_ENTER_WINDOW(SpectrumCanvas::OnMouseEnterWindow)
|
EVT_ENTER_WINDOW(SpectrumCanvas::OnMouseEnterWindow)
|
||||||
EVT_LEAVE_WINDOW(SpectrumCanvas::OnMouseLeftWindow)
|
EVT_LEAVE_WINDOW(SpectrumCanvas::OnMouseLeftWindow)
|
||||||
EVT_MOUSEWHEEL(SpectrumCanvas::OnMouseWheelMoved)
|
EVT_MOUSEWHEEL(SpectrumCanvas::OnMouseWheelMoved)
|
||||||
|
EVT_RIGHT_DOWN(SpectrumCanvas::OnMouseRightDown)
|
||||||
|
EVT_RIGHT_UP(SpectrumCanvas::OnMouseRightReleased)
|
||||||
wxEND_EVENT_TABLE()
|
wxEND_EVENT_TABLE()
|
||||||
|
|
||||||
SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) :
|
SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) :
|
||||||
@ -32,10 +34,13 @@ SpectrumCanvas::SpectrumCanvas(wxWindow *parent, int *attribList) :
|
|||||||
|
|
||||||
glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this));
|
glContext = new PrimaryGLContext(this, &wxGetApp().GetContext(this));
|
||||||
|
|
||||||
mouseTracker.setVertDragLock(true);
|
|
||||||
visualDataQueue.set_max_num_items(1);
|
visualDataQueue.set_max_num_items(1);
|
||||||
|
|
||||||
SetCursor(wxCURSOR_SIZEWE);
|
SetCursor(wxCURSOR_SIZEWE);
|
||||||
|
scaleFactor = 1.0;
|
||||||
|
resetScaleFactor = false;
|
||||||
|
scaleFactorEnabled = false;
|
||||||
|
bwChange = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpectrumCanvas::~SpectrumCanvas() {
|
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);
|
glContext->SetCurrent(*this);
|
||||||
initGLExtensions();
|
initGLExtensions();
|
||||||
@ -137,6 +151,34 @@ bool SpectrumCanvas::getShowDb() {
|
|||||||
return spectrumPanel.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) {
|
void SpectrumCanvas::OnMouseMoved(wxMouseEvent& event) {
|
||||||
InteractiveCanvas::OnMouseMoved(event);
|
InteractiveCanvas::OnMouseMoved(event);
|
||||||
@ -146,12 +188,32 @@ void SpectrumCanvas::OnMouseMoved(wxMouseEvent& event) {
|
|||||||
if (freqChange != 0) {
|
if (freqChange != 0) {
|
||||||
moveCenterFrequency(freqChange);
|
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 {
|
} 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) {
|
void SpectrumCanvas::OnMouseDown(wxMouseEvent& event) {
|
||||||
|
mouseTracker.setVertDragLock(true);
|
||||||
InteractiveCanvas::OnMouseDown(event);
|
InteractiveCanvas::OnMouseDown(event);
|
||||||
SetCursor(wxCURSOR_CROSS);
|
SetCursor(wxCURSOR_CROSS);
|
||||||
}
|
}
|
||||||
@ -161,7 +223,8 @@ void SpectrumCanvas::OnMouseWheelMoved(wxMouseEvent& event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SpectrumCanvas::OnMouseReleased(wxMouseEvent& event) {
|
void SpectrumCanvas::OnMouseReleased(wxMouseEvent& event) {
|
||||||
InteractiveCanvas::OnMouseReleased(event);
|
mouseTracker.setVertDragLock(false);
|
||||||
|
InteractiveCanvas::OnMouseReleased(event);
|
||||||
SetCursor(wxCURSOR_SIZEWE);
|
SetCursor(wxCURSOR_SIZEWE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,3 +245,17 @@ void SpectrumCanvas::attachWaterfallCanvas(WaterfallCanvas* canvas_in) {
|
|||||||
SpectrumVisualDataQueue *SpectrumCanvas::getVisualDataQueue() {
|
SpectrumVisualDataQueue *SpectrumCanvas::getVisualDataQueue() {
|
||||||
return &visualDataQueue;
|
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);
|
void setShowDb(bool showDb);
|
||||||
bool getShowDb();
|
bool getShowDb();
|
||||||
|
|
||||||
|
void setView(long long center_freq_in, int bandwidth_in);
|
||||||
|
void disableView();
|
||||||
|
|
||||||
|
void setScaleFactorEnabled(bool en);
|
||||||
|
|
||||||
SpectrumVisualDataQueue *getVisualDataQueue();
|
SpectrumVisualDataQueue *getVisualDataQueue();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -35,10 +40,17 @@ private:
|
|||||||
void OnMouseReleased(wxMouseEvent& event);
|
void OnMouseReleased(wxMouseEvent& event);
|
||||||
void OnMouseEnterWindow(wxMouseEvent& event);
|
void OnMouseEnterWindow(wxMouseEvent& event);
|
||||||
void OnMouseLeftWindow(wxMouseEvent& event);
|
void OnMouseLeftWindow(wxMouseEvent& event);
|
||||||
|
void OnMouseRightDown(wxMouseEvent& event);
|
||||||
|
void OnMouseRightReleased(wxMouseEvent& event);
|
||||||
|
|
||||||
|
void updateScaleFactor(float factor);
|
||||||
|
|
||||||
PrimaryGLContext *glContext;
|
PrimaryGLContext *glContext;
|
||||||
WaterfallCanvas *waterfallCanvas;
|
WaterfallCanvas *waterfallCanvas;
|
||||||
SpectrumPanel spectrumPanel;
|
SpectrumPanel spectrumPanel;
|
||||||
|
float scaleFactor;
|
||||||
|
int bwChange;
|
||||||
|
bool resetScaleFactor, scaleFactorEnabled;
|
||||||
|
|
||||||
SpectrumVisualDataQueue visualDataQueue;
|
SpectrumVisualDataQueue visualDataQueue;
|
||||||
|
|
||||||
|
@ -190,8 +190,8 @@ void TuningCanvas::StepTuner(ActiveState state, int exponent, bool up) {
|
|||||||
bw += amount;
|
bw += amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bw > wxGetApp().getSampleRate()) {
|
if (bw > CHANNELIZER_RATE_MAX) {
|
||||||
bw = wxGetApp().getSampleRate();
|
bw = CHANNELIZER_RATE_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
wxGetApp().getDemodMgr().setLastBandwidth(bw);
|
wxGetApp().getDemodMgr().setLastBandwidth(bw);
|
||||||
|
@ -43,6 +43,7 @@ WaterfallCanvas::WaterfallCanvas(wxWindow *parent, int *attribList) :
|
|||||||
lpsIndex = 0;
|
lpsIndex = 0;
|
||||||
preBuf = false;
|
preBuf = false;
|
||||||
SetCursor(wxCURSOR_CROSS);
|
SetCursor(wxCURSOR_CROSS);
|
||||||
|
scaleMove = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
WaterfallCanvas::~WaterfallCanvas() {
|
WaterfallCanvas::~WaterfallCanvas() {
|
||||||
@ -72,19 +73,16 @@ void WaterfallCanvas::attachSpectrumCanvas(SpectrumCanvas *canvas_in) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::processInputQueue() {
|
void WaterfallCanvas::processInputQueue() {
|
||||||
if (!glContext) {
|
tex_update.lock();
|
||||||
return;
|
|
||||||
}
|
|
||||||
glContext->SetCurrent(*this);
|
|
||||||
|
|
||||||
gTimer.update();
|
gTimer.update();
|
||||||
|
|
||||||
double targetVis = 1.0 / (double)linesPerSecond;
|
double targetVis = 1.0 / (double)linesPerSecond;
|
||||||
lpsIndex += gTimer.lastUpdateSeconds();
|
lpsIndex += gTimer.lastUpdateSeconds();
|
||||||
|
|
||||||
|
bool updated = false;
|
||||||
if (linesPerSecond) {
|
if (linesPerSecond) {
|
||||||
if (lpsIndex >= targetVis) {
|
if (lpsIndex >= targetVis) {
|
||||||
tex_update.lock();
|
|
||||||
while (lpsIndex >= targetVis) {
|
while (lpsIndex >= targetVis) {
|
||||||
SpectrumVisualData *vData;
|
SpectrumVisualData *vData;
|
||||||
if (!visualDataQueue.empty()) {
|
if (!visualDataQueue.empty()) {
|
||||||
@ -94,21 +92,27 @@ void WaterfallCanvas::processInputQueue() {
|
|||||||
waterfallPanel.setPoints(vData->spectrum_points);
|
waterfallPanel.setPoints(vData->spectrum_points);
|
||||||
waterfallPanel.step();
|
waterfallPanel.step();
|
||||||
vData->decRefCount();
|
vData->decRefCount();
|
||||||
|
updated = true;
|
||||||
}
|
}
|
||||||
lpsIndex-=targetVis;
|
lpsIndex-=targetVis;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tex_update.unlock();
|
|
||||||
}
|
}
|
||||||
}}
|
}
|
||||||
|
if (updated) {
|
||||||
|
wxClientDC(this);
|
||||||
|
glContext->SetCurrent(*this);
|
||||||
|
waterfallPanel.update();
|
||||||
|
}
|
||||||
|
tex_update.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
||||||
|
tex_update.lock();
|
||||||
wxPaintDC dc(this);
|
wxPaintDC dc(this);
|
||||||
|
|
||||||
processInputQueue();
|
|
||||||
|
|
||||||
const wxSize ClientSize = GetClientSize();
|
const wxSize ClientSize = GetClientSize();
|
||||||
long double currentZoom = zoom;
|
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) {
|
if (freqMove != 0.0) {
|
||||||
long long newFreq = getCenterFrequency() + (long long)((long double)getBandwidth()*freqMove) * 0.01;
|
long long newFreq = getCenterFrequency() + (long long)((long double)getBandwidth()*freqMove) * 0.01;
|
||||||
|
|
||||||
|
long long minFreq = bandwidth/2;
|
||||||
|
if (newFreq < minFreq) {
|
||||||
|
newFreq = minFreq;
|
||||||
|
}
|
||||||
|
|
||||||
updateCenterFrequency(newFreq);
|
updateCenterFrequency(newFreq);
|
||||||
|
|
||||||
if (!freqMoving) {
|
if (!freqMoving) {
|
||||||
@ -211,9 +239,7 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
|||||||
glContext->BeginDraw(0,0,0);
|
glContext->BeginDraw(0,0,0);
|
||||||
|
|
||||||
waterfallPanel.calcTransform(CubicVR::mat4::identity());
|
waterfallPanel.calcTransform(CubicVR::mat4::identity());
|
||||||
tex_update.lock();
|
|
||||||
waterfallPanel.draw();
|
waterfallPanel.draw();
|
||||||
tex_update.unlock();
|
|
||||||
|
|
||||||
std::vector<DemodulatorInstance *> &demods = wxGetApp().getDemodMgr().getDemodulators();
|
std::vector<DemodulatorInstance *> &demods = wxGetApp().getDemodMgr().getDemodulators();
|
||||||
|
|
||||||
@ -293,6 +319,7 @@ void WaterfallCanvas::OnPaint(wxPaintEvent& WXUNUSED(event)) {
|
|||||||
glContext->EndDraw();
|
glContext->EndDraw();
|
||||||
|
|
||||||
SwapBuffers();
|
SwapBuffers();
|
||||||
|
tex_update.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) {
|
void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) {
|
||||||
@ -304,14 +331,20 @@ void WaterfallCanvas::OnKeyUp(wxKeyEvent& event) {
|
|||||||
case 'A':
|
case 'A':
|
||||||
case WXK_UP:
|
case WXK_UP:
|
||||||
case WXK_NUMPAD_UP:
|
case WXK_NUMPAD_UP:
|
||||||
|
scaleMove = 0.0;
|
||||||
zoom = 1.0;
|
zoom = 1.0;
|
||||||
mouseZoom = 0.95;
|
if (mouseZoom != 1.0) {
|
||||||
|
mouseZoom = 0.95;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'Z':
|
case 'Z':
|
||||||
case WXK_DOWN:
|
case WXK_DOWN:
|
||||||
case WXK_NUMPAD_DOWN:
|
case WXK_NUMPAD_DOWN:
|
||||||
|
scaleMove = 0.0;
|
||||||
zoom = 1.0;
|
zoom = 1.0;
|
||||||
mouseZoom = 1.05;
|
if (mouseZoom != 1.0) {
|
||||||
|
mouseZoom = 1.05;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case WXK_LEFT:
|
case WXK_LEFT:
|
||||||
case WXK_NUMPAD_LEFT:
|
case WXK_NUMPAD_LEFT:
|
||||||
@ -334,14 +367,22 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
|
|||||||
case 'A':
|
case 'A':
|
||||||
case WXK_UP:
|
case WXK_UP:
|
||||||
case WXK_NUMPAD_UP:
|
case WXK_NUMPAD_UP:
|
||||||
mouseZoom = 1.0;
|
if (!shiftDown) {
|
||||||
zoom = 0.95;
|
mouseZoom = 1.0;
|
||||||
|
zoom = 0.95;
|
||||||
|
} else {
|
||||||
|
scaleMove = 1.0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'Z':
|
case 'Z':
|
||||||
case WXK_DOWN:
|
case WXK_DOWN:
|
||||||
case WXK_NUMPAD_DOWN:
|
case WXK_NUMPAD_DOWN:
|
||||||
mouseZoom = 1.0;
|
if (!shiftDown) {
|
||||||
zoom = 1.05;
|
mouseZoom = 1.0;
|
||||||
|
zoom = 1.05;
|
||||||
|
} else {
|
||||||
|
scaleMove = -1.0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case WXK_RIGHT:
|
case WXK_RIGHT:
|
||||||
case WXK_NUMPAD_RIGHT:
|
case WXK_NUMPAD_RIGHT:
|
||||||
@ -398,7 +439,7 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
long long minFreq = wxGetApp().getSampleRate()/2;
|
long long minFreq = bandwidth/2;
|
||||||
if (freq < minFreq) {
|
if (freq < minFreq) {
|
||||||
freq = minFreq;
|
freq = minFreq;
|
||||||
}
|
}
|
||||||
@ -409,11 +450,9 @@ void WaterfallCanvas::OnKeyDown(wxKeyEvent& event) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
void WaterfallCanvas::OnIdle(wxIdleEvent &event) {
|
void WaterfallCanvas::OnIdle(wxIdleEvent &event) {
|
||||||
|
processInputQueue();
|
||||||
Refresh();
|
Refresh();
|
||||||
event.RequestMore();
|
event.RequestMore();
|
||||||
if (visualDataQueue.size() > linesPerSecond) {
|
|
||||||
processInputQueue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) {
|
void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) {
|
||||||
@ -435,8 +474,8 @@ void WaterfallCanvas::OnMouseMoved(wxMouseEvent& event) {
|
|||||||
int currentBW = demod->getBandwidth();
|
int currentBW = demod->getBandwidth();
|
||||||
|
|
||||||
currentBW = currentBW + bwDiff;
|
currentBW = currentBW + bwDiff;
|
||||||
if (currentBW > wxGetApp().getSampleRate()) {
|
if (currentBW > CHANNELIZER_RATE_MAX) {
|
||||||
currentBW = wxGetApp().getSampleRate();
|
currentBW = CHANNELIZER_RATE_MAX;
|
||||||
}
|
}
|
||||||
if (currentBW < MIN_BANDWIDTH) {
|
if (currentBW < MIN_BANDWIDTH) {
|
||||||
currentBW = MIN_BANDWIDTH;
|
currentBW = MIN_BANDWIDTH;
|
||||||
@ -802,6 +841,7 @@ void WaterfallCanvas::updateCenterFrequency(long long freq) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WaterfallCanvas::setLinesPerSecond(int lps) {
|
void WaterfallCanvas::setLinesPerSecond(int lps) {
|
||||||
|
tex_update.lock();
|
||||||
linesPerSecond = lps;
|
linesPerSecond = lps;
|
||||||
while (!visualDataQueue.empty()) {
|
while (!visualDataQueue.empty()) {
|
||||||
SpectrumVisualData *vData;
|
SpectrumVisualData *vData;
|
||||||
@ -811,7 +851,7 @@ void WaterfallCanvas::setLinesPerSecond(int lps) {
|
|||||||
vData->decRefCount();
|
vData->decRefCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tex_update.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -67,6 +67,7 @@ private:
|
|||||||
long double freqMove;
|
long double freqMove;
|
||||||
float hoverAlpha;
|
float hoverAlpha;
|
||||||
int linesPerSecond;
|
int linesPerSecond;
|
||||||
|
float scaleMove;
|
||||||
|
|
||||||
SpectrumVisualDataQueue visualDataQueue;
|
SpectrumVisualDataQueue visualDataQueue;
|
||||||
Timer gTimer;
|
Timer gTimer;
|
||||||
|
Loading…
Reference in New Issue
Block a user