diff --git a/CMakeLists.txt b/CMakeLists.txt index f1f8bc394..98be77192 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,6 +101,7 @@ option(ENABLE_CHANNELRX_FREQSCANNER "Enable channelrx freqscanner plugin" ON) option(ENABLE_CHANNELRX_ENDOFTRAIN "Enable channelrx end-of-train plugin" ON) option(ENABLE_CHANNELRX_CHANNELPOWER "Enable channelrx channel power plugin" ON) option(ENABLE_CHANNELRX_WDSPRX "Enable channelrx WDSP receiver plugin" ON) +option(ENABLE_CHANNELRX_DEMODINMARSAT "Enable channelrx demodinmarsat plugin" ON) # Channel Tx enablers option(ENABLE_CHANNELTX "Enable channeltx plugins" ON) @@ -825,6 +826,7 @@ if (NOT ENABLE_EXTERNAL_LIBRARIES OR (ENABLE_EXTERNAL_LIBRARIES STREQUAL "AUTO") find_package(HIDAPI) find_package(FFmpeg COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE) find_package(GGMorse) + find_package(LibInmarsatC) find_package(RNnoise) # Devices diff --git a/cmake/Modules/FindLibInmarsatC.cmake b/cmake/Modules/FindLibInmarsatC.cmake new file mode 100644 index 000000000..f2600db2b --- /dev/null +++ b/cmake/Modules/FindLibInmarsatC.cmake @@ -0,0 +1,45 @@ +IF (NOT INMARSATC_FOUND) + FIND_PATH( + INMARSATC_INCLUDE_DIR + NAMES inmarsatc_decoder.h inmarsatc_parser.h + HINTS ${INMARSATC_DIR}/include + PATHS /usr/include + /usr/local/include + ) + + # We don't currently use inmarsatc_demodulator + + FIND_LIBRARY( + INMARSATC_DECODER_LIBRARY + NAMES inmarsatc_decoder + HINTS ${INMARSATC_DIR}/lib + ${INMARSATC_DIR}/lib64 + PATHS /usr/lib + /usr/lib64 + /usr/local/lib + /usr/local/lib64 + ) + + FIND_LIBRARY( + INMARSATC_PARSER_LIBRARY + NAMES inmarsatc_parser + HINTS ${INMARSATC_DIR}/lib + ${INMARSATC_DIR}/lib64 + PATHS /usr/lib + /usr/lib64 + /usr/local/lib + /usr/local/lib64 + ) + + if (INMARSATC_INCLUDE_DIR AND INMARSATC_DECODER_LIBRARY AND INMARSATC_PARSER_LIBRARY) + set(INMARSATC_LIBRARIES ${INMARSATC_DECODER_LIBRARY} ${INMARSATC_PARSER_LIBRARY} CACHE INTERNAL "inmarsatc libraries") + set(INMARSATC_FOUND TRUE CACHE INTERNAL "inmarsatc found") + message(STATUS "Found inmarsatc: ${INMARSATC_INCLUDE_DIR}, ${INMARSATC_LIBRARIES}") + else () + set(INMARSATC_FOUND FALSE CACHE INTERNAL "inmarsatc found") + message(STATUS "inmarsatc not found.") + endif () + + mark_as_advanced(INMARSATC_INCLUDE_DIR INMARSATC_LIBRARIES) + +ENDIF (NOT INMARSATC_FOUND) diff --git a/debian/control b/debian/control index 38e29c329..3e152564d 100644 --- a/debian/control +++ b/debian/control @@ -91,7 +91,7 @@ Description: SDR/Analyzer/Generator front-end for various hardware Builds on Linux, Windows and Mac O/S Reception modes supported: Analog: AM, ATV, NFM, WFM, SSB, broadcast FM, APT, ILS, VOR - Digital: D-Star, Yaesu SF, DMR, dPMR, FreeDV, M17, DAB, DVB-S, LoRa, ADS-B, Packet (AX.25/APRS), AIS, FT8, Navtex, Radiosonde, RTTY, Pager + Digital: D-Star, Yaesu SF, DMR, dPMR, FreeDV, M17, DAB, DVB-S, LoRa, ADS-B, Packet (AX.25/APRS), AIS, FT8, Navtex, Radiosonde, RTTY, Pager, Inmarsat C Analyzer: Generic channel Transmission modes supported: Analog: AM, ATV, NFM, SSB, WFM diff --git a/doc/img/InmarsatDemod_map.png b/doc/img/InmarsatDemod_map.png new file mode 100644 index 000000000..b2aacc75b Binary files /dev/null and b/doc/img/InmarsatDemod_map.png differ diff --git a/doc/img/InmarsatDemod_plugin.png b/doc/img/InmarsatDemod_plugin.png new file mode 100644 index 000000000..7e6889d1a Binary files /dev/null and b/doc/img/InmarsatDemod_plugin.png differ diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 7afc022f4..fccf4b3d0 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -599,6 +599,8 @@ if ((NOT CODEC2_FOUND OR CODEC2_EXTERNAL) AND NOT USE_PRECOMPILED_LIBS) set(CODEC2_LIBRARIES "${SDRANGEL_BINARY_LIB_DIR}/codec2.lib" CACHE INTERNAL "") elseif (LINUX) set(CODEC2_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libcodec2${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") + elseif (APPLE) + set(CODEC2_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/codec2/src/codec2-build/src/libcodec2${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") endif () ExternalProject_Add(codec2 GIT_REPOSITORY https://github.com/drowe67/codec2-dev.git @@ -621,7 +623,6 @@ if ((NOT CODEC2_FOUND OR CODEC2_EXTERNAL) AND NOT USE_PRECOMPILED_LIBS) set(CODEC2_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libcodec2${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") makeLink("${source_dir}/src" "${source_dir}/src/codec2" codec2) elseif (APPLE) - set(CODEC2_LIBRARIES "${binary_dir}/src/libcodec2${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") # some source include "codec2/comp.h" and some without codec2 makeLink("${source_dir}/src" "${source_dir}/src/codec2" codec2) set(MACOS_EXTERNAL_LIBS_FIXUP "${MACOS_EXTERNAL_LIBS_FIXUP};${binary_dir}/src") @@ -634,6 +635,8 @@ if (NOT CM256CC_FOUND OR CM256CC_EXTERNAL) set(CM256CC_LIBRARIES "${SDRANGEL_BINARY_LIB_DIR}/cm256cc.lib" CACHE INTERNAL "") elseif (LINUX) set(CM256CC_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libcm256cc${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") + elseif (APPLE) + set(CM256CC_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/cm256cc/src/cm256cc-build/libcm256cc${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") endif () string(REPLACE ";" "|" CMAKE_PREFIX_PATH_SEP "${CMAKE_PREFIX_PATH}") ExternalProject_Add(cm256cc @@ -659,7 +662,6 @@ if (NOT CM256CC_FOUND OR CM256CC_EXTERNAL) if (WIN32) install(FILES "${SDRANGEL_BINARY_BIN_DIR}/cm256cc${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION "${INSTALL_LIB_DIR}") elseif (APPLE) # kept in case of relaxation of APPLE exclusion - set(CM256CC_LIBRARIES "${binary_dir}/libcm256cc${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") install(DIRECTORY "${binary_dir}/" DESTINATION "${INSTALL_LIB_DIR}" FILES_MATCHING PATTERN "libcm256cc*${CMAKE_SHARED_LIBRARY_SUFFIX}") set(MACOS_EXTERNAL_LIBS_FIXUP "${MACOS_EXTERNAL_LIBS_FIXUP};${binary_dir}") @@ -672,6 +674,8 @@ if ((NOT LIBDSDCC_FOUND OR LIBDSDCC_EXTERNAL) AND (NOT LIBMBE_FOUND OR LIBMBE_EX set(LIBMBE_LIBRARIES "${SDRANGEL_BINARY_LIB_DIR}/libmbe.lib" CACHE INTERNAL "") elseif (LINUX) set(LIBMBE_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libmbe${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") + elseif (APPLE) + set(LIBMBE_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/mbelib/src/mbelib-build/libmbe${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") endif () ExternalProject_Add(mbelib GIT_REPOSITORY https://github.com/srcejon/mbelib.git @@ -691,7 +695,6 @@ if ((NOT LIBDSDCC_FOUND OR LIBDSDCC_EXTERNAL) AND (NOT LIBMBE_FOUND OR LIBMBE_EX if (WIN32) install(FILES "${SDRANGEL_BINARY_BIN_DIR}/libmbe${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION "${INSTALL_LIB_DIR}") elseif (APPLE) - set(LIBMBE_LIBRARIES "${binary_dir}/libmbe${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") install(DIRECTORY "${binary_dir}/" DESTINATION "${INSTALL_LIB_DIR}" FILES_MATCHING PATTERN "libmbe*${CMAKE_SHARED_LIBRARY_SUFFIX}") set(MACOS_EXTERNAL_LIBS_FIXUP "${MACOS_EXTERNAL_LIBS_FIXUP};${binary_dir}") @@ -704,6 +707,8 @@ if (NOT LIBSERIALDV_FOUND OR LIBSERIALDV_EXTERNAL) set(LIBSERIALDV_LIBRARY "${SDRANGEL_BINARY_LIB_DIR}/serialdv.lib" CACHE INTERNAL "") elseif (LINUX) set(LIBSERIALDV_LIBRARY "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libserialdv${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") + elseif (APPLE) + set(LIBSERIALDV_LIBRARY "${EXTERNAL_BUILD_LIBRARIES}/serialdv/src/serialdv-build/libserialdv${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") endif () ExternalProject_Add(serialdv GIT_REPOSITORY https://github.com/f4exb/serialDV.git @@ -726,7 +731,6 @@ if (NOT LIBSERIALDV_FOUND OR LIBSERIALDV_EXTERNAL) # so we need a link makeLink("${source_dir}" "${source_dir}/dsp" serialdv) elseif (APPLE) - set(LIBSERIALDV_LIBRARY "${binary_dir}/libserialdv${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") # because sdrbase/dsp/dvserialworker.h use dsp/dvcontroller.h # so we need a link makeLink("${source_dir}" "${source_dir}/dsp" serialdv) @@ -741,6 +745,8 @@ if ((NOT LIBDSDCC_FOUND OR LIBDSDCC_EXTERNAL) AND LIBMBE_FOUND) set(LIBDSDCC_LIBRARIES "${SDRANGEL_BINARY_LIB_DIR}/dsdcc.lib" CACHE INTERNAL "") elseif (LINUX) set(LIBDSDCC_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libdsdcc${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") + elseif (APPLE) + set(LIBDSDCC_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/dsdcc/src/dsdcc-build/libdsdcc${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") endif () ExternalProject_Add(dsdcc GIT_REPOSITORY https://github.com/f4exb/dsdcc.git @@ -762,7 +768,6 @@ if ((NOT LIBDSDCC_FOUND OR LIBDSDCC_EXTERNAL) AND LIBMBE_FOUND) if (WIN32) install(FILES "${SDRANGEL_BINARY_BIN_DIR}/dsdcc${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION "${INSTALL_LIB_DIR}") elseif (APPLE) - set(LIBDSDCC_LIBRARIES "${binary_dir}/libdsdcc${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") install(DIRECTORY "${binary_dir}/" DESTINATION "${INSTALL_LIB_DIR}" FILES_MATCHING PATTERN "libdsdcc*${CMAKE_SHARED_LIBRARY_SUFFIX}") set(MACOS_EXTERNAL_LIBS_FIXUP "${MACOS_EXTERNAL_LIBS_FIXUP};${binary_dir}/") @@ -792,6 +797,8 @@ if(ENABLE_CHANNELRX_DEMODAPT) set(APT_LIBRARIES "${SDRANGEL_BINARY_LIB_DIR}/apt.lib" CACHE INTERNAL "") elseif (LINUX) set(APT_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libapt${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") + elseif (APPLE) + set(APT_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/apt/src/apt-build/libapt${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") endif() ExternalProject_Add(apt GIT_REPOSITORY https://github.com/srcejon/aptdec.git @@ -810,7 +817,6 @@ if(ENABLE_CHANNELRX_DEMODAPT) if (WIN32) install(FILES "${SDRANGEL_BINARY_BIN_DIR}/apt${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION "${INSTALL_LIB_DIR}") elseif (APPLE) - set(APT_LIBRARIES "${binary_dir}/libapt${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") install(DIRECTORY "${binary_dir}/" DESTINATION "${INSTALL_LIB_DIR}" FILES_MATCHING PATTERN "libapt*${CMAKE_SHARED_LIBRARY_SUFFIX}") set(MACOS_EXTERNAL_LIBS_FIXUP "${MACOS_EXTERNAL_LIBS_FIXUP};${binary_dir}/") @@ -825,6 +831,8 @@ if(ENABLE_FEATURE_SATELLITETRACKER OR ENABLE_CHANNELRX_DEMODAPT) set(SGP4_LIBRARIES "${SDRANGEL_BINARY_LIB_DIR}/sgp4s.lib" CACHE INTERNAL "") elseif (LINUX) set(SGP4_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libsgp4s${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") + elseif (APPLE) + set(SGP4_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/sgp4/src/sgp4-build/libsgp4/libsgp4s${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") endif() ExternalProject_Add(sgp4 GIT_REPOSITORY https://github.com/dnwrnr/sgp4.git @@ -843,7 +851,6 @@ if(ENABLE_FEATURE_SATELLITETRACKER OR ENABLE_CHANNELRX_DEMODAPT) if (WIN32) install(FILES "${SDRANGEL_BINARY_BIN_DIR}/sgp4s${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION "${INSTALL_LIB_DIR}") elseif (APPLE) - set(SGP4_LIBRARIES "${binary_dir}/libsgp4/libsgp4s${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") install(DIRECTORY "${binary_dir}/libsgp4" DESTINATION "${INSTALL_LIB_DIR}" FILES_MATCHING PATTERN "libsgp4s*${CMAKE_SHARED_LIBRARY_SUFFIX}") set(MACOS_EXTERNAL_LIBS_FIXUP "${MACOS_EXTERNAL_LIBS_FIXUP};${binary_dir}/libsgp4") @@ -854,7 +861,7 @@ if(ENABLE_CHANNELRX_REMOTETCPSINK) if (WIN32) set(FLAC_LIBRARIES "${SDRANGEL_BINARY_LIB_DIR}/FLAC.lib" CACHE INTERNAL "") elseif (LINUX) - set(FLAC_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/flac/src/flac-build/src/libFLAC/libFLAC${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") + set(FLAC_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libFLAC${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") elseif (APPLE) set(FLAC_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/flac/src/flac-build/src/libFLAC/libFLAC${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") elseif (EMSCRIPTEN) @@ -888,6 +895,8 @@ if(ENABLE_FEATURE_MORSEDECODER) set(GGMORSE_LIBRARIES "${SDRANGEL_BINARY_LIB_DIR}/ggmorse.lib" CACHE INTERNAL "") elseif (LINUX) set(GGMORSE_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libggmorse${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") + elseif (APPLE) + set(GGMORSE_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/ggmorse/src/ggmorse-build/src/libggmorse${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") endif() ExternalProject_Add(ggmorse GIT_REPOSITORY https://github.com/srcejon/ggmorse.git @@ -908,13 +917,57 @@ if(ENABLE_FEATURE_MORSEDECODER) if (WIN32) install(FILES "${SDRANGEL_BINARY_BIN_DIR}/ggmorse${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION "${INSTALL_LIB_DIR}") elseif (APPLE) - set(GGMORSE_LIBRARIES "${binary_dir}/src/libggmorse${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") install(DIRECTORY "${binary_dir}/src" DESTINATION "${INSTALL_LIB_DIR}" FILES_MATCHING PATTERN "libggmorse*${CMAKE_SHARED_LIBRARY_SUFFIX}") set(MACOS_EXTERNAL_LIBS_FIXUP "${MACOS_EXTERNAL_LIBS_FIXUP};${binary_dir}/src") endif () endif() +# For Inmarsat C decoder +if(ENABLE_CHANNELRX_DEMODINMARSAT) + if (WIN32) + set(INMARSATC_LIBRARIES + "${SDRANGEL_BINARY_LIB_DIR}/inmarsatc_parser.lib" + "${SDRANGEL_BINARY_LIB_DIR}/inmarsatc_decoder.lib" + CACHE INTERNAL "" + ) + elseif (LINUX) + set(INMARSATC_LIBRARIES + "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libinmarsatc_decoder${CMAKE_SHARED_LIBRARY_SUFFIX}" + "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libinmarsatc_parser${CMAKE_SHARED_LIBRARY_SUFFIX}" + CACHE INTERNAL "" + ) + elseif (APPLE) + set(INMARSATC_LIBRARIES + "${EXTERNAL_BUILD_LIBRARIES}/inmarsatc/src/inmarsatc-build/libinmarsatc_decoder${CMAKE_SHARED_LIBRARY_SUFFIX}" + "${EXTERNAL_BUILD_LIBRARIES}/inmarsatc/src/inmarsatc-build/libinmarsatc_parser${CMAKE_SHARED_LIBRARY_SUFFIX}" + CACHE INTERNAL "" + ) + endif() + ExternalProject_Add(inmarsatc + GIT_REPOSITORY https://github.com/srcejon/inmarsatc.git + GIT_TAG "msvc" + PREFIX "${EXTERNAL_BUILD_LIBRARIES}/inmarsatc" + CMAKE_ARGS ${COMMON_CMAKE_ARGS} + BUILD_BYPRODUCTS "${INMARSATC_LIBRARIES}" + INSTALL_COMMAND "" + TEST_COMMAND "" + ) + ExternalProject_Get_Property(inmarsatc source_dir binary_dir) + set(INMARSATC_DEPENDS inmarsatc CACHE INTERNAL "") + set_global_cache(INMARSATC_FOUND ON) + set(INMARSATC_EXTERNAL ON CACHE INTERNAL "") + set(INMARSATC_INCLUDE_DIR "${EXTERNAL_BUILD_LIBRARIES}/inmarsatc/src/inmarsatc" CACHE INTERNAL "") + if (WIN32) + install(FILES "${SDRANGEL_BINARY_BIN_DIR}/inmarsatc_parser${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION "${INSTALL_LIB_DIR}") + install(FILES "${SDRANGEL_BINARY_BIN_DIR}/inmarsatc_decoder${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION "${INSTALL_LIB_DIR}") + elseif (APPLE) + install(DIRECTORY "${binary_dir}" DESTINATION "${INSTALL_LIB_DIR}" + FILES_MATCHING PATTERN "libinmarsatc*${CMAKE_SHARED_LIBRARY_SUFFIX}") + set(MACOS_EXTERNAL_LIBS_FIXUP "${MACOS_EXTERNAL_LIBS_FIXUP};${binary_dir}/src") + endif () +endif() + # For denoiser feature if(LINUX AND ENABLE_FEATURE_DENOISER) if (WIN32) @@ -990,6 +1043,8 @@ if (ZLIB_FOUND AND FAAD_FOUND AND ENABLE_CHANNELRX_DEMODDAB) set(DAB_LIBRARIES "${SDRANGEL_BINARY_LIB_DIR}/dab_lib.lib" CACHE INTERNAL "") elseif (LINUX) set(DAB_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/lib${LIB_SUFFIX}/libdab_lib${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") + elseif (APPLE) + set(DAB_LIBRARIES "${EXTERNAL_BUILD_LIBRARIES}/dab/src/dab-build/libdab_lib${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") endif() if (WIN32) ExternalProject_Add(dab @@ -1014,7 +1069,7 @@ if (ZLIB_FOUND AND FAAD_FOUND AND ENABLE_CHANNELRX_DEMODDAB) ExternalProject_Add(dab GIT_REPOSITORY https://github.com/srcejon/dab-cmdline.git GIT_TAG msvc - DEPENDS ${ZLIB_DEPENDS} ${FAAD_DEPENDS} + DEPENDS ${ZLIB_DEPENDS} ${FAAD_DEPENDS} ${FFTW3F_DEPENDS} PREFIX "${EXTERNAL_BUILD_LIBRARIES}/dab" SOURCE_SUBDIR "library" CMAKE_ARGS ${COMMON_CMAKE_ARGS} @@ -1037,13 +1092,12 @@ if (ZLIB_FOUND AND FAAD_FOUND AND ENABLE_CHANNELRX_DEMODDAB) if (WIN32) install(FILES "${SDRANGEL_BINARY_BIN_DIR}/dab_lib${CMAKE_SHARED_LIBRARY_SUFFIX}" DESTINATION "${INSTALL_LIB_DIR}") elseif (APPLE) - set(DAB_LIBRARIES "${binary_dir}/libdab_lib${CMAKE_SHARED_LIBRARY_SUFFIX}" CACHE INTERNAL "") install(DIRECTORY "${binary_dir}/" DESTINATION "${INSTALL_LIB_DIR}" FILES_MATCHING PATTERN "libdab_lib*${CMAKE_SHARED_LIBRARY_SUFFIX}") set(MACOS_EXTERNAL_LIBS_FIXUP "${MACOS_EXTERNAL_LIBS_FIXUP};${binary_dir}/") endif () else() - message(STATUS "Can't build DAB library as missing zlib or faad") + message(STATUS "Can't build DAB library as missing zlib (${ZLIB_FOUND}) or faad (${FAAD_FOUND})") endif() # Device interface libraries. diff --git a/plugins/channelrx/CMakeLists.txt b/plugins/channelrx/CMakeLists.txt index 3b9f2129b..59783572c 100644 --- a/plugins/channelrx/CMakeLists.txt +++ b/plugins/channelrx/CMakeLists.txt @@ -1,5 +1,11 @@ project(demod) +if (ENABLE_CHANNELRX_DEMODINMARSAT AND INMARSATC_FOUND) + add_subdirectory(demodinmarsat) +else() + message(STATUS "Not building demodinmarsat (ENABLE_CHANNELRX_DEMODINMARSAT=${ENABLE_CHANNELRX_DEMODINMARSAT} INMARSATC_FOUND=${INMARSATC_FOUND})") +endif() + if (ENABLE_CHANNELRX_CHANNELPOWER) add_subdirectory(channelpower) endif() diff --git a/plugins/channelrx/demodinmarsat/CMakeLists.txt b/plugins/channelrx/demodinmarsat/CMakeLists.txt new file mode 100644 index 000000000..5c5c931f6 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/CMakeLists.txt @@ -0,0 +1,72 @@ +project(demodinmarsat) + +set(demodinmarsat_SOURCES + inmarsatdemod.cpp + inmarsatdemodsettings.cpp + inmarsatdemodbaseband.cpp + inmarsatdemodsink.cpp + inmarsatdemodplugin.cpp + inmarsatdemodwebapiadapter.cpp +) + +set(demodinmarsat_HEADERS + inmarsatdemod.h + inmarsatdemodsettings.h + inmarsatdemodbaseband.h + inmarsatdemodsink.h + inmarsatdemodplugin.h + inmarsatdemodwebapiadapter.h +) + +include_directories( + ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client + ${INMARSATC_INCLUDE_DIR} +) + +if(NOT SERVER_MODE) + set(demodinmarsat_SOURCES + ${demodinmarsat_SOURCES} + inmarsatdemodgui.cpp + inmarsatdemodgui.ui + ) + set(demodinmarsat_HEADERS + ${demodinmarsat_HEADERS} + inmarsatdemodgui.h + ) + + set(TARGET_NAME ${PLUGINS_PREFIX}demodinmarsat) + set(TARGET_LIB "Qt::Widgets") + set(TARGET_LIB_GUI "sdrgui") + set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR}) +else() + set(TARGET_NAME ${PLUGINSSRV_PREFIX}demodinmarsatsrv) + set(TARGET_LIB "") + set(TARGET_LIB_GUI "") + set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR}) +endif() + +if(NOT Qt6_FOUND) + add_library(${TARGET_NAME} ${demodinmarsat_SOURCES}) +else() + qt_add_plugin(${TARGET_NAME} CLASS_NAME InmarsatDemodPlugin ${demodinmarsat_SOURCES}) +endif() + +if(NOT BUILD_SHARED_LIBS) + set_property(GLOBAL APPEND PROPERTY STATIC_PLUGINS_PROPERTY ${TARGET_NAME}) +endif() + +target_link_libraries(${TARGET_NAME} PRIVATE + Qt::Core + ${TARGET_LIB} + sdrbase + ${TARGET_LIB_GUI} + ${INMARSATC_LIBRARIES} +) + +install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER}) + +# Install debug symbols +if (WIN32) + install(FILES $/${TARGET_NAME}stripped.pdb CONFIGURATIONS Release DESTINATION ${INSTALL_FOLDER} RENAME ${TARGET_NAME}.pdb ) + install(FILES $ CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} ) +endif() diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemod.cpp b/plugins/channelrx/demodinmarsat/inmarsatdemod.cpp new file mode 100644 index 000000000..19401d045 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemod.cpp @@ -0,0 +1,671 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2021-2026 Jon Beniston, M7RCE // +// Copyright (C) 2021-2022 Edouard Griffiths, F4EXB // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "inmarsatdemod.h" + +#include +#include +#include +#include +#include +#include + +#include "SWGChannelSettings.h" +#include "SWGWorkspaceInfo.h" +#include "SWGInmarsatDemodSettings.h" +#include "SWGChannelReport.h" + +#include "dsp/dspcommands.h" +#include "device/deviceapi.h" +#include "settings/serializable.h" +#include "util/db.h" +#include "maincore.h" + +MESSAGE_CLASS_DEFINITION(InmarsatDemod::MsgConfigureInmarsatDemod, Message) + +const char * const InmarsatDemod::m_channelIdURI = "sdrangel.channel.inmarsatdemod"; +const char * const InmarsatDemod::m_channelId = "InmarsatDemod"; + +InmarsatDemod::InmarsatDemod(DeviceAPI *deviceAPI) : + ChannelAPI(m_channelIdURI, ChannelAPI::StreamSingleSink), + m_deviceAPI(deviceAPI), + m_basebandSampleRate(0) +{ + setObjectName(m_channelId); + + m_basebandSink = new InmarsatDemodBaseband(this); + m_basebandSink->setMessageQueueToChannel(getInputMessageQueue()); + m_basebandSink->setChannel(this); + m_basebandSink->moveToThread(&m_thread); + + applySettings(m_settings, QStringList(), true); + + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + + m_networkManager = new QNetworkAccessManager(); + QObject::connect( + m_networkManager, + &QNetworkAccessManager::finished, + this, + &InmarsatDemod::networkManagerFinished + ); + QObject::connect( + this, + &ChannelAPI::indexInDeviceSetChanged, + this, + &InmarsatDemod::handleIndexInDeviceSetChanged + ); +} + +InmarsatDemod::~InmarsatDemod() +{ + qDebug("InmarsatDemod::~InmarsatDemod"); + QObject::disconnect( + m_networkManager, + &QNetworkAccessManager::finished, + this, + &InmarsatDemod::networkManagerFinished + ); + delete m_networkManager; + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this, true); + + if (m_basebandSink->isRunning()) { + stop(); + } + + delete m_basebandSink; +} + +void InmarsatDemod::setDeviceAPI(DeviceAPI *deviceAPI) +{ + if (deviceAPI != m_deviceAPI) + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this, false); + m_deviceAPI = deviceAPI; + m_deviceAPI->addChannelSink(this); + m_deviceAPI->addChannelSinkAPI(this); + } +} + +uint32_t InmarsatDemod::getNumberOfDeviceStreams() const +{ + return m_deviceAPI->getNbSourceStreams(); +} + +void InmarsatDemod::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool firstOfBurst) +{ + (void) firstOfBurst; + m_basebandSink->feed(begin, end); +} + +void InmarsatDemod::start() +{ + qDebug("InmarsatDemod::start"); + + m_basebandSink->reset(); + m_basebandSink->startWork(); + m_thread.start(); + + DSPSignalNotification *dspMsg = new DSPSignalNotification(m_basebandSampleRate, m_centerFrequency); + m_basebandSink->getInputMessageQueue()->push(dspMsg); + + InmarsatDemodBaseband::MsgConfigureInmarsatDemodBaseband *msg = InmarsatDemodBaseband::MsgConfigureInmarsatDemodBaseband::create(m_settings, QStringList(), true); + m_basebandSink->getInputMessageQueue()->push(msg); +} + +void InmarsatDemod::stop() +{ + qDebug("InmarsatDemod::stop"); + m_basebandSink->stopWork(); + m_thread.quit(); + m_thread.wait(); +} + +bool InmarsatDemod::handleMessage(const Message& cmd) +{ + if (MsgConfigureInmarsatDemod::match(cmd)) + { + MsgConfigureInmarsatDemod& cfg = (MsgConfigureInmarsatDemod&) cmd; + qDebug() << "InmarsatDemod::handleMessage: MsgConfigureInmarsatDemod"; + applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce()); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + m_basebandSampleRate = notif.getSampleRate(); + m_centerFrequency = notif.getCenterFrequency(); + // Forward to the sink + DSPSignalNotification* rep = new DSPSignalNotification(notif); // make a copy + qDebug() << "InmarsatDemod::handleMessage: DSPSignalNotification"; + m_basebandSink->getInputMessageQueue()->push(rep); + // Forward to GUI if any + if (m_guiMessageQueue) { + m_guiMessageQueue->push(new DSPSignalNotification(notif)); + } + + return true; + } + else if (MainCore::MsgPacket::match(cmd)) + { + // Forward to GUI + MainCore::MsgPacket& report = (MainCore::MsgPacket&)cmd; + if (getMessageQueueToGUI()) + { + MainCore::MsgPacket *msg = new MainCore::MsgPacket(report); + getMessageQueueToGUI()->push(msg); + } + + // Forward to packet features + QList packetsPipes; + MainCore::instance()->getMessagePipes().getMessagePipes(this, "packets", packetsPipes); + + for (const auto& pipe : packetsPipes) + { + MessageQueue *messageQueue = qobject_cast(pipe->m_element); + MainCore::MsgPacket *msg = new MainCore::MsgPacket(report); + messageQueue->push(msg); + } + + // Forward via UDP + if (m_settings.m_udpEnabled) + { + qDebug() << "Forwarding to " << m_settings.m_udpAddress << ":" << m_settings.m_udpPort; + m_udpSocket.writeDatagram(report.getPacket().data(), report.getPacket().size(), + QHostAddress(m_settings.m_udpAddress), m_settings.m_udpPort); + } + + // Write to log file + if (m_logFile.isOpen()) + { + m_logStream << report.getDateTime().date().toString() << "," + << report.getDateTime().time().toString() << "," + << report.getPacket().toHex() << "\n"; + } + + return true; + } + else if (MainCore::MsgChannelDemodQuery::match(cmd)) + { + qDebug() << "InmarsatDemod::handleMessage: MsgChannelDemodQuery"; + sendSampleRateToDemodAnalyzer(); + + return true; + } + else + { + return false; + } +} + +ScopeVis *InmarsatDemod::getScopeSink() +{ + return m_basebandSink->getScopeSink(); +} + +void InmarsatDemod::setCenterFrequency(qint64 frequency) +{ + InmarsatDemodSettings settings = m_settings; + settings.m_inputFrequencyOffset = frequency; + applySettings(settings, {"inputFrequencyOffset"}, false); + + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureInmarsatDemod *msgToGUI = MsgConfigureInmarsatDemod::create(settings, {"inputFrequencyOffset"}, false); + m_guiMessageQueue->push(msgToGUI); + } +} + +void InmarsatDemod::applySettings(const InmarsatDemodSettings& settings, const QStringList& settingsKeys, bool force) +{ + qDebug() << "InmarsatDemod::applySettings:" + << settings.getDebugString(settingsKeys, force) + << " force: " << force; + + if (settingsKeys.contains("streamIndex")) + { + if (m_deviceAPI->getSampleMIMO()) // change of stream is possible for MIMO devices only + { + m_deviceAPI->removeChannelSinkAPI(this); + m_deviceAPI->removeChannelSink(this, m_settings.m_streamIndex); + m_deviceAPI->addChannelSink(this, settings.m_streamIndex); + m_deviceAPI->addChannelSinkAPI(this); + m_settings.m_streamIndex = settings.m_streamIndex; // make sure ChannelAPI::getStreamIndex() is consistent + emit streamIndexChanged(settings.m_streamIndex); + } + } + + InmarsatDemodBaseband::MsgConfigureInmarsatDemodBaseband *msg = InmarsatDemodBaseband::MsgConfigureInmarsatDemodBaseband::create(settings, settingsKeys, force); + m_basebandSink->getInputMessageQueue()->push(msg); + + if (settings.m_useReverseAPI) + { + bool fullUpdate = (settingsKeys.contains("useReverseAPI") && settings.m_useReverseAPI) || + settingsKeys.contains("reverseAPIAddress") || + settingsKeys.contains("reverseAPIPort") || + settingsKeys.contains("reverseAPIDeviceIndex") || + settingsKeys.contains("reverseAPIChannelIndex"); + webapiReverseSendSettings(settingsKeys, settings, fullUpdate || force); + } + + if ((settings.m_logEnabled != m_settings.m_logEnabled) + || (settings.m_logFilename != m_settings.m_logFilename) + || force) + { + if (m_logFile.isOpen()) + { + m_logStream.flush(); + m_logFile.close(); + } + if (settings.m_logEnabled && !settings.m_logFilename.isEmpty()) + { + m_logFile.setFileName(settings.m_logFilename); + if (m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) + { + qDebug() << "InmarsatDemod::applySettings - Logging to: " << settings.m_logFilename; + bool newFile = m_logFile.size() == 0; + m_logStream.setDevice(&m_logFile); + if (newFile) + { + // Write header + m_logStream << "Date,Time,Data\n"; + } + } + else + { + qDebug() << "InmarsatDemod::applySettings - Unable to open log file: " << settings.m_logFilename; + } + } + } + + if (force) { + m_settings = settings; + } else { + m_settings.applySettings(settingsKeys, settings); + } +} + +void InmarsatDemod::sendSampleRateToDemodAnalyzer() +{ + QList pipes; + MainCore::instance()->getMessagePipes().getMessagePipes(this, "reportdemod", pipes); + + if (pipes.size() > 0) + { + for (const auto& pipe : pipes) + { + MessageQueue *messageQueue = qobject_cast(pipe->m_element); + MainCore::MsgChannelDemodReport *msg = MainCore::MsgChannelDemodReport::create( + this, + InmarsatDemodSettings::CHANNEL_SAMPLE_RATE + ); + messageQueue->push(msg); + } + } +} + +QByteArray InmarsatDemod::serialize() const +{ + return m_settings.serialize(); +} + +bool InmarsatDemod::deserialize(const QByteArray& data) +{ + if (m_settings.deserialize(data)) + { + MsgConfigureInmarsatDemod *msg = MsgConfigureInmarsatDemod::create(m_settings, QStringList(), true); + m_inputMessageQueue.push(msg); + return true; + } + else + { + m_settings.resetToDefaults(); + MsgConfigureInmarsatDemod *msg = MsgConfigureInmarsatDemod::create(m_settings, QStringList(), true); + m_inputMessageQueue.push(msg); + return false; + } +} + +int InmarsatDemod::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setInmarsatDemodSettings(new SWGSDRangel::SWGInmarsatDemodSettings()); + response.getInmarsatDemodSettings()->init(); + webapiFormatChannelSettings(response, m_settings); + return 200; +} + +int InmarsatDemod::webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setIndex(m_settings.m_workspaceIndex); + return 200; +} + +int InmarsatDemod::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + InmarsatDemodSettings settings = m_settings; + webapiUpdateChannelSettings(settings, channelSettingsKeys, response); + + MsgConfigureInmarsatDemod *msg = MsgConfigureInmarsatDemod::create(settings, channelSettingsKeys, force); + m_inputMessageQueue.push(msg); + + qDebug("InmarsatDemod::webapiSettingsPutPatch: forward to GUI: %p", m_guiMessageQueue); + if (m_guiMessageQueue) // forward to GUI if any + { + MsgConfigureInmarsatDemod *msgToGUI = MsgConfigureInmarsatDemod::create(settings, channelSettingsKeys, force); + m_guiMessageQueue->push(msgToGUI); + } + + webapiFormatChannelSettings(response, settings); + + return 200; +} + +int InmarsatDemod::webapiReportGet( + SWGSDRangel::SWGChannelReport& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setInmarsatDemodReport(new SWGSDRangel::SWGInmarsatDemodReport()); + response.getInmarsatDemodReport()->init(); + webapiFormatChannelReport(response); + return 200; +} + +void InmarsatDemod::webapiUpdateChannelSettings( + InmarsatDemodSettings& settings, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response) +{ + if (channelSettingsKeys.contains("inputFrequencyOffset")) { + settings.m_inputFrequencyOffset = response.getInmarsatDemodSettings()->getInputFrequencyOffset(); + } + if (channelSettingsKeys.contains("rfBandwidth")) { + settings.m_rfBandwidth = response.getInmarsatDemodSettings()->getRfBandwidth(); + } + if (channelSettingsKeys.contains("rrcRolloff")) { + settings.m_rrcRolloff = response.getInmarsatDemodSettings()->getRrcRolloff(); + } + if (channelSettingsKeys.contains("pllBandwidth")) { + settings.m_pllBW = response.getInmarsatDemodSettings()->getPllBandwidth(); + } + if (channelSettingsKeys.contains("udpEnabled")) { + settings.m_udpEnabled = response.getInmarsatDemodSettings()->getUdpEnabled(); + } + if (channelSettingsKeys.contains("udpAddress")) { + settings.m_udpAddress = *response.getInmarsatDemodSettings()->getUdpAddress(); + } + if (channelSettingsKeys.contains("udpPort")) { + settings.m_udpPort = response.getInmarsatDemodSettings()->getUdpPort(); + } + if (channelSettingsKeys.contains("logFilename")) { + settings.m_logFilename = *response.getInmarsatDemodSettings()->getLogFilename(); + } + if (channelSettingsKeys.contains("logEnabled")) { + settings.m_logEnabled = response.getInmarsatDemodSettings()->getLogEnabled(); + } + if (channelSettingsKeys.contains("useFileTime")) { + settings.m_useFileTime = response.getInmarsatDemodSettings()->getUseFileTime(); + } + if (channelSettingsKeys.contains("rgbColor")) { + settings.m_rgbColor = response.getInmarsatDemodSettings()->getRgbColor(); + } + if (channelSettingsKeys.contains("title")) { + settings.m_title = *response.getInmarsatDemodSettings()->getTitle(); + } + if (channelSettingsKeys.contains("streamIndex")) { + settings.m_streamIndex = response.getInmarsatDemodSettings()->getStreamIndex(); + } + if (channelSettingsKeys.contains("useReverseAPI")) { + settings.m_useReverseAPI = response.getInmarsatDemodSettings()->getUseReverseApi() != 0; + } + if (channelSettingsKeys.contains("reverseAPIAddress")) { + settings.m_reverseAPIAddress = *response.getInmarsatDemodSettings()->getReverseApiAddress(); + } + if (channelSettingsKeys.contains("reverseAPIPort")) { + settings.m_reverseAPIPort = response.getInmarsatDemodSettings()->getReverseApiPort(); + } + if (channelSettingsKeys.contains("reverseAPIDeviceIndex")) { + settings.m_reverseAPIDeviceIndex = response.getInmarsatDemodSettings()->getReverseApiDeviceIndex(); + } + if (channelSettingsKeys.contains("reverseAPIChannelIndex")) { + settings.m_reverseAPIChannelIndex = response.getInmarsatDemodSettings()->getReverseApiChannelIndex(); + } + if (settings.m_channelMarker && channelSettingsKeys.contains("channelMarker")) { + settings.m_channelMarker->updateFrom(channelSettingsKeys, response.getInmarsatDemodSettings()->getChannelMarker()); + } + if (settings.m_rollupState && channelSettingsKeys.contains("rollupState")) { + settings.m_rollupState->updateFrom(channelSettingsKeys, response.getInmarsatDemodSettings()->getRollupState()); + } +} + +void InmarsatDemod::webapiFormatChannelSettings(SWGSDRangel::SWGChannelSettings& response, const InmarsatDemodSettings& settings) +{ + response.getInmarsatDemodSettings()->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + response.getInmarsatDemodSettings()->setRfBandwidth(settings.m_rfBandwidth); + response.getInmarsatDemodSettings()->setRrcRolloff(settings.m_rrcRolloff); + response.getInmarsatDemodSettings()->setPllBandwidth(settings.m_pllBW); + response.getInmarsatDemodSettings()->setUdpEnabled(settings.m_udpEnabled); + response.getInmarsatDemodSettings()->setUdpAddress(new QString(settings.m_udpAddress)); + response.getInmarsatDemodSettings()->setUdpPort(settings.m_udpPort); + response.getInmarsatDemodSettings()->setLogFilename(new QString(settings.m_logFilename)); + response.getInmarsatDemodSettings()->setLogEnabled(settings.m_logEnabled); + response.getInmarsatDemodSettings()->setUseFileTime(settings.m_useFileTime); + + response.getInmarsatDemodSettings()->setRgbColor(settings.m_rgbColor); + if (response.getInmarsatDemodSettings()->getTitle()) { + *response.getInmarsatDemodSettings()->getTitle() = settings.m_title; + } else { + response.getInmarsatDemodSettings()->setTitle(new QString(settings.m_title)); + } + + response.getInmarsatDemodSettings()->setStreamIndex(settings.m_streamIndex); + response.getInmarsatDemodSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0); + + if (response.getInmarsatDemodSettings()->getReverseApiAddress()) { + *response.getInmarsatDemodSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress; + } else { + response.getInmarsatDemodSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress)); + } + + response.getInmarsatDemodSettings()->setReverseApiPort(settings.m_reverseAPIPort); + response.getInmarsatDemodSettings()->setReverseApiDeviceIndex(settings.m_reverseAPIDeviceIndex); + response.getInmarsatDemodSettings()->setReverseApiChannelIndex(settings.m_reverseAPIChannelIndex); + + if (settings.m_channelMarker) + { + if (response.getInmarsatDemodSettings()->getChannelMarker()) + { + settings.m_channelMarker->formatTo(response.getInmarsatDemodSettings()->getChannelMarker()); + } + else + { + SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker(); + settings.m_channelMarker->formatTo(swgChannelMarker); + response.getInmarsatDemodSettings()->setChannelMarker(swgChannelMarker); + } + } + + if (settings.m_rollupState) + { + if (response.getInmarsatDemodSettings()->getRollupState()) + { + settings.m_rollupState->formatTo(response.getInmarsatDemodSettings()->getRollupState()); + } + else + { + SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); + settings.m_rollupState->formatTo(swgRollupState); + response.getInmarsatDemodSettings()->setRollupState(swgRollupState); + } + } +} + +void InmarsatDemod::webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response) +{ + double magsqAvg, magsqPeak; + int nbMagsqSamples; + getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples); + + response.getInmarsatDemodReport()->setChannelPowerDb(CalcDb::dbPower(magsqAvg)); + response.getInmarsatDemodReport()->setChannelSampleRate(m_basebandSink->getChannelSampleRate()); +} + +void InmarsatDemod::webapiReverseSendSettings(const QList& channelSettingsKeys, const InmarsatDemodSettings& settings, bool force) +{ + SWGSDRangel::SWGChannelSettings *swgChannelSettings = new SWGSDRangel::SWGChannelSettings(); + webapiFormatChannelSettings(channelSettingsKeys, swgChannelSettings, settings, force); + + QString channelSettingsURL = QString("http://%1:%2/sdrangel/deviceset/%3/channel/%4/settings") + .arg(settings.m_reverseAPIAddress) + .arg(settings.m_reverseAPIPort) + .arg(settings.m_reverseAPIDeviceIndex) + .arg(settings.m_reverseAPIChannelIndex); + m_networkRequest.setUrl(QUrl(channelSettingsURL)); + m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + QBuffer *buffer = new QBuffer(); + buffer->open((QBuffer::ReadWrite)); + buffer->write(swgChannelSettings->asJson().toUtf8()); + buffer->seek(0); + + // Always use PATCH to avoid passing reverse API settings + QNetworkReply *reply = m_networkManager->sendCustomRequest(m_networkRequest, "PATCH", buffer); + buffer->setParent(reply); + + delete swgChannelSettings; +} + +void InmarsatDemod::webapiFormatChannelSettings( + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings *swgChannelSettings, + const InmarsatDemodSettings& settings, + bool force +) +{ + swgChannelSettings->setDirection(0); // Single sink (Rx) + swgChannelSettings->setOriginatorChannelIndex(getIndexInDeviceSet()); + swgChannelSettings->setOriginatorDeviceSetIndex(getDeviceSetIndex()); + swgChannelSettings->setChannelType(new QString("InmarsatDemod")); + swgChannelSettings->setInmarsatDemodSettings(new SWGSDRangel::SWGInmarsatDemodSettings()); + SWGSDRangel::SWGInmarsatDemodSettings *swgInmarsatDemodSettings = swgChannelSettings->getInmarsatDemodSettings(); + + // transfer data that has been modified. When force is on transfer all data except reverse API data + + if (channelSettingsKeys.contains("inputFrequencyOffset") || force) { + swgInmarsatDemodSettings->setInputFrequencyOffset(settings.m_inputFrequencyOffset); + } + if (channelSettingsKeys.contains("rfBandwidth") || force) { + swgInmarsatDemodSettings->setRfBandwidth(settings.m_rfBandwidth); + } + if (channelSettingsKeys.contains("rrcRolloff") || force) { + swgInmarsatDemodSettings->setRrcRolloff(settings.m_rrcRolloff); + } + if (channelSettingsKeys.contains("pllBandwidth") || force) { + swgInmarsatDemodSettings->setPllBandwidth(settings.m_pllBW); + } + if (channelSettingsKeys.contains("udpEnabled") || force) { + swgInmarsatDemodSettings->setUdpEnabled(settings.m_udpEnabled); + } + if (channelSettingsKeys.contains("udpAddress") || force) { + swgInmarsatDemodSettings->setUdpAddress(new QString(settings.m_udpAddress)); + } + if (channelSettingsKeys.contains("udpPort") || force) { + swgInmarsatDemodSettings->setUdpPort(settings.m_udpPort); + } + if (channelSettingsKeys.contains("logFilename") || force) { + swgInmarsatDemodSettings->setLogFilename(new QString(settings.m_logFilename)); + } + if (channelSettingsKeys.contains("logEnabled") || force) { + swgInmarsatDemodSettings->setLogEnabled(settings.m_logEnabled); + } + if (channelSettingsKeys.contains("useFilTime") || force) { + swgInmarsatDemodSettings->setUseFileTime(settings.m_useFileTime); + } + if (channelSettingsKeys.contains("rgbColor") || force) { + swgInmarsatDemodSettings->setRgbColor(settings.m_rgbColor); + } + if (channelSettingsKeys.contains("title") || force) { + swgInmarsatDemodSettings->setTitle(new QString(settings.m_title)); + } + if (channelSettingsKeys.contains("streamIndex") || force) { + swgInmarsatDemodSettings->setStreamIndex(settings.m_streamIndex); + } + + if (settings.m_channelMarker && (channelSettingsKeys.contains("channelMarker") || force)) + { + SWGSDRangel::SWGChannelMarker *swgChannelMarker = new SWGSDRangel::SWGChannelMarker(); + settings.m_channelMarker->formatTo(swgChannelMarker); + swgInmarsatDemodSettings->setChannelMarker(swgChannelMarker); + } + + if (settings.m_rollupState && (channelSettingsKeys.contains("rollupState") || force)) + { + SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState(); + settings.m_rollupState->formatTo(swgRollupState); + swgInmarsatDemodSettings->setRollupState(swgRollupState); + } +} + +void InmarsatDemod::networkManagerFinished(QNetworkReply *reply) +{ + QNetworkReply::NetworkError replyError = reply->error(); + + if (replyError) + { + qWarning() << "InmarsatDemod::networkManagerFinished:" + << " error(" << (int) replyError + << "): " << replyError + << ": " << reply->errorString(); + } + else + { + QString answer = reply->readAll(); + answer.chop(1); // remove last \n + qDebug("InmarsatDemod::networkManagerFinished: reply:\n%s", answer.toStdString().c_str()); + } + + reply->deleteLater(); +} + +void InmarsatDemod::handleIndexInDeviceSetChanged(int index) +{ + if (index < 0) { + return; + } + + QString fifoLabel = QString("%1 [%2:%3]") + .arg(m_channelId) + .arg(m_deviceAPI->getDeviceSetIndex()) + .arg(index); + m_basebandSink->setFifoLabel(fifoLabel); +} diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemod.h b/plugins/channelrx/demodinmarsat/inmarsatdemod.h new file mode 100644 index 000000000..6be6fffee --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemod.h @@ -0,0 +1,180 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// Copyright (C) 2015-2022 Edouard Griffiths, F4EXB // +// Copyright (C) 2020-2026 Jon Beniston, M7RCE // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_INMARSATDEMOD_H +#define INCLUDE_INMARSATDEMOD_H + +#include +#include +#include +#include +#include + +#include "dsp/basebandsamplesink.h" +#include "channel/channelapi.h" +#include "util/message.h" + +#include "inmarsatdemodbaseband.h" +#include "inmarsatdemodsettings.h" + +class QNetworkAccessManager; +class QNetworkReply; +class QThread; +class DeviceAPI; +class ScopeVis; + +class InmarsatDemod : public BasebandSampleSink, public ChannelAPI { +public: + class MsgConfigureInmarsatDemod : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const InmarsatDemodSettings& getSettings() const { return m_settings; } + const QStringList& getSettingsKeys() const { return m_settingsKeys; } + bool getForce() const { return m_force; } + + static MsgConfigureInmarsatDemod* create(const InmarsatDemodSettings& settings, const QStringList& settingsKeys, bool force) + { + return new MsgConfigureInmarsatDemod(settings, settingsKeys, force); + } + + private: + InmarsatDemodSettings m_settings; + QStringList m_settingsKeys; + bool m_force; + + MsgConfigureInmarsatDemod(const InmarsatDemodSettings& settings, const QStringList& settingsKeys, bool force) : + Message(), + m_settings(settings), + m_settingsKeys(settingsKeys), + m_force(force) + { } + }; + + InmarsatDemod(DeviceAPI *deviceAPI); + virtual ~InmarsatDemod(); + virtual void destroy() { delete this; } + virtual void setDeviceAPI(DeviceAPI *deviceAPI); + virtual DeviceAPI *getDeviceAPI() { return m_deviceAPI; } + + using BasebandSampleSink::feed; + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end, bool po); + virtual void start(); + virtual void stop(); + virtual void pushMessage(Message *msg) { m_inputMessageQueue.push(msg); } + virtual QString getSinkName() { return objectName(); } + + virtual void getIdentifier(QString& id) { id = objectName(); } + virtual QString getIdentifier() const { return objectName(); } + virtual const QString& getURI() const { return getName(); } + virtual void getTitle(QString& title) { title = m_settings.m_title; } + virtual qint64 getCenterFrequency() const { return m_settings.m_inputFrequencyOffset; } + virtual void setCenterFrequency(qint64 frequency); + + virtual QByteArray serialize() const; + virtual bool deserialize(const QByteArray& data); + + virtual int getNbSinkStreams() const { return 1; } + virtual int getNbSourceStreams() const { return 0; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + + virtual qint64 getStreamCenterFrequency(int streamIndex, bool sinkElseSource) const + { + (void) streamIndex; + (void) sinkElseSource; + return 0; + } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiWorkspaceGet( + SWGSDRangel::SWGWorkspaceInfo& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiReportGet( + SWGSDRangel::SWGChannelReport& response, + QString& errorMessage); + + static void webapiFormatChannelSettings( + SWGSDRangel::SWGChannelSettings& response, + const InmarsatDemodSettings& settings); + + static void webapiUpdateChannelSettings( + InmarsatDemodSettings& settings, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response); + + ScopeVis *getScopeSink(); + double getMagSq() const { return m_basebandSink->getMagSq(); } + + void getMagSqLevels(double& avg, double& peak, int& nbSamples) { + m_basebandSink->getMagSqLevels(avg, peak, nbSamples); + } + + void getPLLStatus(bool &locked, Real &coarseFreqCurrent, Real &coarseFreqCurrentPower, Real &coarseFreq, Real &coarseFreqPower, Real &fineFreq, Real &evm, bool &synced) const { + m_basebandSink->getPLLStatus(locked, coarseFreqCurrent, coarseFreqCurrentPower, coarseFreq, coarseFreqPower, fineFreq, evm, synced); + } + + uint32_t getNumberOfDeviceStreams() const; + + static const char * const m_channelIdURI; + static const char * const m_channelId; + +private: + DeviceAPI *m_deviceAPI; + QThread m_thread; + InmarsatDemodBaseband* m_basebandSink; + InmarsatDemodSettings m_settings; + int m_basebandSampleRate; //!< stored from device message used when starting baseband sink + qint64 m_centerFrequency; + QUdpSocket m_udpSocket; + QFile m_logFile; + QTextStream m_logStream; + + QNetworkAccessManager *m_networkManager; + QNetworkRequest m_networkRequest; + + virtual bool handleMessage(const Message& cmd); + void applySettings(const InmarsatDemodSettings& settings, const QStringList& settingsKeys, bool force = false); + void sendSampleRateToDemodAnalyzer(); + void webapiReverseSendSettings(const QList& channelSettingsKeys, const InmarsatDemodSettings& settings, bool force); + void webapiFormatChannelSettings( + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings *swgChannelSettings, + const InmarsatDemodSettings& settings, + bool force + ); + void webapiFormatChannelReport(SWGSDRangel::SWGChannelReport& response); + +private slots: + void networkManagerFinished(QNetworkReply *reply); + void handleIndexInDeviceSetChanged(int index); + +}; + +#endif // INCLUDE_INMARSATDEMOD_H diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodbaseband.cpp b/plugins/channelrx/demodinmarsat/inmarsatdemodbaseband.cpp new file mode 100644 index 000000000..9196f4389 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodbaseband.cpp @@ -0,0 +1,184 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019-2021 Edouard Griffiths, F4EXB // +// Copyright (C) 2020-2026 Jon Beniston, M7RCE // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "dsp/dspcommands.h" +#include "dsp/downchannelizer.h" + +#include "inmarsatdemodbaseband.h" + +MESSAGE_CLASS_DEFINITION(InmarsatDemodBaseband::MsgConfigureInmarsatDemodBaseband, Message) + +InmarsatDemodBaseband::InmarsatDemodBaseband(InmarsatDemod *packetDemod) : + m_sink(packetDemod), + m_running(false) +{ + qDebug("InmarsatDemodBaseband::InmarsatDemodBaseband"); + + m_scopeSink.setNbStreams(InmarsatDemodSettings::m_scopeStreams); + m_sink.setScopeSink(&m_scopeSink); + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(48000)); + m_channelizer = new DownChannelizer(&m_sink); +} + +InmarsatDemodBaseband::~InmarsatDemodBaseband() +{ + m_inputMessageQueue.clear(); + + delete m_channelizer; +} + +void InmarsatDemodBaseband::reset() +{ + QMutexLocker mutexLocker(&m_mutex); + m_inputMessageQueue.clear(); + m_sampleFifo.reset(); +} + +void InmarsatDemodBaseband::startWork() +{ + QMutexLocker mutexLocker(&m_mutex); + QObject::connect( + &m_sampleFifo, + &SampleSinkFifo::dataReady, + this, + &InmarsatDemodBaseband::handleData, + Qt::QueuedConnection + ); + connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + m_running = true; +} + +void InmarsatDemodBaseband::stopWork() +{ + QMutexLocker mutexLocker(&m_mutex); + disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + QObject::disconnect( + &m_sampleFifo, + &SampleSinkFifo::dataReady, + this, + &InmarsatDemodBaseband::handleData + ); + m_running = false; +} + +void InmarsatDemodBaseband::setChannel(ChannelAPI *channel) +{ + m_sink.setChannel(channel); +} + +void InmarsatDemodBaseband::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + m_sampleFifo.write(begin, end); +} + +void InmarsatDemodBaseband::handleData() +{ + QMutexLocker mutexLocker(&m_mutex); + + while ((m_sampleFifo.fill() > 0) && (m_inputMessageQueue.size() == 0)) + { + SampleVector::iterator part1begin; + SampleVector::iterator part1end; + SampleVector::iterator part2begin; + SampleVector::iterator part2end; + + std::size_t count = m_sampleFifo.readBegin(m_sampleFifo.fill(), &part1begin, &part1end, &part2begin, &part2end); + + // first part of FIFO data + if (part1begin != part1end) { + m_channelizer->feed(part1begin, part1end); + } + + // second part of FIFO data (used when block wraps around) + if(part2begin != part2end) { + m_channelizer->feed(part2begin, part2end); + } + + m_sampleFifo.readCommit((unsigned int) count); + } +} + +void InmarsatDemodBaseband::handleInputMessages() +{ + Message* message; + + while ((message = m_inputMessageQueue.pop()) != nullptr) + { + if (handleMessage(*message)) { + delete message; + } + } +} + +bool InmarsatDemodBaseband::handleMessage(const Message& cmd) +{ + if (MsgConfigureInmarsatDemodBaseband::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + MsgConfigureInmarsatDemodBaseband& cfg = (MsgConfigureInmarsatDemodBaseband&) cmd; + qDebug() << "InmarsatDemodBaseband::handleMessage: MsgConfigureInmarsatDemodBaseband"; + + applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce()); + + return true; + } + else if (DSPSignalNotification::match(cmd)) + { + QMutexLocker mutexLocker(&m_mutex); + DSPSignalNotification& notif = (DSPSignalNotification&) cmd; + qDebug() << "InmarsatDemodBaseband::handleMessage: DSPSignalNotification: basebandSampleRate: " << notif.getSampleRate(); + setBasebandSampleRate(notif.getSampleRate()); + m_sampleFifo.setSize(SampleSinkFifo::getSizePolicy(notif.getSampleRate())); + + return true; + } + else + { + return false; + } +} + +void InmarsatDemodBaseband::applySettings(const InmarsatDemodSettings& settings, const QStringList& settingsKeys, bool force) +{ + if (settingsKeys.contains("inputFrequencyOffset") || force) + { + m_channelizer->setChannelization(InmarsatDemodSettings::CHANNEL_SAMPLE_RATE, settings.m_inputFrequencyOffset); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); + } + + m_sink.applySettings(settings, settingsKeys, force); + + if (force) { + m_settings = settings; + } else { + m_settings.applySettings(settingsKeys, settings); + } +} + +int InmarsatDemodBaseband::getChannelSampleRate() const +{ + return m_channelizer->getChannelSampleRate(); +} + +void InmarsatDemodBaseband::setBasebandSampleRate(int sampleRate) +{ + m_channelizer->setBasebandSampleRate(sampleRate); + m_sink.applyChannelSettings(m_channelizer->getChannelSampleRate(), m_channelizer->getChannelFrequencyOffset()); +} diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodbaseband.h b/plugins/channelrx/demodinmarsat/inmarsatdemodbaseband.h new file mode 100644 index 000000000..8beefc1f4 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodbaseband.h @@ -0,0 +1,107 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019-2022 Edouard Griffiths, F4EXB // +// Copyright (C) 2020-2026 Jon Beniston, M7RCE // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_INMARSATDEMODBASEBAND_H +#define INCLUDE_INMARSATDEMODBASEBAND_H + +#include +#include + +#include "dsp/samplesinkfifo.h" +#include "dsp/scopevis.h" +#include "util/message.h" +#include "util/messagequeue.h" + +#include "inmarsatdemodsink.h" + +class DownChannelizer; +class ChannelAPI; +class InmarsatDemod; +class ScopeVis; + +class InmarsatDemodBaseband : public QObject +{ + Q_OBJECT +public: + class MsgConfigureInmarsatDemodBaseband : public Message { + MESSAGE_CLASS_DECLARATION + + public: + const InmarsatDemodSettings& getSettings() const { return m_settings; } + const QStringList& getSettingsKeys() const { return m_settingsKeys; } + bool getForce() const { return m_force; } + + static MsgConfigureInmarsatDemodBaseband* create(const InmarsatDemodSettings& settings, const QStringList& settingsKeys, bool force) + { + return new MsgConfigureInmarsatDemodBaseband(settings, settingsKeys, force); + } + + private: + InmarsatDemodSettings m_settings; + QStringList m_settingsKeys; + bool m_force; + + MsgConfigureInmarsatDemodBaseband(const InmarsatDemodSettings& settings, const QStringList& settingsKeys, bool force) : + Message(), + m_settings(settings), + m_settingsKeys(settingsKeys), + m_force(force) + { } + }; + + InmarsatDemodBaseband(InmarsatDemod *packetDemod); + ~InmarsatDemodBaseband(); + void reset(); + void startWork(); + void stopWork(); + void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } //!< Get the queue for asynchronous inbound communication + void getMagSqLevels(double& avg, double& peak, int& nbSamples) { + m_sink.getMagSqLevels(avg, peak, nbSamples); + } + void getPLLStatus(bool &locked, Real &coarseFreqCurrent, Real &coarseFreqCurrentPower, Real &coarseFreq, Real &coarseFreqPower, Real &fineFreq, Real &evm, bool &synced) const { + m_sink.getPLLStatus(locked, coarseFreqCurrent, coarseFreqCurrentPower, coarseFreq, coarseFreqPower, fineFreq, evm, synced); + } + void setMessageQueueToChannel(MessageQueue *messageQueue) { m_sink.setMessageQueueToChannel(messageQueue); } + void setBasebandSampleRate(int sampleRate); + int getChannelSampleRate() const; + ScopeVis *getScopeSink() { return &m_scopeSink; } + void setChannel(ChannelAPI *channel); + double getMagSq() const { return m_sink.getMagSq(); } + bool isRunning() const { return m_running; } + void setFifoLabel(const QString& label) { m_sampleFifo.setLabel(label); } + +private: + SampleSinkFifo m_sampleFifo; + DownChannelizer *m_channelizer; + InmarsatDemodSink m_sink; + MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication + InmarsatDemodSettings m_settings; + ScopeVis m_scopeSink; + bool m_running; + QRecursiveMutex m_mutex; + + bool handleMessage(const Message& cmd); + void applySettings(const InmarsatDemodSettings& settings, const QStringList& settingsKeys, bool force = false); + +private slots: + void handleInputMessages(); + void handleData(); //!< Handle data when samples have to be processed +}; + +#endif // INCLUDE_INMARSATDEMODBASEBAND_H diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodgui.cpp b/plugins/channelrx/demodinmarsat/inmarsatdemodgui.cpp new file mode 100644 index 000000000..e9be80e43 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodgui.cpp @@ -0,0 +1,1999 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2021-2026 Jon Beniston, M7RCE // +// Copyright (C) 2021-2022 Edouard Griffiths, F4EXB // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include +#include + +#include "inmarsatdemodgui.h" + +#include "ui_inmarsatdemodgui.h" +#include "device/deviceuiset.h" +#include "dsp/dspengine.h" +#include "dsp/dspcommands.h" +#include "feature/featurewebapiutils.h" +#include "plugin/pluginapi.h" +#include "util/csv.h" +#include "util/db.h" +#include "util/units.h" +#include "util/osndb.h" +#include "gui/basicchannelsettingsdialog.h" +#include "gui/dialogpositioner.h" +#include "dsp/glscopesettings.h" +#include "gui/tabletapandhold.h" +#include "maincore.h" +#include "SWGMapItem.h" + +#include "inmarsatdemod.h" + +MultipartMessage::MultipartMessage(int id, std::map params, const QDateTime& dateTime) : + m_id(id), + m_icon(nullptr), + m_latitude(0.0f), + m_longitude(0.0f) +{ + update(params, dateTime); +} + +void MultipartMessage::update(std::map params, const QDateTime& dateTime) +{ + m_dateTime = dateTime; + m_service = QString::fromStdString(params["serviceCodeAndAddressName"]); + m_priority = QString::fromStdString(params["priorityText"]); + m_address = decodeAddress( + QString::fromStdString(params["messageType"]), + QString::fromStdString(params["addressHex"]), + &m_latitude, + &m_longitude, + &m_addressCoordinates, + &m_icon + ); +} + +void MultipartMessage::addPart(const MessagePart& part) +{ + int i; + bool insert = false; + + for (i = 0; i < m_parts.size(); i++) + { + if ((m_parts[i].m_packet == part.m_packet) && (m_parts[i].m_part == part.m_part)) + { + m_parts[i] = part; + break; + } + else if ((m_parts[i].m_packet == part.m_packet) && (m_parts[i].m_part > part.m_part)) + { + insert = true; + break; + } + else if (m_parts[i].m_packet > part.m_packet) + { + insert = true; + break; + } + } + if (insert) { + m_parts.insert(i, part); + } else if (i == m_parts.size()) { + m_parts.append(part); + } + parseMessage(); +} + +QString MultipartMessage::getMessage() const +{ + QString msg; + + for (const auto& part : m_parts) { + msg = msg.append(part.m_text); + } + return msg; +} + +// Cordinates of the form: 61-02.04N 059-32.47W +QRegularExpression MultipartMessage::m_re(QStringLiteral("(\\d+)-(\\d+)(.(\\d+))?([NS]) (\\d+)-(\\d+)(.(\\d+))?([EW])")); + +void MultipartMessage::parseMessage() +{ + if (getComplete()) + { + QString message = getMessage(); + m_messageCoordinates.clear(); + + QRegularExpressionMatchIterator i = m_re.globalMatch(message); + while (i.hasNext()) + { + QRegularExpressionMatch match = i.next(); + if (match.hasMatch()) + { + int latDeg = match.captured(1).toInt(); + int latMin = match.captured(2).toInt(); + int latSec = match.captured(4).toInt(); + bool north = match.captured(5) == "N"; + int lonDeg = match.captured(6).toInt(); + int lonMin = match.captured(7).toInt(); + int lonSec = match.captured(9).toInt(); + bool east = match.captured(10) == "E"; + + float latitude = latDeg + latMin/60.0f + latSec/3600.0f; + if (!north) { + latitude = -latitude; + } + float longitude = lonDeg + lonMin/60.0f + lonSec/3600.0f; + if (!east) { + longitude = -longitude; + } + + QGeoCoordinate coord(latitude, longitude); + m_messageCoordinates.append(coord); + } + } + } +} + +void InmarsatDemodGUI::packetsCustomContextMenuRequested(QPoint pos) +{ + QTableWidgetItem *item = ui->packets->itemAt(pos); + if (item) + { + QMenu* tableContextMenu = new QMenu(ui->packets); + connect(tableContextMenu, &QMenu::aboutToHide, tableContextMenu, &QMenu::deleteLater); + QAction* copyAction = new QAction("Copy", tableContextMenu); + const QString text = item->text(); + connect(copyAction, &QAction::triggered, this, [text]()->void { + QClipboard *clipboard = QGuiApplication::clipboard(); + clipboard->setText(text); + }); + tableContextMenu->addAction(copyAction); + tableContextMenu->popup(ui->packets->viewport()->mapToGlobal(pos)); + } +} + +void InmarsatDemodGUI::messagesCustomContextMenuRequested(QPoint pos) +{ + QTableWidgetItem *item = ui->messages->itemAt(pos); + if (item) + { + QMenu* tableContextMenu = new QMenu(ui->messages); + connect(tableContextMenu, &QMenu::aboutToHide, tableContextMenu, &QMenu::deleteLater); + + QAction* copyAction = new QAction("Copy", tableContextMenu); + const QString text = item->text(); + connect(copyAction, &QAction::triggered, this, [text]()->void { + QClipboard *clipboard = QGuiApplication::clipboard(); + clipboard->setText(text); + }); + tableContextMenu->addAction(copyAction); + + QString id = ui->messages->item(item->row(), MESSAGE_COL_ID)->data(Qt::DisplayRole).toString(); + MultipartMessage *message = m_messages[id.toInt()]; + if (message->getCoordinates().size() > 0) + { + QAction* findAction = new QAction("Find on map", tableContextMenu); + connect(findAction, &QAction::triggered, this, [id]()->void { + FeatureWebAPIUtils::mapFind(id); + }); + tableContextMenu->addAction(findAction); + } + + tableContextMenu->popup(ui->messages->viewport()->mapToGlobal(pos)); + } +} + +void InmarsatDemodGUI::resizeTable() +{ + // Fill table with a row of dummy data that will size the columns nicely + // Trailing spaces are for sort arrow + int row = ui->packets->rowCount(); + ui->packets->setRowCount(row + 1); + ui->packets->setItem(row, PACKET_COL_DATE, new QTableWidgetItem("Frid Apr 15 2016-")); + ui->packets->setItem(row, PACKET_COL_TIME, new QTableWidgetItem("10:17:00")); + ui->packets->setItem(row, PACKET_COL_SAT, new QTableWidgetItem("Atlantic Ocean")); + ui->packets->setItem(row, PACKET_COL_MES, new QTableWidgetItem("123456789-")); + ui->packets->setItem(row, PACKET_COL_LES, new QTableWidgetItem("123456-15-")); + ui->packets->setItem(row, PACKET_COL_TYPE, new QTableWidgetItem("Multiframe Packet Start-")); + ui->packets->setItem(row, PACKET_COL_FRAME_NO, new QTableWidgetItem("123456")); + ui->packets->setItem(row, PACKET_COL_LCN, new QTableWidgetItem("888")); + ui->packets->setItem(row, PACKET_COL_ULF, new QTableWidgetItem("15,888.888")); + ui->packets->setItem(row, PACKET_COL_DLF, new QTableWidgetItem("15,888.888")); + ui->packets->setItem(row, PACKET_COL_MSG_ID, new QTableWidgetItem("80555301-")); + ui->packets->setItem(row, PACKET_COL_PRIORITY, new QTableWidgetItem("Urgency")); + ui->packets->setItem(row, PACKET_COL_ADDRESS, new QTableWidgetItem("90S 180W 1000 nm")); + ui->packets->setItem(row, PACKET_COL_MESSAGE, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ")); + ui->packets->setItem(row, PACKET_COL_DECODE, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ")); + ui->packets->setItem(row, PACKET_COL_DATA_HEX, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ")); + ui->packets->resizeColumnsToContents(); + ui->packets->removeRow(row); + + row = ui->messages->rowCount(); + ui->messages->setRowCount(row + 1); + ui->messages->setItem(row, MESSAGE_COL_DATE, new QTableWidgetItem("Frid Apr 15 2016-")); + ui->messages->setItem(row, MESSAGE_COL_TIME, new QTableWidgetItem("10:17:00")); + ui->messages->setItem(row, MESSAGE_COL_ID, new QTableWidgetItem("Atlantic Ocean")); + ui->messages->setItem(row, MESSAGE_COL_SERVICE, new QTableWidgetItem("SafetyNET ")); + ui->messages->setItem(row, MESSAGE_COL_PRIORITY, new QTableWidgetItem("Urgency-")); + ui->messages->setItem(row, MESSAGE_COL_ADDRESS, new QTableWidgetItem("90S 180W 1000 nm")); + ui->messages->setItem(row, MESSAGE_COL_MESSAGE, new QTableWidgetItem("ABCEDGHIJKLMNOPQRSTUVWXYZ")); + ui->messages->setItem(row, MESSAGE_COL_PARTS, new QTableWidgetItem("88/88+")); + ui->messages->resizeColumnsToContents(); + ui->messages->removeRow(row); +} + +// Columns in table reordered +void InmarsatDemodGUI::packets_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) +{ + (void) oldVisualIndex; + + m_settings.m_packetsColumnIndexes[logicalIndex] = newVisualIndex; +} + +// Column in table resized (when hidden size is 0) +void InmarsatDemodGUI::packets_sectionResized(int logicalIndex, int oldSize, int newSize) +{ + (void) oldSize; + + m_settings.m_packetsColumnSizes[logicalIndex] = newSize; +} + +// Right click in table header - show column select menu +void InmarsatDemodGUI::packetsColumnSelectMenu(QPoint pos) +{ + m_packetsMenu->popup(ui->packets->horizontalHeader()->viewport()->mapToGlobal(pos)); +} + +// Hide/show column when menu selected +void InmarsatDemodGUI::packetsColumnSelectMenuChecked(bool checked) +{ + (void) checked; + + QAction* action = qobject_cast(sender()); + if (action != nullptr) + { + int idx = action->data().toInt(nullptr); + ui->packets->setColumnHidden(idx, !action->isChecked()); + } +} + +// Columns in table reordered +void InmarsatDemodGUI::messages_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex) +{ + (void) oldVisualIndex; + + m_settings.m_messagesColumnIndexes[logicalIndex] = newVisualIndex; +} + +// Column in table resized (when hidden size is 0) +void InmarsatDemodGUI::messages_sectionResized(int logicalIndex, int oldSize, int newSize) +{ + (void) oldSize; + + m_settings.m_messagesColumnSizes[logicalIndex] = newSize; +} + + +// Right click in table header - show column select menu +void InmarsatDemodGUI::messagesColumnSelectMenu(QPoint pos) +{ + m_messagesMenu->popup(ui->messages->horizontalHeader()->viewport()->mapToGlobal(pos)); +} + +// Hide/show column when menu selected +void InmarsatDemodGUI::messagesColumnSelectMenuChecked(bool checked) +{ + (void) checked; + + QAction* action = qobject_cast(sender()); + if (action != nullptr) + { + int idx = action->data().toInt(nullptr); + ui->messages->setColumnHidden(idx, !action->isChecked()); + } +} + +// Create column select menu item +QAction *InmarsatDemodGUI::createCheckableItem(QString &text, int idx, bool checked, bool packets) +{ + QAction *action = new QAction(text, this); + action->setCheckable(true); + action->setChecked(checked); + action->setData(QVariant(idx)); + if (packets) { + connect(action, &QAction::triggered, this, &InmarsatDemodGUI::packetsColumnSelectMenuChecked); + } else { + connect(action, &QAction::triggered, this, &InmarsatDemodGUI::messagesColumnSelectMenuChecked); + } + return action; +} + +InmarsatDemodGUI* InmarsatDemodGUI::create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) +{ + InmarsatDemodGUI* gui = new InmarsatDemodGUI(pluginAPI, deviceUISet, rxChannel); + return gui; +} + +void InmarsatDemodGUI::destroy() +{ + delete this; +} + +void InmarsatDemodGUI::resetToDefaults() +{ + m_settings.resetToDefaults(); + displaySettings(); + applyAllSettings(); +} + +QByteArray InmarsatDemodGUI::serialize() const +{ + return m_settings.serialize(); +} + +bool InmarsatDemodGUI::deserialize(const QByteArray& data) +{ + if(m_settings.deserialize(data)) { + displaySettings(); + applyAllSettings(); + return true; + } else { + resetToDefaults(); + return false; + } +} + +static QString formatFreqMHz(const QString& freq) +{ + if (freq.isEmpty()) + { + return ""; + } + else + { + QLocale l; + l.setNumberOptions(l.numberOptions() & ~QLocale::OmitGroupSeparator); + + double d = freq.toDouble(); + + qint64 f = (qint64) (d * 1000000.0); + + int precision; + + if (f % 1000000 == 0) { + precision = 0; + } else if (f % 1000 == 0) { + precision = 3; + } else { + precision = 6; + } + + return l.toString(d, 'f', precision); + } +} + +static QString formatFreqMHz(std::string string) +{ + return formatFreqMHz(QString::fromStdString(string)); +} + +// https://en.wikipedia.org/wiki/NAVAREA +const QStringList MultipartMessage::m_navAreas = { + "0", + "I United Kingdom", + "II France", + "III Spain", + "IV USA", + "V Brazil", + "VI Argentina", + "VII South Africa", + "VIII India", + "IX Pakistan", + "X Australia", + "XI Japan", + "XII USA", + "XIII Russian Federation", + "XIV New Zealand", + "XV Chile", + "XVI Peru", + "XVII Canada", + "XVIII Canada", + "XIX Norway", + "XX Russian Federation", + "XXI Russian Federation" +}; + +// We use flags from ADS-B demod +const QStringList MultipartMessage::m_navAreaFlags = { + "", + "united_kingdom", + "france", + "spain", + "united_states", + "brazil", + "argentina", + "south_africa", + "india", + "pakistan", + "australia", + "japan", + "united_states", + "russia", + "new_zealand", + "chile", + "peru", + "canada", + "canada", + "norway", + "russia", + "russia" +}; + +QString MultipartMessage::decodeAddress(QString serviceCode, QString addressHex, float *latitude, float *longitude, QList *coordinates, QIcon **icon) +{ + bool ok; + int messageTypeNum = serviceCode.toInt(&ok); + + if (ok) + { + switch (messageTypeNum) + { + case 0x31: + { + // Navarea + int navArea = addressHex.left(2).toInt(&ok, 16); + if (ok && (navArea > 0) && (navArea < m_navAreas.size())) + { + if (icon) { + *icon = AircraftInformation::getFlagIcon(m_navAreaFlags[navArea]); + } + return QString("NAVAREA %1").arg(m_navAreas[navArea]); + } + else + { + return QString("NAVAREA Unknown %1").arg(navArea); + } + } + break; + + case 0x13: + { + QByteArray addressBytes = QByteArray::fromHex(addressHex.toLatin1()); + // Navarea - TBD B1/B2 + int navArea = addressBytes[0]; + if ((navArea > 0) && (navArea < m_navAreas.size())) + { + if (icon) { + *icon = AircraftInformation::getFlagIcon(m_navAreaFlags[navArea]); + } + return QString("NAVAREA %1").arg(m_navAreas[navArea]); + } + else + { + return QString("NAVAREA Unknown %1").arg(navArea); + } + } + break; + + case 0x04: + case 0x34: + { + // Rectangular area + QByteArray addressBytes = QByteArray::fromHex(addressHex.toLatin1()); + int south = (addressBytes[0] >> 7) & 1; + int latDeg = addressBytes[0] & 0x7f; + int lonDeg = (addressBytes[1] & 0xff) << 1; + int west = (addressBytes[2] >> 7) & 1; + int latExtentNorth = addressBytes[2] & 0x7f; + int latExtentEast = addressBytes[3] & 0xff; + + if (coordinates) + { + int lat1Deg = west ? -latDeg : latDeg; + int lon1Deg = south ? -lonDeg : lonDeg; + int lat2Deg = lat1Deg + latExtentEast; + int lon2Deg = lon1Deg + latExtentNorth; + *latitude = lat1Deg + lat2Deg / 2; + *longitude = lon1Deg + lon2Deg / 2; + coordinates->clear(); + coordinates->append(QGeoCoordinate(latDeg, lonDeg)); + coordinates->append(QGeoCoordinate(lat2Deg, lonDeg)); + coordinates->append(QGeoCoordinate(lat2Deg, lon2Deg)); + coordinates->append(QGeoCoordinate(latDeg, lon2Deg)); + coordinates->append(QGeoCoordinate(latDeg, lonDeg)); + } + + return QString("%1%2 %3%4 %5%7N %6%7E") + .arg(south ? "S" : "N") + .arg(latDeg) + .arg(west ? "W" : "E") + .arg(lonDeg) + .arg(latExtentNorth) + .arg(latExtentEast) + .arg(QChar(0x00B0)); + } + break; + + case 0x14: + case 0x24: + case 0x44: + { + // Circular area + QByteArray addressBytes = QByteArray::fromHex(addressHex.toLatin1()); + int south = (addressBytes[0] >> 7) & 1; + int latDeg = addressBytes[0] & 0x7f; + int lonDeg = (addressBytes[1] & 0xff) << 1; + int west = (addressBytes[2] >> 7) & 1; + int radius = ((addressBytes[2] & 0x7f) << 8) | (addressBytes[3] & 0xff); + + if (coordinates) + { + int lat1Deg = west ? -latDeg : latDeg; + int lon1Deg = south ? -lonDeg : lonDeg; + QGeoCoordinate centre(lat1Deg, lon1Deg); + + *latitude = lat1Deg; + *longitude = lon1Deg; + coordinates->clear(); + for (int theta = 0; theta <= 360; theta += 10) { + coordinates->append(centre.atDistanceAndAzimuth(Units::nauticalMilesToMetres(radius), theta)); + } + } + + return QString("%1%2 %3%4 %5nm") + .arg(south ? "S" : "N") + .arg(latDeg) + .arg(west ? "W" : "E") + .arg(lonDeg) + .arg(radius); + } + + case 0x11: + { + // System message address + QStringList systemAddresses = { + "All mobiles", + "AOR (East)", + "AOR (West)", + "POR", + "IOR", + "Inmarsat-A MES", + "Inmarsat-C MES", + "Inmarsat-B MES", + "Inmarsat-M MES", + "Inmarsat-B/M MESs", + "Inmarsat Aero-C AMESs" + }; + QByteArray addressBytes = QByteArray::fromHex(addressHex.toLatin1()); + int address = addressBytes[0] & 0xff; + if (address < systemAddresses.size()) { + return systemAddresses[address]; + } else { + return "Unknown"; + } + } + break; + + case 23: + case 33: + { + // MES Id + QByteArray addressBytes = QByteArray::fromHex(addressHex.toLatin1()); + + unsigned address = ((addressBytes[0] & 0xff) << 16) | ((addressBytes[1] & 0xff) << 8) | (addressBytes[2] & 0xff); + + return QString::number(address); + } + break; + + case 73: + { + // Fixed area number + QByteArray addressBytes = QByteArray::fromHex(addressHex.toLatin1()); + + unsigned address = ((addressBytes[0] & 0xff) << 16) | ((addressBytes[1] & 0xff) << 8) | (addressBytes[2] & 0xff); + + return QString::number(address); + } + break; + + default: + return addressHex; + } + } + else + { + return addressHex; + } +} + +void InmarsatDemodGUI::updateMessageTable(MultipartMessage *message) +{ + QScrollBar *sb = ui->messages->verticalScrollBar(); + bool scrollToBottom = sb->value() == sb->maximum(); + + // Find if message already in table + + int row; + bool found = false; + + for (row = 0; row < ui->messages->rowCount(); row++) + { + if (ui->messages->item(row, MESSAGE_COL_ID)->data(Qt::DisplayRole) == message->getId()) + { + found = true; + break; + } + } + + QTableWidgetItem *dateItem; + QTableWidgetItem *timeItem; + QTableWidgetItem *serviceItem; + QTableWidgetItem *priorityItem; + QTableWidgetItem *addressItem; + QTableWidgetItem *messageItem; + QTableWidgetItem *partsItem; + + if (!found) + { + ui->messages->setSortingEnabled(false); + row = ui->messages->rowCount(); + ui->messages->setRowCount(row + 1); + + dateItem = new QTableWidgetItem(); + timeItem = new QTableWidgetItem(); + QTableWidgetItem *idItem = new QTableWidgetItem(); + serviceItem = new QTableWidgetItem(); + priorityItem = new QTableWidgetItem(); + addressItem = new QTableWidgetItem(); + messageItem = new QTableWidgetItem(); + partsItem = new QTableWidgetItem(); + + ui->messages->setItem(row, MESSAGE_COL_DATE, dateItem); + ui->messages->setItem(row, MESSAGE_COL_TIME, timeItem); + ui->messages->setItem(row, MESSAGE_COL_ID, idItem); + ui->messages->setItem(row, MESSAGE_COL_SERVICE, serviceItem); + ui->messages->setItem(row, MESSAGE_COL_PRIORITY, priorityItem); + ui->messages->setItem(row, MESSAGE_COL_ADDRESS, addressItem); + ui->messages->setItem(row, MESSAGE_COL_MESSAGE, messageItem); + ui->messages->setItem(row, MESSAGE_COL_PARTS, partsItem); + + idItem->setData(Qt::DisplayRole, message->getId()); + } + else + { + dateItem = ui->messages->item(row, MESSAGE_COL_DATE); + timeItem = ui->messages->item(row, MESSAGE_COL_TIME); + serviceItem = ui->messages->item(row, MESSAGE_COL_SERVICE); + priorityItem = ui->messages->item(row, MESSAGE_COL_PRIORITY); + addressItem = ui->messages->item(row, MESSAGE_COL_ADDRESS); + messageItem = ui->messages->item(row, MESSAGE_COL_MESSAGE); + partsItem = ui->messages->item(row, MESSAGE_COL_PARTS); + } + + dateItem->setData(Qt::DisplayRole, message->getDateTime().date()); + timeItem->setData(Qt::DisplayRole, message->getDateTime().time()); + serviceItem->setText(message->getService()); + priorityItem->setText(message->getPriority()); + addressItem->setText(message->getAddress()); + QIcon *icon = message->getAddressIcon(); + if (icon) { + addressItem->setIcon(*icon); + } + messageItem->setText(message->getMessage()); + QString parts = QString("%1 / %2%3") + .arg(message->getParts()) + .arg(message->getTotalParts()) + .arg(message->getComplete() ? "" : "+"); + partsItem->setData(Qt::DisplayRole, parts); + + filterRow(ui->messages, row, -1, MESSAGE_COL_MESSAGE); + + if (!found && !m_loadingData) + { + ui->messages->setSortingEnabled(true); + if (scrollToBottom) { + ui->messages->scrollToBottom(); + } + } +} + +void InmarsatDemodGUI::decodeAppend(QString& decode, const QString& title, const std::string& variable, inmarsatc::frameParser::FrameParser::frameParser_result& frame) +{ + decode = decode.append("" + title + " " + QString::fromStdString(frame.decoding_result.packetVars[variable])); +} + +void InmarsatDemodGUI::decodeAppendFreqMHz(QString& decode, const QString& title, const std::string& variable, inmarsatc::frameParser::FrameParser::frameParser_result& frame) +{ + decode = decode.append("" + title + " " + formatFreqMHz(frame.decoding_result.packetVars[variable])); +} + +void InmarsatDemodGUI::decodeAppendHTML(QString& decode, const QString& title, const std::string& variable, inmarsatc::frameParser::FrameParser::frameParser_result& frame) +{ + decode = decode.append("" + title + " " + toHTML(frame.decoding_result.packetVars[variable])); +} + +// Add row to table +void InmarsatDemodGUI::packetReceived(const QByteArray& bytes, QDateTime dateTime) +{ + inmarsatc::decoder::Decoder::decoder_result decoderResult; + + memcpy(&decoderResult, bytes.data(), sizeof(decoderResult)); + + std::vector parserResults = m_parser.parseFrame(decoderResult); + + for (auto& frame : parserResults) + { + if (!(!frame.decoding_result.isDecodedPacket || !frame.decoding_result.isCrc)) + { + // Is scroll bar at bottom + QScrollBar *sb = ui->packets->verticalScrollBar(); + bool scrollToBottom = sb->value() == sb->maximum(); + + ui->packets->setSortingEnabled(false); + int row = ui->packets->rowCount(); + ui->packets->setRowCount(row + 1); + + QTableWidgetItem *dateItem = new QTableWidgetItem(); + QTableWidgetItem *timeItem = new QTableWidgetItem(); + QTableWidgetItem *satItem = new QTableWidgetItem(); + QTableWidgetItem *mesItem = new QTableWidgetItem(); + QTableWidgetItem *lesItem = new QTableWidgetItem(); + QTableWidgetItem *msgIdItem = new QTableWidgetItem(); + QTableWidgetItem *typeItem = new QTableWidgetItem(); + QTableWidgetItem *frameNoItem = new QTableWidgetItem(); + QTableWidgetItem *lcnItem = new QTableWidgetItem(); + QTableWidgetItem *ulfItem = new QTableWidgetItem(); + QTableWidgetItem *dlfItem = new QTableWidgetItem(); + QTableWidgetItem *priorityItem = new QTableWidgetItem(); + QTableWidgetItem *addressItem = new QTableWidgetItem(); + QTableWidgetItem *messageItem = new QTableWidgetItem(); + QTableWidgetItem *decodeItem = new QTableWidgetItem(); + QTableWidgetItem *dataHexItem = new QTableWidgetItem(); + QLabel *decodeLabel = new QLabel(); + ui->packets->setItem(row, PACKET_COL_DATE, dateItem); + ui->packets->setItem(row, PACKET_COL_TIME, timeItem); + ui->packets->setItem(row, PACKET_COL_SAT, satItem); + ui->packets->setItem(row, PACKET_COL_MES, mesItem); + ui->packets->setItem(row, PACKET_COL_LES, lesItem); + ui->packets->setItem(row, PACKET_COL_MSG_ID, msgIdItem); + ui->packets->setItem(row, PACKET_COL_TYPE, typeItem); + ui->packets->setItem(row, PACKET_COL_FRAME_NO, frameNoItem); + ui->packets->setItem(row, PACKET_COL_LCN, lcnItem); + ui->packets->setItem(row, PACKET_COL_ULF, ulfItem); + ui->packets->setItem(row, PACKET_COL_DLF, dlfItem); + ui->packets->setItem(row, PACKET_COL_PRIORITY, priorityItem); + ui->packets->setItem(row, PACKET_COL_ADDRESS, addressItem); + ui->packets->setItem(row, PACKET_COL_MESSAGE, messageItem); + ui->packets->setItem(row, PACKET_COL_DECODE, decodeItem); + ui->packets->setItem(row, PACKET_COL_DATA_HEX, dataHexItem); + ui->packets->setCellWidget(row, PACKET_COL_DECODE, decodeLabel); + + dateItem->setText(dateTime.date().toString()); + timeItem->setText(dateTime.time().toString()); + + typeItem->setText(QString::fromStdString(frame.decoding_result.packetVars["packetDescriptorText"])); + frameNoItem->setText(QString::number(frame.decoding_result.frameNumber)); + + satItem->setText(QString::fromStdString(frame.decoding_result.packetVars["satName"])); + mesItem->setText(QString::fromStdString(frame.decoding_result.packetVars["mesId"])); + QString lesName = QString::fromStdString(frame.decoding_result.packetVars["lesName"]); + if (!lesName.isEmpty()) { + lesItem->setText(lesName); + } else { + lesItem->setText(QString::fromStdString(frame.decoding_result.packetVars["les"])); + } + lcnItem->setText(QString::fromStdString(frame.decoding_result.packetVars["logicalChannelNo"])); + ulfItem->setText(formatFreqMHz(frame.decoding_result.packetVars["uplinkChannelMhz"])); + dlfItem->setText(formatFreqMHz(frame.decoding_result.packetVars["downlinkChannelMhz"])); + msgIdItem->setText(QString::fromStdString(frame.decoding_result.packetVars["messageId"])); + priorityItem->setText(QString::fromStdString(frame.decoding_result.packetVars["priorityText"])); + + QIcon *icon = nullptr; + QString address = MultipartMessage::decodeAddress( + QString::fromStdString(frame.decoding_result.packetVars["messageType"]), + QString::fromStdString(frame.decoding_result.packetVars["addressHex"]), + nullptr, + nullptr, + nullptr, + &icon + ); + addressItem->setText(address); + if (icon) { + addressItem->setIcon(*icon); + } + + QString message; + QString decode; + + quint8 type; + if (((frame.decoding_result.packetDescriptor >> 7) & 1) == 0) { + type = frame.decoding_result.packetDescriptor >> 4; + } else { + type = frame.decoding_result.packetDescriptor & 0x3f; + } + + switch(type) + { + case 0x00: // Acknowledgement Request + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "Sat", "satName", frame); + decodeAppend(decode, "LES", "lesName", frame); + decodeAppend(decode, "LCN", "logicalChannelNo", frame); + decodeAppendFreqMHz(decode, "ULF", "uplinkChannelMhz", frame); + break; + + case 0x01: // Announcement + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "Sat", "satName", frame); + decodeAppend(decode, "MES", "mesId", frame); + decodeAppend(decode, "LES", "lesName", frame); + decodeAppendFreqMHz(decode, "DLF", "downlinkChannelMhz", frame); + decodeAppend(decode, "Service", "serviceText", frame); + decodeAppend(decode, "Direction", "directionText", frame); + decodeAppend(decode, "Priority", "priorityText", frame); + decodeAppend(decode, "LCN", "logicalChannelNo", frame); + decodeAppend(decode, "Message Reference Number", "messageReferenceNumber", frame); + decodeAppend(decode, "Sub-address", "subAddress", frame); + decodeAppend(decode, "Presentation", "presentationText", frame); + decodeAppend(decode, "Packets", "packets", frame); + decodeAppend(decode, "Last Count", "lastCount", frame); + break; + + case 0x02: // Clear + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "Sat", "satName", frame); + decodeAppend(decode, "MES", "mesId", frame); + decodeAppend(decode, "LES", "lesName", frame); + decodeAppend(decode, "LCN", "logicalChannelNo", frame); + break; + + case 0x03: // Logical Channel Assignment + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "Sat", "satName", frame); + decodeAppend(decode, "MES", "mesId", frame); + decodeAppend(decode, "LES", "lesName", frame); + decodeAppend(decode, "Status Bits", "status_bits", frame); + decodeAppend(decode, "LCN", "logicalChannelNo", frame); + decodeAppend(decode, "Frame Length", "frameLength", frame); + decodeAppend(decode, "Duration", "duration", frame); + decodeAppendFreqMHz(decode, "DLF", "downlinkChannelMhz", frame); + decodeAppendFreqMHz(decode, "ULF", "uplinkChannelMhz", frame); + decodeAppend(decode, "Frame Offset", "frameOffset", frame); + decodeAppend(decode, "Packet Descriptor 1", "packetDescriptor1", frame); + break; + + case 0x06: // Signalling Channel Descriptor + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppendFreqMHz(decode, "ULF", "uplinkChannelMhz", frame); + decodeAppendHTML(decode, "Services", "services", frame); + decodeAppendHTML(decode, "TDM slots", "tdmSlots", frame); + break; + + case 0x07: // Bulletin Board + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "Network Version", "networkVersion", frame); + decodeAppend(decode, "Sat", "satName", frame); + decodeAppend(decode, "LES", "lesName", frame); + decodeAppend(decode, "Signalling Channel", "signallingChannel", frame); + decodeAppend(decode, "Count", "count", frame); + decodeAppend(decode, "Channel Type Name", "channelTypeName", frame); + decodeAppend(decode, "Local", "local", frame); + decodeAppend(decode, "Random Interval", "randomInterval", frame); + decodeAppendHTML(decode, "Status", "status", frame); + decodeAppendHTML(decode, "Services", "services", frame); + break; + + case 0x12: // Login Ack + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "MES", "mesId", frame); + decodeAppendFreqMHz(decode, "DLF", "downlinkChannelMhz", frame); + if (frame.decoding_result.packetVars.find("networkVesrion") != frame.decoding_result.packetVars.end()) { + decodeAppend(decode, "Network Version", "networkVersion", frame); + } + if (frame.decoding_result.packetVars.find("stationCount") != frame.decoding_result.packetVars.end()) + { + decodeAppend(decode, "Station Count", "stationCount", frame); + decodeAppendHTML(decode, "Stations", "stations", frame); + } + break; + + case 0x13: // Logout Ack + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "MES", "mesId", frame); + break; + + case 0x22: // Group Poll + { + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "Sat", "satName", frame); + decodeAppend(decode, "Data Network ID", "dataNetworkId", frame); + decodeAppend(decode, "LES", "lesName", frame); + decodeAppend(decode, "LES TDM", "lesTDM", frame); + decodeAppend(decode, "Randomizing Inverval", "randomizingInterval", frame); + decodeAppend(decode, "Sub-address", "subAddress", frame); + decodeAppend(decode, "Response", "responseText", frame); + decodeAppend(decode, "Command", "commandText", frame); + decodeAppend(decode, "Sequence No", "sequenceNo", frame); + qint8 command = QString::fromStdString(frame.decoding_result.packetVars["command"]).toInt(); + if (command == 0x4) + { + decodeAppend(decode, "Start Frame", "startFrame", frame); + decodeAppend(decode, "Interval", "interval", frame); + } + else if (command == 0x8) + { + decodeAppend(decode, "MEM Id", "memId", frame); + } + else if (command == 0xa) + { + decodeAppend(decode, "Member Number", "memberNumber", frame); + } + if (frame.decoding_result.packetVars.find("shortMessage") != frame.decoding_result.packetVars.end()) + { + std::string shortMessage = frame.decoding_result.packetVars["shortMessage"]; + for (int k = 0; k < (int)shortMessage.length(); k++) + { + char chr = shortMessage[k] & 0x7F; + if ((chr < 0x20 && chr != '\n' && chr != '\r')) { + message = message.append("(" + QString::number(chr) + ")"); + } else { + message = message.append(chr); + } + } + decode = decode.append("Text" + message); + } + QString textHex = QString::fromStdString(frame.decoding_result.packetVars["text"]); + if (!textHex.isEmpty()) { + decode = decode.append("Text (hex)" + textHex); + } + break; + } + + case 0x23: // Individual Poll + { + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "Sat", "satName", frame); + decodeAppend(decode, "MES", "mesId", frame); + decodeAppend(decode, "LES", "lesName", frame); + decodeAppend(decode, "Sub-address", "subAddress", frame); + decodeAppend(decode, "Data Network ID", "dataNetworkId", frame); + decodeAppend(decode, "Response", "responseText", frame); + decodeAppend(decode, "Command", "commandText", frame); + decodeAppend(decode, "Sequence No", "sequenceNo", frame); + qint8 command = QString::fromStdString(frame.decoding_result.packetVars["command"]).toInt(); + if (command == 0x4) + { + decodeAppend(decode, "Start Frame", "startFrame", frame); + decodeAppend(decode, "Interval", "interval", frame); + } + else if (command == 0x8) + { + decodeAppend(decode, "MEM Id", "memId", frame); + } + else if (command == 0xa) + { + decodeAppend(decode, "Member Number", "memberNumber", frame); + } + if (frame.decoding_result.packetVars.find("shortMessage") != frame.decoding_result.packetVars.end()) + { + std::string shortMessage = frame.decoding_result.packetVars["shortMessage"]; + for (int k = 0; k < (int)shortMessage.length(); k++) + { + char chr = shortMessage[k] & 0x7F; + if ((chr < 0x20 && chr != '\n' && chr != '\r')) { + message = message.append("(" + QString::number(chr) + ")"); + } else { + message = message.append(chr); + } + } + decode = decode.append("Text" + message); + } + QString textHex = QString::fromStdString(frame.decoding_result.packetVars["text"]); + if (!textHex.isEmpty()) { + decode = decode.append("Text (hex)" + textHex); + } + break; + } + + case 0x28: // Confirmation + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "Sat", "satName", frame); + decodeAppend(decode, "MES", "mesId", frame); + decodeAppend(decode, "LES", "lesName", frame); + decodeAppend(decode, "Message Reference Number", "messageReferenceNumber", frame); + decodeAppendHTML(decode, "Message Status", "messageStatusDescriptors", frame); + break; + + case 0x2A: // Message + { + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "Sat", "satName", frame); + decodeAppend(decode, "LES", "lesName", frame); + decodeAppend(decode, "LCN", "logicalChannelNo", frame); + decodeAppend(decode, "Packet No", "packetNo", frame); + //bool isBinary = frame.decoding_result.payload.presentation == PACKETDECODER_PRESENTATION_BINARY; + if (frame.decoding_result.payload.presentation == PACKETDECODER_PRESENTATION_IA5) + { + for (int i = 0; i < (int)frame.decoding_result.payload.data8Bit.size(); i++) + { + char chr = frame.decoding_result.payload.data8Bit[i] & 0x7F; + if ((chr < 0x20 && chr != '\n' && chr != '\r')) { + message = message.append("(" + QString::number((uint16_t)chr, 16) + ")"); + } else { + message = message.append(chr); + } + } + } + else if (frame.decoding_result.payload.presentation == PACKETDECODER_PRESENTATION_ITA2) + { + + } + else + { + for (int i = 0; i < (int)frame.decoding_result.payload.data8Bit.size(); i++) + { + uint8_t data = frame.decoding_result.payload.data8Bit[i]; + message = message.append(QString::number((uint16_t)data, 16) + " "); + } + } + } + decode = decode.append("Payload" + message); + break; + + case 0x2B: // Network Update + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "LES List Length", "lesListLength", frame); + decodeAppend(decode, "Station Start Hex", "stationStartHex", frame); + decodeAppend(decode, "Station Count", "stationCount", frame); + decodeAppendHTML(decode, "Stations", "stations", frame); + break; + + case 0x30: // EGC (Single Header) + case 0x31: // EGC (First of Double Header) + case 0x32: // EGC (Second of Double Header) + { + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "Service Code", "serviceCodeAndAddressName", frame); + decodeAppend(decode, "Continuation", "continuation", frame); + decodeAppend(decode, "Priority", "priorityText", frame); + decodeAppend(decode, "Repetition Number", "repetition", frame); + decodeAppend(decode, "Message Sequence Number", "messageId", frame); + decodeAppend(decode, "Packet Sequence Number", "packetNo", frame); + decodeAppend(decode, "Presentation", "presentationText", frame); + decodeAppend(decode, "Sat", "satName", frame); + decodeAppend(decode, "LES", "lesName", frame); + decode = decode.append("Address" + address); + decodeAppend(decode, "Checksum", "egcChecksum", frame); + + bool isBinary = frame.decoding_result.payload.presentation == PACKETDECODER_PRESENTATION_BINARY; + if (!isBinary) + { + for (int k = 0; k < (int)frame.decoding_result.payload.data8Bit.size(); k++) { + char chr = frame.decoding_result.payload.data8Bit[k] & 0x7F; + if ((chr < 0x20 && chr != '\n' && chr != '\r')) { + message = message.append("(" + QString::number((uint16_t)chr, 16) + ")"); + } else { + message = message.append(chr); + } + } + } + else + { + for (int i = 0; i < (int)frame.decoding_result.payload.data8Bit.size(); i++) + { + uint8_t data = frame.decoding_result.payload.data8Bit[i]; + message = message.append(QString::number(data, 16) + " "); + } + } + decode = decode.append("Message" + message); + } + break; + + case 0x2c: // Request Status + { + decodeAppend(decode, "Packet", "packetDescriptorText", frame); + decodeAppend(decode, "Sat", "satName", frame); + decodeAppend(decode, "MES", "mesId", frame); + decodeAppend(decode, "LES", "lesName", frame); + QString flag = QString::fromStdString(frame.decoding_result.packetVars["rejected"]) == "0" ? "Pending" : "Rejected"; + decode = decode.append("Pending / Reject Flag " + flag); + decodeAppend(decode, "Status Code", "statusCodeText", frame); + break; + } + + } + + messageItem->setText(message); + decodeLabel->setText(decode); + + QByteArray frameBytes((char *) decoderResult.decodedFrame, decoderResult.length); + dataHexItem->setText(frameBytes.toHex()); + + filterRow(ui->packets, row, PACKET_COL_TYPE, PACKET_COL_MESSAGE); + if (!m_loadingData) + { + ui->packets->setSortingEnabled(true); + if (scrollToBottom) { + ui->packets->scrollToBottom(); + } + } + + QString messageId = QString::fromStdString(frame.decoding_result.packetVars["messageId"]); + if (!messageId.isEmpty()) + { + int id = messageId.toInt(); + MultipartMessage *multipartMessage = nullptr; + if (m_messages.contains(id)) + { + multipartMessage = m_messages[id]; + multipartMessage->update(frame.decoding_result.packetVars, dateTime); + } + else + { + multipartMessage = new MultipartMessage(id, frame.decoding_result.packetVars, dateTime); + m_messages.insert(id, multipartMessage); + } + MessagePart part; + part.m_part = frame.decoding_result.packetDescriptor == 0xb1 ? 1 : 2; + part.m_packet = QString::fromStdString(frame.decoding_result.packetVars["packetNo"]).toInt(); + part.m_continuation = QString::fromStdString(frame.decoding_result.packetVars["continuation"]) == "1"; + part.m_text = message; + multipartMessage->addPart(part); + updateMessageTable(multipartMessage); + + if (!multipartMessage->getCoordinates().isEmpty()) + { + QString name = QString::number(multipartMessage->getId()); + QString messageText = multipartMessage->getMessage().replace("\n", "
"); + sendAreaToMapFeature(name, messageText, multipartMessage->getLatitude(), multipartMessage->getLongitude(), multipartMessage->getCoordinates()); + } + } + } + } +} + +QString InmarsatDemodGUI::toHTML(const std::string& string) const +{ + return toHTML(QString::fromStdString(string)); +} + +QString InmarsatDemodGUI::toHTML(const QString& string) const +{ + QString s = string; + return s.replace("\n", "
"); +} + +void InmarsatDemodGUI::sendAreaToMapFeature(const QString& name, const QString& text, float latitude, float longitude, const QList& coordinates) +{ + // Send to Map feature + QList mapPipes; + MainCore::instance()->getMessagePipes().getMessagePipes(m_inmarsatDemod, "mapitems", mapPipes); + + if (mapPipes.size() > 0) + { + if (!m_mapItems.contains(name)) { + m_mapItems.append(name); + } + + for (const auto& pipe : mapPipes) + { + MessageQueue *messageQueue = qobject_cast(pipe->m_element); + SWGSDRangel::SWGMapItem *swgMapItem = new SWGSDRangel::SWGMapItem(); + + swgMapItem->setName(new QString(name)); + swgMapItem->setLatitude(latitude); + swgMapItem->setLongitude(longitude); + swgMapItem->setAltitude(0.0); + QString image = QString("none"); + swgMapItem->setImage(new QString(image)); + swgMapItem->setImageRotation(0); + swgMapItem->setText(new QString(text)); // Not used - label is used instead for now + swgMapItem->setLabel(new QString(text)); + swgMapItem->setAltitudeReference(0); + + QList *coords = new QList(); + + for (const auto& geoC : coordinates) + { + SWGSDRangel::SWGMapCoordinate* c = new SWGSDRangel::SWGMapCoordinate(); + c->setLatitude(geoC.latitude()); + c->setLongitude(geoC.longitude()); + c->setAltitude(geoC.altitude()); + coords->append(c); + } + + swgMapItem->setCoordinates(coords); + swgMapItem->setType(3); + + MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_inmarsatDemod, swgMapItem); + messageQueue->push(msg); + } + } +} + +void InmarsatDemodGUI::clearAreaFromMapFeature(const QString& name) +{ + QList mapPipes; + MainCore::instance()->getMessagePipes().getMessagePipes(m_inmarsatDemod, "mapitems", mapPipes); + for (const auto& pipe : mapPipes) + { + MessageQueue *messageQueue = qobject_cast(pipe->m_element); + SWGSDRangel::SWGMapItem *swgMapItem = new SWGSDRangel::SWGMapItem(); + swgMapItem->setName(new QString(name)); + swgMapItem->setImage(new QString("")); + swgMapItem->setType(3); + MainCore::MsgMapItem *msg = MainCore::MsgMapItem::create(m_inmarsatDemod, swgMapItem); + messageQueue->push(msg); + } + m_mapItems.removeAll(name); +} + +void InmarsatDemodGUI::clearAllFromMapFeature() +{ + while (m_mapItems.size() > 0) { + clearAreaFromMapFeature(m_mapItems[0]); + } +} + + +void InmarsatDemodGUI::on_messages_itemSelectionChanged() +{ + QList items = ui->messages->selectedItems(); + if (items.size() > 0) + { + ui->packets->clearSelection(); + + int row = items[0]->row(); + QString message = ui->messages->item(row, MESSAGE_COL_MESSAGE)->text(); + ui->decode->setText(message); + } +} + +void InmarsatDemodGUI::on_packets_itemSelectionChanged() +{ + QList items = ui->packets->selectedItems(); + if (items.size() > 0) + { + ui->messages->clearSelection(); + + int row = items[0]->row(); + // Display decoded packet + QLabel *label = qobject_cast(ui->packets->cellWidget(row, PACKET_COL_DECODE)); + QString decode = label->text(); + ui->decode->setText(decode); + } +} + +bool InmarsatDemodGUI::handleMessage(const Message& message) +{ + if (InmarsatDemod::MsgConfigureInmarsatDemod::match(message)) + { + qDebug("InmarsatDemodGUI::handleMessage: InmarsatDemod::MsgConfigureInmarsatDemod"); + const InmarsatDemod::MsgConfigureInmarsatDemod& cfg = (InmarsatDemod::MsgConfigureInmarsatDemod&) message; + if (cfg.getForce()) { + m_settings = cfg.getSettings(); + } else { + m_settings.applySettings(cfg.getSettingsKeys(), cfg.getSettings()); + } + blockApplySettings(true); + ui->scopeGUI->updateSettings(); + m_channelMarker.updateSettings(static_cast(m_settings.m_channelMarker)); + displaySettings(); + blockApplySettings(false); + return true; + } + else if (DSPSignalNotification::match(message)) + { + DSPSignalNotification& notif = (DSPSignalNotification&) message; + m_deviceCenterFrequency = notif.getCenterFrequency(); + m_basebandSampleRate = notif.getSampleRate(); + ui->deltaFrequency->setValueRange(false, 7, -m_basebandSampleRate/2, m_basebandSampleRate/2); + ui->deltaFrequencyLabel->setToolTip(tr("Range %1 %L2 Hz").arg(QChar(0xB1)).arg(m_basebandSampleRate/2)); + updateAbsoluteCenterFrequency(); + return true; + } + else if (MainCore::MsgPacket::match(message)) + { + MainCore::MsgPacket& report = (MainCore::MsgPacket&) message; + packetReceived(report.getPacket(), report.getDateTime()); + return true; + } + + return false; +} + +void InmarsatDemodGUI::handleInputMessages() +{ + Message* message; + + while ((message = getInputMessageQueue()->pop()) != 0) + { + if (handleMessage(*message)) + { + delete message; + } + } +} + +void InmarsatDemodGUI::channelMarkerChangedByCursor() +{ + ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); + m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + applySetting("inputFrequencyOffset"); +} + +void InmarsatDemodGUI::channelMarkerHighlightedByCursor() +{ + setHighlighted(m_channelMarker.getHighlighted()); +} + +void InmarsatDemodGUI::on_deltaFrequency_changed(qint64 value) +{ + m_channelMarker.setCenterFrequency(value); + m_settings.m_inputFrequencyOffset = m_channelMarker.getCenterFrequency(); + updateAbsoluteCenterFrequency(); + applySetting("inputFrequencyOffset"); +} + +void InmarsatDemodGUI::on_rfBW_valueChanged(int value) +{ + float bw = value * 100.0f; + ui->rfBWText->setText(QString("%1k").arg(value / 10.0, 0, 'f', 1)); + m_channelMarker.setBandwidth(bw); + m_settings.m_rfBandwidth = bw; + applySetting("rfBandwidth"); +} + +void InmarsatDemodGUI::on_equalizer_currentIndexChanged(int index) +{ + m_settings.m_equalizer = (InmarsatDemodSettings::Equalizer) index; + applySetting("equalizer"); +} + +void InmarsatDemodGUI::on_rrcRolloff_valueChanged(int value) +{ + ui->rrcRolloffText->setText(QString("%1").arg(value / 100.0, 0, 'f', 2)); + m_settings.m_rrcRolloff = value / 100.0; + applySetting("rrfRolloff"); +} + +void InmarsatDemodGUI::on_pll_clicked(bool checked) +{ + ui->rrcRolloff->setVisible(checked); + ui->rrcRolloffLabel->setVisible(checked); + ui->rrcRolloffText->setVisible(checked); + ui->rrcRolloffLine->setVisible(checked); + ui->pllBW->setVisible(checked); + ui->pllBWText->setVisible(checked); + ui->pllBWLabel->setVisible(checked); + ui->ssBW->setVisible(checked); + ui->ssBWText->setVisible(checked); + ui->ssBWLabel->setVisible(checked); + ui->ssBWLine->setVisible(checked); + ui->pllCoarseLine->setVisible(checked); + ui->pllCoarseFreq->setVisible(checked); + ui->pllCoarseFreqLabel->setVisible(checked); + ui->pllFineLine->setVisible(checked); + ui->pllFineFreq->setVisible(checked); + ui->pllFineFreqLabel->setVisible(checked); + ui->pllFineFreqUnits->setVisible(checked); +} + +void InmarsatDemodGUI::on_pllBW_valueChanged(int value) +{ + ui->pllBWText->setText(QString("%1").arg(value / 1000.0, 0, 'f', 3)); + m_settings.m_pllBW = value / 1000.0; + applySetting("pllBandwidth"); +} + +void InmarsatDemodGUI::on_ssBW_valueChanged(int value) +{ + ui->ssBWText->setText(QString("%1").arg(value / 1000.0, 0, 'f', 3)); + m_settings.m_ssBW = value / 1000.0; + applySetting("ssBandwidth"); +} + +void InmarsatDemodGUI::on_filterType_editingFinished() +{ + m_settings.m_filterType = ui->filterType->text(); + updateRegularExpressions(); + filter(); + applySetting("filterType"); +} + +void InmarsatDemodGUI::on_filterMessage_editingFinished() +{ + m_settings.m_filterMessage = ui->filterMessage->text(); + updateRegularExpressions(); + filter(); + applySetting("filterMessage"); +} + +void InmarsatDemodGUI::on_clearTable_clicked() +{ + ui->packets->setRowCount(0); + ui->messages->setRowCount(0); + ui->decode->setText(""); + qDeleteAll(m_messages); + m_messages.clear(); + clearAllFromMapFeature(); +} + +void InmarsatDemodGUI::on_udpEnabled_clicked(bool checked) +{ + m_settings.m_udpEnabled = checked; + applySetting("udpEnabled"); +} + +void InmarsatDemodGUI::on_udpAddress_editingFinished() +{ + m_settings.m_udpAddress = ui->udpAddress->text(); + applySetting("udpAddress"); +} + +void InmarsatDemodGUI::on_udpPort_editingFinished() +{ + m_settings.m_udpPort = ui->udpPort->text().toInt(); + applySetting("udpPort"); +} + +void InmarsatDemodGUI::filterRow(QTableWidget *table, int row, int typeCol, int messageCol) +{ + bool hidden = false; + + if ((m_settings.m_filterType != "") && (typeCol != -1)) + { + QTableWidgetItem *typeItem = table->item(row, typeCol); + QRegularExpressionMatch match = m_typeRE.match(typeItem->text()); + if (!match.hasMatch()) { + hidden = true; + } + } + if (m_settings.m_filterMessage != "") + { + QTableWidgetItem *messageItem = table->item(row, messageCol); + QRegularExpressionMatch match = m_messageRE.match(messageItem->text()); + if (!match.hasMatch()) { + hidden = true; + } + } + table->setRowHidden(row, hidden); +} + +void InmarsatDemodGUI::filter() +{ + for (int i = 0; i < ui->packets->rowCount(); i++) { + filterRow(ui->packets, i, PACKET_COL_TYPE, PACKET_COL_MESSAGE); + } + for (int i = 0; i < ui->messages->rowCount(); i++) { + filterRow(ui->messages, i, -1, MESSAGE_COL_MESSAGE); + } +} + +void InmarsatDemodGUI::onWidgetRolled(QWidget* widget, bool rollDown) +{ + (void) widget; + (void) rollDown; + + getRollupContents()->saveState(m_rollupState); + applySetting("rollupState"); +} + +void InmarsatDemodGUI::onMenuDialogCalled(const QPoint &p) +{ + if (m_contextMenuType == ContextMenuType::ContextMenuChannelSettings) + { + BasicChannelSettingsDialog dialog(&m_channelMarker, this); + dialog.setUseReverseAPI(m_settings.m_useReverseAPI); + dialog.setReverseAPIAddress(m_settings.m_reverseAPIAddress); + dialog.setReverseAPIPort(m_settings.m_reverseAPIPort); + dialog.setReverseAPIDeviceIndex(m_settings.m_reverseAPIDeviceIndex); + dialog.setReverseAPIChannelIndex(m_settings.m_reverseAPIChannelIndex); + dialog.setDefaultTitle(m_displayedName); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + dialog.setNumberOfStreams(m_inmarsatDemod->getNumberOfDeviceStreams()); + dialog.setStreamIndex(m_settings.m_streamIndex); + } + + dialog.move(p); + new DialogPositioner(&dialog, false); + dialog.exec(); + + m_settings.m_rgbColor = m_channelMarker.getColor().rgb(); + m_settings.m_title = m_channelMarker.getTitle(); + m_settings.m_useReverseAPI = dialog.useReverseAPI(); + m_settings.m_reverseAPIAddress = dialog.getReverseAPIAddress(); + m_settings.m_reverseAPIPort = dialog.getReverseAPIPort(); + m_settings.m_reverseAPIDeviceIndex = dialog.getReverseAPIDeviceIndex(); + m_settings.m_reverseAPIChannelIndex = dialog.getReverseAPIChannelIndex(); + + setWindowTitle(m_settings.m_title); + setTitle(m_channelMarker.getTitle()); + setTitleColor(m_settings.m_rgbColor); + + QList settingsKeys({ + "rgbColor", + "title", + "useReverseAPI", + "reverseAPIAddress", + "reverseAPIPort", + "reverseAPIDeviceIndex", + "reverseAPIChannelIndex" + }); + + if (m_deviceUISet->m_deviceMIMOEngine) + { + m_settings.m_streamIndex = dialog.getSelectedStreamIndex(); + m_channelMarker.clearStreamIndexes(); + m_channelMarker.addStreamIndex(m_settings.m_streamIndex); + updateIndexLabel(); + } + + applySettings(settingsKeys); + } + + resetContextMenuType(); +} + +InmarsatDemodGUI::InmarsatDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent) : + ChannelGUI(parent), + ui(new Ui::InmarsatDemodGUI), + m_pluginAPI(pluginAPI), + m_deviceUISet(deviceUISet), + m_channelMarker(this), + m_deviceCenterFrequency(0), + m_doApplySettings(true), + m_tickCount(0), + m_loadingData(false) +{ + setAttribute(Qt::WA_DeleteOnClose, true); + m_helpURL = "plugins/channelrx/demodinmarsat/readme.md"; + RollupContents *rollupContents = getRollupContents(); + ui->setupUi(rollupContents); + setSizePolicy(rollupContents->sizePolicy()); + rollupContents->arrangeRollups(); + connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool))); + connect(this, SIGNAL(packetsCustomContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &))); + + m_inmarsatDemod = reinterpret_cast(rxChannel); + m_inmarsatDemod->setMessageQueueToGUI(getInputMessageQueue()); + + connect(&MainCore::instance()->getMasterTimer(), SIGNAL(timeout()), this, SLOT(tick())); // 50 ms + + m_scopeVis = m_inmarsatDemod->getScopeSink(); + m_scopeVis->setGLScope(ui->glScope); + ui->glScope->connectTimer(MainCore::instance()->getMasterTimer()); + ui->scopeGUI->setBuddies(m_scopeVis->getInputMessageQueue(), m_scopeVis, ui->glScope); + ui->scopeGUI->setStreams(QStringList({"IQ", "MagSq", "IQ AGC", "AGC gain/avg", "IQ CFO", "IQ RRC", "TED Er", "TED Er Sum", "CL", "IQ Derot", "Eq", "Eq Er", "Bit/mu"})); + + // Scope settings to display the IQ waveforms + ui->scopeGUI->setPreTrigger(1); + GLScopeSettings::TraceData traceDataI, traceDataQ; + traceDataI.m_projectionType = Projector::ProjectionReal; + traceDataI.m_amp = 1.0; // for -1 to +1 + traceDataI.m_ofs = 0.0; // vertical offset + traceDataQ.m_projectionType = Projector::ProjectionImag; + traceDataQ.m_amp = 1.0; + traceDataQ.m_ofs = 0.0; + ui->scopeGUI->changeTrace(0, traceDataI); + ui->scopeGUI->addTrace(traceDataQ); + ui->scopeGUI->setDisplayMode(GLScopeSettings::DisplayXYV); + ui->scopeGUI->focusOnTrace(0); // re-focus to take changes into account in the GUI + + GLScopeSettings::TriggerData triggerData; + triggerData.m_triggerLevel = 0.1; + triggerData.m_triggerLevelCoarse = 10; + triggerData.m_triggerPositiveEdge = true; + ui->scopeGUI->changeTrigger(0, triggerData); + ui->scopeGUI->focusOnTrigger(0); // re-focus to take changes into account in the GUI + + m_scopeVis->setLiveRate(48000); + //m_scopeVis->setFreeRun(false); // FIXME: add method rather than call m_scopeVis->configure() + + ui->deltaFrequencyLabel->setText(QString("%1f").arg(QChar(0x94, 0x03))); + ui->deltaFrequency->setColorMapper(ColorMapper(ColorMapper::GrayGold)); + ui->deltaFrequency->setValueRange(false, 7, -9999999, 9999999); + ui->channelPowerMeter->setColorTheme(LevelMeterSignalDB::ColorGreenAndBlue); + + m_channelMarker.blockSignals(true); + m_channelMarker.setColor(Qt::yellow); + m_channelMarker.setBandwidth(m_settings.m_rfBandwidth); + m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); + m_channelMarker.setTitle("Inmarsat C Demodulator"); + m_channelMarker.blockSignals(false); + m_channelMarker.setVisible(true); // activate signal on the last setting only + + setTitleColor(m_channelMarker.getColor()); + m_settings.setChannelMarker(&m_channelMarker); + m_settings.setScopeGUI(ui->scopeGUI); + m_settings.setRollupState(&m_rollupState); + + m_deviceUISet->addChannelMarker(&m_channelMarker); + + connect(&m_channelMarker, SIGNAL(changedByCursor()), this, SLOT(channelMarkerChangedByCursor())); + connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); + connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + + // Resize the table using dummy data + resizeTable(); + // Allow user to reorder columns + ui->packets->horizontalHeader()->setSectionsMovable(true); + // Allow user to sort table by clicking on headers + ui->packets->setSortingEnabled(true); + // Add context menu to allow hiding/showing of columns + m_packetsMenu = new QMenu(ui->packets); + for (int i = 0; i < ui->packets->horizontalHeader()->count(); i++) + { + QString text = ui->packets->horizontalHeaderItem(i)->text(); + m_packetsMenu->addAction(createCheckableItem(text, i, true, true)); + } + ui->packets->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->packets->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(packetsColumnSelectMenu(QPoint))); + // Get signals when columns change + connect(ui->packets->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(packets_sectionMoved(int, int, int))); + connect(ui->packets->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(packets_sectionResized(int, int, int))); + ui->packets->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->packets, SIGNAL(customContextMenuRequested(QPoint)), SLOT(packetsCustomContextMenuRequested(QPoint))); + TableTapAndHold *packetsTableTapAndHold = new TableTapAndHold(ui->packets); + connect(packetsTableTapAndHold, &TableTapAndHold::tapAndHold, this, &InmarsatDemodGUI::packetsCustomContextMenuRequested); + + // Allow user to reorder columns + ui->messages->horizontalHeader()->setSectionsMovable(true); + // Allow user to sort table by clicking on headers + ui->messages->setSortingEnabled(true); + // Add context menu to allow hiding/showing of columns + m_messagesMenu = new QMenu(ui->messages); + for (int i = 0; i < ui->messages->horizontalHeader()->count(); i++) + { + QString text = ui->messages->horizontalHeaderItem(i)->text(); + m_messagesMenu->addAction(createCheckableItem(text, i, true, false)); + } + ui->messages->horizontalHeader()->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->messages->horizontalHeader(), SIGNAL(customContextMenuRequested(QPoint)), SLOT(messagesColumnSelectMenu(QPoint))); + // Get signals when columns change + connect(ui->messages->horizontalHeader(), SIGNAL(sectionMoved(int, int, int)), SLOT(messages_sectionMoved(int, int, int))); + connect(ui->messages->horizontalHeader(), SIGNAL(sectionResized(int, int, int)), SLOT(messages_sectionResized(int, int, int))); + ui->messages->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->messages, SIGNAL(customContextMenuRequested(QPoint)), SLOT(messagesCustomContextMenuRequested(QPoint))); + TableTapAndHold *messagesTableTapAndHold = new TableTapAndHold(ui->messages); + connect(messagesTableTapAndHold, &TableTapAndHold::tapAndHold, this, &InmarsatDemodGUI::messagesCustomContextMenuRequested); + + on_pll_clicked(false); + ui->scopeContainer->setVisible(false); + + displaySettings(); + makeUIConnections(); + applyAllSettings(); + m_resizer.enableChildMouseTracking(); +} + +InmarsatDemodGUI::~InmarsatDemodGUI() +{ + clearAllFromMapFeature(); + delete ui; +} + +void InmarsatDemodGUI::blockApplySettings(bool block) +{ + m_doApplySettings = !block; +} + +void InmarsatDemodGUI::applySetting(const QString& settingsKey) +{ + applySettings({settingsKey}); +} + +void InmarsatDemodGUI::applySettings(const QStringList& settingsKeys, bool force) +{ + m_settingsKeys.append(settingsKeys); + if (m_doApplySettings) + { + InmarsatDemod::MsgConfigureInmarsatDemod* message = InmarsatDemod::MsgConfigureInmarsatDemod::create(m_settings, m_settingsKeys, force); + m_inmarsatDemod->getInputMessageQueue()->push(message); + m_settingsKeys.clear(); + } +} + +void InmarsatDemodGUI::applyAllSettings() +{ + applySettings(QStringList(), true); +} + +void InmarsatDemodGUI::displaySettings() +{ + m_channelMarker.blockSignals(true); + m_channelMarker.setBandwidth(m_settings.m_rfBandwidth); + m_channelMarker.setCenterFrequency(m_settings.m_inputFrequencyOffset); + m_channelMarker.setTitle(m_settings.m_title); + m_channelMarker.blockSignals(false); + m_channelMarker.setColor(m_settings.m_rgbColor); // activate signal on the last setting only + + setTitleColor(m_settings.m_rgbColor); + setWindowTitle(m_channelMarker.getTitle()); + setTitle(m_channelMarker.getTitle()); + + blockApplySettings(true); + + updateRegularExpressions(); + + ui->deltaFrequency->setValue(m_channelMarker.getCenterFrequency()); + + ui->rfBWText->setText(QString("%1k").arg(m_settings.m_rfBandwidth / 1000.0, 0, 'f', 1)); + ui->rfBW->setValue(m_settings.m_rfBandwidth / 100.0); + + ui->equalizer->setCurrentIndex((int) m_settings.m_equalizer); + + ui->rrcRolloffText->setText(QString("%1").arg(m_settings.m_rrcRolloff, 0, 'f', 2)); + ui->rrcRolloff->setValue(m_settings.m_rrcRolloff * 100.0); + + ui->pllBWText->setText(QString("%1").arg(m_settings.m_pllBW, 0, 'f', 3)); + ui->pllBW->setValue(m_settings.m_pllBW * 1000.0); + + ui->ssBWText->setText(QString("%1").arg(m_settings.m_ssBW, 0, 'f', 3)); + ui->ssBW->setValue(m_settings.m_ssBW * 1000.0); + + updateIndexLabel(); + + ui->filterType->setText(m_settings.m_filterType); + ui->filterMessage->setText(m_settings.m_filterMessage); + + ui->udpEnabled->setChecked(m_settings.m_udpEnabled); + ui->udpAddress->setText(m_settings.m_udpAddress); + ui->udpPort->setText(QString::number(m_settings.m_udpPort)); + + ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename)); + ui->logEnable->setChecked(m_settings.m_logEnabled); + + ui->useFileTime->setChecked(m_settings.m_useFileTime); + + // Order and size columns + QHeaderView *header = ui->packets->horizontalHeader(); + for (int i = 0; i < INMARSATDEMOD_PACKETS_COLUMNS; i++) + { + bool hidden = m_settings.m_packetsColumnSizes[i] == 0; + header->setSectionHidden(i, hidden); + m_packetsMenu->actions().at(i)->setChecked(!hidden); + if (m_settings.m_packetsColumnSizes[i] > 0) + ui->packets->setColumnWidth(i, m_settings.m_packetsColumnSizes[i]); + header->moveSection(header->visualIndex(i), m_settings.m_packetsColumnIndexes[i]); + } + header = ui->messages->horizontalHeader(); + for (int i = 0; i < INMARSATDEMOD_MESSAGES_COLUMNS; i++) + { + bool hidden = m_settings.m_messagesColumnSizes[i] == 0; + header->setSectionHidden(i, hidden); + m_messagesMenu->actions().at(i)->setChecked(!hidden); + if (m_settings.m_messagesColumnSizes[i] > 0) + ui->messages->setColumnWidth(i, m_settings.m_messagesColumnSizes[i]); + header->moveSection(header->visualIndex(i), m_settings.m_messagesColumnIndexes[i]); + } + + filter(); + + getRollupContents()->restoreState(m_rollupState); + updateAbsoluteCenterFrequency(); + blockApplySettings(false); +} + +void InmarsatDemodGUI::leaveEvent(QEvent* event) +{ + m_channelMarker.setHighlighted(false); + ChannelGUI::leaveEvent(event); +} + +void InmarsatDemodGUI::enterEvent(EnterEventType* event) +{ + m_channelMarker.setHighlighted(true); + ChannelGUI::enterEvent(event); +} + +void InmarsatDemodGUI::tick() +{ + double magsqAvg, magsqPeak; + int nbMagsqSamples; + m_inmarsatDemod->getMagSqLevels(magsqAvg, magsqPeak, nbMagsqSamples); + double powDbAvg = CalcDb::dbPower(magsqAvg); + double powDbPeak = CalcDb::dbPower(magsqPeak); + + ui->channelPowerMeter->levelChanged( + (100.0f + powDbAvg) / 100.0f, + (100.0f + powDbPeak) / 100.0f, + nbMagsqSamples); + + if (m_tickCount % 4 == 0) { + ui->channelPower->setText(QString::number(powDbAvg, 'f', 1)); + } + + bool locked; + Real coarseFreqCurrent; + Real coarseFreqCurrentPower; + Real coarseFreq; + Real coarseFreqPower; + Real fineFreq; + Real evm; + bool synced; + m_inmarsatDemod->getPLLStatus(locked, coarseFreqCurrent, coarseFreqCurrentPower, coarseFreq, coarseFreqPower, fineFreq, evm, synced); + + if (synced) + { + ui->pll->setStyleSheet("QToolButton { background-color : green; }"); + ui->pll->setIcon(QIcon(":/locked.png").pixmap(QSize(20, 20))); + } + else if (locked) + { + ui->pll->setStyleSheet("QToolButton { background-color : yellow; }"); + ui->pll->setIcon(QIcon(":/locked.png").pixmap(QSize(20, 20))); + } + else + { + ui->pll->setStyleSheet("QToolButton { background:rgb(79,79,79); }"); + ui->pll->setIcon(QIcon(":/unlocked.png").pixmap(QSize(20, 20))); + } + + ui->pllFineFreq->setText(QString::number(std::round(fineFreq))); + ui->pllCoarseFreq->setText( + QString("Current: %1 Hz %2 dB Locked: %3 Hz %4 dB") + .arg(std::round(coarseFreqCurrent)) + .arg(std::round(CalcDb::dbPower(coarseFreqCurrentPower))) + .arg(std::round(coarseFreq)) + .arg(std::round(CalcDb::dbPower(coarseFreqPower))) + ); + + ui->evm->setText(QString("%1").arg(evm * 100.0f, 0, 'f', 1)); + + m_tickCount++; +} + +void InmarsatDemodGUI::on_logEnable_clicked(bool checked) +{ + m_settings.m_logEnabled = checked; + applySetting("logEnabled"); +} + +void InmarsatDemodGUI::on_logFilename_clicked() +{ + // Get filename to save to + QFileDialog fileDialog(nullptr, "Select file to log received frames to", "", "*.csv"); + fileDialog.setDefaultSuffix("csv"); + fileDialog.setAcceptMode(QFileDialog::AcceptSave); + if (fileDialog.exec()) + { + QStringList fileNames = fileDialog.selectedFiles(); + if (fileNames.size() > 0) + { + m_settings.m_logFilename = fileNames[0]; + ui->logFilename->setToolTip(QString(".csv log filename: %1").arg(m_settings.m_logFilename)); + applySetting("logFilename"); + } + } +} + +// Read .csv log and process as received frames +void InmarsatDemodGUI::on_logOpen_clicked() +{ + QFileDialog fileDialog(nullptr, "Select .csv log file to read", "", "*.csv"); + if (fileDialog.exec()) + { + QStringList fileNames = fileDialog.selectedFiles(); + if (fileNames.size() > 0) + { + QFile file(fileNames[0]); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QTextStream in(&file); + QString error; + QHash colIndexes = CSV::readHeader(in, {"Date", "Time", "Data"}, error); + if (error.isEmpty()) + { + m_loadingData = true; + + int dateCol = colIndexes.value("Date"); + int timeCol = colIndexes.value("Time"); + int dataCol = colIndexes.value("Data"); + int maxCol = std::max({dateCol, timeCol, dataCol}); + + QMessageBox dialog(this); + dialog.setText("Reading packet data"); + dialog.addButton(QMessageBox::Cancel); + dialog.show(); + QApplication::processEvents(); + int count = 0; + bool cancelled = false; + QStringList cols; + while (!cancelled && CSV::readRow(in, &cols)) + { + if (cols.size() > maxCol) + { + QDate date = QDate::fromString(cols[dateCol]); + QTime time = QTime::fromString(cols[timeCol]); + QDateTime dateTime(date, time); + QByteArray bytes = QByteArray::fromHex(cols[dataCol].toLatin1()); + packetReceived(bytes, dateTime); + if (count % 1000 == 0) + { + QApplication::processEvents(); + if (dialog.clickedButton()) { + cancelled = true; + } + } + count++; + } + } + dialog.close(); + + m_loadingData = false; + ui->packets->setSortingEnabled(true); + ui->messages->setSortingEnabled(true); + } + else + { + QMessageBox::critical(this, "Inmarsat Demod", error); + } + } + else + { + QMessageBox::critical(this, "Inmarsat Demod", QString("Failed to open file %1").arg(fileNames[0])); + } + } + } +} + +void InmarsatDemodGUI::on_useFileTime_toggled(bool checked) +{ + m_settings.m_useFileTime = checked; + applySetting("useFileTime"); +} + +void InmarsatDemodGUI::makeUIConnections() +{ + QObject::connect(ui->deltaFrequency, &ValueDialZ::changed, this, &InmarsatDemodGUI::on_deltaFrequency_changed); + QObject::connect(ui->rfBW, &QSlider::valueChanged, this, &InmarsatDemodGUI::on_rfBW_valueChanged); + QObject::connect(ui->equalizer, QOverload::of(&QComboBox::currentIndexChanged), this, &InmarsatDemodGUI::on_equalizer_currentIndexChanged); + QObject::connect(ui->rrcRolloff, &QDial::valueChanged, this, &InmarsatDemodGUI::on_rrcRolloff_valueChanged); + QObject::connect(ui->pllBW, &QDial::valueChanged, this, &InmarsatDemodGUI::on_pllBW_valueChanged); + QObject::connect(ui->ssBW, &QDial::valueChanged, this, &InmarsatDemodGUI::on_ssBW_valueChanged); + QObject::connect(ui->pll, &QToolButton::clicked, this, &InmarsatDemodGUI::on_pll_clicked); + QObject::connect(ui->filterType, &QLineEdit::editingFinished, this, &InmarsatDemodGUI::on_filterType_editingFinished); + QObject::connect(ui->filterMessage, &QLineEdit::editingFinished, this, &InmarsatDemodGUI::on_filterMessage_editingFinished); + QObject::connect(ui->clearTable, &QPushButton::clicked, this, &InmarsatDemodGUI::on_clearTable_clicked); + QObject::connect(ui->udpEnabled, &QCheckBox::clicked, this, &InmarsatDemodGUI::on_udpEnabled_clicked); + QObject::connect(ui->udpAddress, &QLineEdit::editingFinished, this, &InmarsatDemodGUI::on_udpAddress_editingFinished); + QObject::connect(ui->udpPort, &QLineEdit::editingFinished, this, &InmarsatDemodGUI::on_udpPort_editingFinished); + QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &InmarsatDemodGUI::on_logEnable_clicked); + QObject::connect(ui->logFilename, &QToolButton::clicked, this, &InmarsatDemodGUI::on_logFilename_clicked); + QObject::connect(ui->logOpen, &QToolButton::clicked, this, &InmarsatDemodGUI::on_logOpen_clicked); + QObject::connect(ui->useFileTime, &ButtonSwitch::toggled, this, &InmarsatDemodGUI::on_useFileTime_toggled); + QObject::connect(ui->packets, &QTableWidget::itemSelectionChanged, this, &InmarsatDemodGUI::on_packets_itemSelectionChanged); + QObject::connect(ui->messages, &QTableWidget::itemSelectionChanged, this, &InmarsatDemodGUI::on_messages_itemSelectionChanged); +} + +void InmarsatDemodGUI::updateAbsoluteCenterFrequency() +{ + setStatusFrequency(m_deviceCenterFrequency + m_settings.m_inputFrequencyOffset); +} + +void InmarsatDemodGUI::updateRegularExpressions() +{ + m_typeRE = QRegularExpression(QRegularExpression::anchoredPattern(m_settings.m_filterType)); + m_messageRE = QRegularExpression(QRegularExpression::anchoredPattern(m_settings.m_filterMessage), QRegularExpression::DotMatchesEverythingOption); +} diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodgui.h b/plugins/channelrx/demodinmarsat/inmarsatdemodgui.h new file mode 100644 index 000000000..af84a326d --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodgui.h @@ -0,0 +1,260 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2020-2026 Jon Beniston, M7RCE // +// Copyright (C) 2020, 2022 Edouard Griffiths, F4EXB // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_INMARSATDEMODGUI_H +#define INCLUDE_INMARSATDEMODGUI_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "channel/channelgui.h" +#include "dsp/channelmarker.h" +#include "util/messagequeue.h" +#include "settings/rollupstate.h" +#include "inmarsatdemodsettings.h" + +class PluginAPI; +class DeviceUISet; +class BasebandSampleSink; +class ScopeVis; +class InmarsatDemod; +class InmarsatDemodGUI; + +namespace Ui { + class InmarsatDemodGUI; +} +class InmarsatDemodGUI; + +struct MessagePart { + int m_part; + int m_packet; + bool m_continuation; + QString m_text; +}; + +class MultipartMessage { +public: + + MultipartMessage(int id, std::map params, const QDateTime& dateTime); + void update(std::map params, const QDateTime& dateTime); + void addPart(const MessagePart& part); + QString getMessage() const; + int getParts() const { return m_parts.size(); } + int getTotalParts() const { return m_parts[m_parts.size()-1].m_packet * 2; } + QDateTime getDateTime() const { return m_dateTime; } + bool getComplete() const { return getParts() == getTotalParts() && !m_parts[m_parts.size()-1].m_continuation; } + int getId() const { return m_id; } + QString getService() const { return m_service; } + QString getPriority() const { return m_priority; } + QString getAddress() const { return m_address; } + QIcon *getAddressIcon() const { return m_icon; } + float getLatitude() const { return m_addressCoordinates.size() > 0 ? m_latitude : m_messageCoordinates[0].latitude(); } + float getLongitude() const { return m_addressCoordinates.size() > 0 ? m_longitude : m_messageCoordinates[0].longitude(); } + QList& getCoordinates() { return m_addressCoordinates.size() > 0 ? m_addressCoordinates : m_messageCoordinates; } + + static QString decodeAddress(QString messageType, QString addressHex, float *latitude = nullptr, float *longitude = nullptr, QList *coordinates = nullptr, QIcon **icon = nullptr); + +private: + + QDateTime m_dateTime; + int m_id; + QString m_service; + QString m_priority; + QString m_address; + QIcon *m_icon; + QList m_parts; + float m_latitude; + float m_longitude; + QList m_addressCoordinates; + QList m_messageCoordinates; + + static QRegularExpression m_re; + static const QStringList m_navAreas; + static const QStringList m_navAreaFlags; + + void parseMessage(); +}; + +class InmarsatDemodGUI : public ChannelGUI { + Q_OBJECT + +public: + static InmarsatDemodGUI* create(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel); + virtual void destroy(); + + void resetToDefaults(); + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; } + virtual void setWorkspaceIndex(int index) { m_settings.m_workspaceIndex = index; }; + virtual int getWorkspaceIndex() const { return m_settings.m_workspaceIndex; }; + virtual void setGeometryBytes(const QByteArray& blob) { m_settings.m_geometryBytes = blob; }; + virtual QByteArray getGeometryBytes() const { return m_settings.m_geometryBytes; }; + virtual QString getTitle() const { return m_settings.m_title; }; + virtual QColor getTitleColor() const { return m_settings.m_rgbColor; }; + virtual void zetHidden(bool hidden) { m_settings.m_hidden = hidden; } + virtual bool getHidden() const { return m_settings.m_hidden; } + virtual ChannelMarker& getChannelMarker() { return m_channelMarker; } + virtual int getStreamIndex() const { return m_settings.m_streamIndex; } + virtual void setStreamIndex(int streamIndex) { m_settings.m_streamIndex = streamIndex; } + +public slots: + void channelMarkerChangedByCursor(); + void channelMarkerHighlightedByCursor(); + +private: + Ui::InmarsatDemodGUI* ui; + PluginAPI* m_pluginAPI; + DeviceUISet* m_deviceUISet; + ChannelMarker m_channelMarker; + RollupState m_rollupState; + InmarsatDemodSettings m_settings; + QList m_settingsKeys; + qint64 m_deviceCenterFrequency; + bool m_doApplySettings; + ScopeVis* m_scopeVis; + + InmarsatDemod* m_inmarsatDemod; + int m_basebandSampleRate; + uint32_t m_tickCount; + MessageQueue m_inputMessageQueue; + + QMenu *m_packetsMenu; // Column select context menu + QMenu *m_messagesMenu; + QStringList m_mapItems; + bool m_loadingData; + + inmarsatc::frameParser::FrameParser m_parser; + QHash m_messages; + + QRegularExpression m_typeRE; + QRegularExpression m_messageRE; + + explicit InmarsatDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel, QWidget* parent = 0); + virtual ~InmarsatDemodGUI(); + + void blockApplySettings(bool block); + void applySetting(const QString& settingsKey); + void applySettings(const QStringList& settingsKeys, bool force = false); + void applyAllSettings(); + void displaySettings(); + void packetReceived(const QByteArray& packet, QDateTime dateTime); + bool handleMessage(const Message& message); + void makeUIConnections(); + void updateAbsoluteCenterFrequency(); + + void leaveEvent(QEvent*); + void enterEvent(EnterEventType*); + + void updateMessageTable(MultipartMessage *message); + void resizeTable(); + QAction *createCheckableItem(QString& text, int idx, bool checked, bool packets); + + void sendAreaToMapFeature(const QString& name, const QString& text, float latitude, float longitude, const QList& coordinates); + void clearAreaFromMapFeature(const QString& name); + void clearAllFromMapFeature(); + + void updateRegularExpressions(); + void filterRow(QTableWidget *table, int row, int typeCol, int messageCol); + void filter(); + + void decodeAppend(QString& decode, const QString& title, const std::string& variable, inmarsatc::frameParser::FrameParser::frameParser_result& frame); + void decodeAppendFreqMHz(QString& decode, const QString& title, const std::string& variable, inmarsatc::frameParser::FrameParser::frameParser_result& frame); + void decodeAppendHTML(QString& decode, const QString& title, const std::string& variable, inmarsatc::frameParser::FrameParser::frameParser_result& frame); + QString toHTML(const std::string& string) const; + QString toHTML(const QString& string) const; + + enum MessageCol { + MESSAGE_COL_DATE, + MESSAGE_COL_TIME, + MESSAGE_COL_ID, + MESSAGE_COL_SERVICE, + MESSAGE_COL_PRIORITY, + MESSAGE_COL_ADDRESS, + MESSAGE_COL_PARTS, + MESSAGE_COL_MESSAGE + }; + + enum PacketCol { + PACKET_COL_DATE, + PACKET_COL_TIME, + PACKET_COL_SAT, + PACKET_COL_MES, + PACKET_COL_LES, + PACKET_COL_TYPE, + PACKET_COL_FRAME_NO, + PACKET_COL_LCN, + PACKET_COL_ULF, + PACKET_COL_DLF, + PACKET_COL_MSG_ID, + PACKET_COL_PRIORITY, + PACKET_COL_ADDRESS, + PACKET_COL_MESSAGE, + PACKET_COL_DECODE, + PACKET_COL_DATA_HEX + }; + +private slots: + void on_packets_itemSelectionChanged(); + void on_messages_itemSelectionChanged(); + void on_deltaFrequency_changed(qint64 value); + void on_rfBW_valueChanged(int index); + void on_equalizer_currentIndexChanged(int index); + void on_rrcRolloff_valueChanged(int value); + void on_pll_clicked(bool checked=false); + void on_pllBW_valueChanged(int value); + void on_ssBW_valueChanged(int value); + void on_filterType_editingFinished(); + void on_filterMessage_editingFinished(); + void on_clearTable_clicked(); + void on_udpEnabled_clicked(bool checked); + void on_udpAddress_editingFinished(); + void on_udpPort_editingFinished(); + void on_logEnable_clicked(bool checked=false); + void on_logFilename_clicked(); + void on_logOpen_clicked(); + void on_useFileTime_toggled(bool checked=false); + void packets_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); + void packets_sectionResized(int logicalIndex, int oldSize, int newSize); + void packetsColumnSelectMenu(QPoint pos); + void packetsColumnSelectMenuChecked(bool checked = false); + void packetsCustomContextMenuRequested(QPoint pos); + void messages_sectionMoved(int logicalIndex, int oldVisualIndex, int newVisualIndex); + void messages_sectionResized(int logicalIndex, int oldSize, int newSize); + void messagesColumnSelectMenu(QPoint pos); + void messagesColumnSelectMenuChecked(bool checked = false); + void messagesCustomContextMenuRequested(QPoint pos); + void onWidgetRolled(QWidget* widget, bool rollDown); + void onMenuDialogCalled(const QPoint& p); + void handleInputMessages(); + void tick(); +}; + +#endif // INCLUDE_INMARSATDEMODGUI_H diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodgui.ui b/plugins/channelrx/demodinmarsat/inmarsatdemodgui.ui new file mode 100644 index 000000000..a6df51f93 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodgui.ui @@ -0,0 +1,1265 @@ + + + InmarsatDemodGUI + + + + 0 + 0 + 1116 + 856 + + + + + 0 + 0 + + + + + 352 + 0 + + + + + Liberation Sans + 9 + + + + Qt::FocusPolicy::StrongFocus + + + Inmarsat Std-C Demodulator + + + + + 0 + 0 + 1111 + 151 + + + + + 350 + 0 + + + + Settings + + + + 3 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 2 + + + + + + 16 + 0 + + + + Df + + + + + + + + 0 + 0 + + + + + 32 + 16 + + + + + Liberation Mono + 12 + + + + PointingHandCursor + + + Qt::FocusPolicy::StrongFocus + + + Demod shift frequency from center in Hz + + + + + + + Hz + + + + + + + Qt::Orientation::Vertical + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + Channel power + + + Qt::LayoutDirection::RightToLeft + + + 0.0 + + + + + + + dB + + + + + + + + + + + + + dB + + + + + + + + 0 + 0 + + + + + 0 + 24 + + + + + Liberation Mono + 8 + + + + Level meter (dB) top trace: average, bottom trace: instantaneous peak, tip: peak hold + + + + + + + + + Qt::Orientation::Horizontal + + + + + + + + + BW + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + RF bandwidth + + + 20 + + + 200 + + + 1 + + + 40 + + + Qt::Orientation::Horizontal + + + + + + + + 30 + 0 + + + + 10.0k + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + Qt::Orientation::Vertical + + + + + + + Eq + + + + + + + Equalizer + + + + None + + + + + CMA + + + + + LMS + + + + + + + + Qt::Orientation::Vertical + + + + + + + EVM + + + + + + + + 36 + 0 + + + + Error vector magnitude averaged over last second + + + 100.0 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + % + + + + + + + Qt::Orientation::Vertical + + + + + + + RRC + + + + + + + + 24 + 24 + + + + Root Raised Cosine Filter rolloff + + + 100 + + + + + + + + 30 + 0 + + + + 0.7 + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Orientation::Vertical + + + + + + + CFO + + + + + + + + 310 + 0 + + + + Coarse frequency offset + + + Current: -100Hz -20dB Locked: -100 Hz -20 dB + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + Qt::Orientation::Vertical + + + + + + + CL + + + + + + + + 40 + 0 + + + + Costas Loop frequency in Hertz + + + - + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + Hz + + + + + + + Qt::Orientation::Vertical + + + + + + + SS + + + + + + + + 24 + 24 + + + + Symbol synchronizer loop bandwidth + + + 1000 + + + + + + + + 40 + 0 + + + + 0.062 + + + + + + + Qt::Orientation::Vertical + + + + + + + CL + + + + + + + + 24 + 24 + + + + Costas Loop loop bandwidth + + + 1000 + + + + + + + + 40 + 0 + + + + 0.062 + + + + + + + Green background indicates PLL locked. Check for more details + + + + + + + :/unlocked.png:/unlocked.png + + + true + + + false + + + + + + + + + Qt::Orientation::Horizontal + + + + + + + + + UDP + + + + + + + Send packets via UDP + + + Qt::LayoutDirection::RightToLeft + + + + + + + + + + + 120 + 0 + + + + Qt::FocusPolicy::ClickFocus + + + Destination UDP address + + + 000.000.000.000 + + + 127.0.0.1 + + + + + + + : + + + Qt::AlignmentFlag::AlignCenter + + + + + + + + 50 + 0 + + + + + 50 + 16777215 + + + + Qt::FocusPolicy::ClickFocus + + + Destination UDP port + + + 00000 + + + 9998 + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Orientation::Horizontal + + + + + + + + + Type + + + + + + + Display only packets where the message type field matches the specified regular expression + + + + + + + Message + + + + + + + Display only packets where the message field matches the specified regular expression + + + + + + + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + + + + + + + 24 + 16777215 + + + + Check to use date and time from input file. Uncheck to use date and time from clock. + + + + + + + :/clockcurrent.png + :/clockfile.png:/clockcurrent.png + + + + + + + + 24 + 16777215 + + + + Start/stop logging of received packets to .csv file + + + + + + + :/record_off.png:/record_off.png + + + + + + + Set log .csv filename + + + ... + + + + :/save.png:/save.png + + + false + + + + + + + Read data from .csv log file + + + ... + + + + :/load.png:/load.png + + + false + + + + + + + Clear packets from table + + + + + + + :/bin.png:/bin.png + + + + + + + + + + + 0 + 170 + 391 + 241 + + + + + 0 + 0 + + + + Received Packets + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Orientation::Vertical + + + + 0 + + + + Packets + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Received packets + + + QAbstractItemView::EditTrigger::NoEditTriggers + + + QAbstractItemView::SelectionMode::SingleSelection + + + QAbstractItemView::SelectionBehavior::SelectRows + + + + Date + + + Date packet was received + + + + + Time + + + Time packet was received + + + + + Sat + + + Satellite region + + + + + MES + + + Mobile Earth Station + + + + + LES + + + Land Earth Station + + + + + Type + + + Packet type + + + + + Frame + + + Frame number + + + + + LCN + + + Logical channel number + + + + + ULF (MHz) + + + Uplink frequency + + + + + DLF (MHz) + + + Downlink frequency + + + + + MsgId + + + Message ID + + + + + Priority + + + Priority + + + + + Address + + + Address + + + + + Message + + + Message + + + + + Decode + + + + + Data (Hex) + + + Frame data as hex + + + + + + + + + Messages + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QAbstractItemView::EditTrigger::NoEditTriggers + + + QAbstractItemView::SelectionMode::SingleSelection + + + QAbstractItemView::SelectionBehavior::SelectRows + + + true + + + + Date + + + Date last part of message was received + + + + + Time + + + Time last part of message was received + + + + + ID + + + Message ID + + + + + Service + + + Service Code + + + + + Priority + + + Priority + + + + + Address + + + Address + + + + + Parts + + + How many parts have been received / Total expected parts + + + + + Message + + + Message text + + + + + + + + + + true + + + + + + + + + + 30 + 430 + 714 + 331 + + + + + 714 + 0 + + + + Waveforms + + + + 2 + + + 3 + + + 3 + + + 3 + + + 3 + + + + + + 200 + 250 + + + + + Liberation Mono + 8 + + + + + + + + + + + + + RollupContents + QWidget +
gui/rollupcontents.h
+ 1 +
+ + ValueDialZ + QWidget +
gui/valuedialz.h
+ 1 +
+ + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
+ + LevelMeterSignalDB + QWidget +
gui/levelmeter.h
+ 1 +
+ + GLScope + QWidget +
gui/glscope.h
+ 1 +
+ + GLScopeGUI + QWidget +
gui/glscopegui.h
+ 1 +
+
+ + deltaFrequency + rfBW + rrcRolloff + pll + udpEnabled + filterType + filterMessage + useFileTime + logEnable + logFilename + logOpen + clearTable + packets + atab + decode + messages + + + + + +
diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodplugin.cpp b/plugins/channelrx/demodinmarsat/inmarsatdemodplugin.cpp new file mode 100644 index 000000000..cd85c1717 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodplugin.cpp @@ -0,0 +1,94 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// Copyright (C) 2015-2022 Edouard Griffiths, F4EXB // +// Copyright (C) 2020-2026 Jon Beniston, M7RCE // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include "plugin/pluginapi.h" + +#ifndef SERVER_MODE +#include "inmarsatdemodgui.h" +#endif +#include "inmarsatdemod.h" +#include "inmarsatdemodwebapiadapter.h" +#include "inmarsatdemodplugin.h" + +const PluginDescriptor InmarsatDemodPlugin::m_pluginDescriptor = { + InmarsatDemod::m_channelId, + QStringLiteral("Inmarsat C Demodulator"), + QStringLiteral("7.22.11"), + QStringLiteral("(c) Jon Beniston, M7RCE"), + QStringLiteral("https://github.com/f4exb/sdrangel"), + true, + QStringLiteral("https://github.com/f4exb/sdrangel") +}; + +InmarsatDemodPlugin::InmarsatDemodPlugin(QObject* parent) : + QObject(parent), + m_pluginAPI(0) +{ +} + +const PluginDescriptor& InmarsatDemodPlugin::getPluginDescriptor() const +{ + return m_pluginDescriptor; +} + +void InmarsatDemodPlugin::initPlugin(PluginAPI* pluginAPI) +{ + m_pluginAPI = pluginAPI; + + m_pluginAPI->registerRxChannel(InmarsatDemod::m_channelIdURI, InmarsatDemod::m_channelId, this); +} + +void InmarsatDemodPlugin::createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const +{ + if (bs || cs) + { + InmarsatDemod *instance = new InmarsatDemod(deviceAPI); + + if (bs) { + *bs = instance; + } + + if (cs) { + *cs = instance; + } + } +} + +#ifdef SERVER_MODE +ChannelGUI* InmarsatDemodPlugin::createRxChannelGUI( + DeviceUISet *deviceUISet, + BasebandSampleSink *rxChannel) const +{ + (void) deviceUISet; + (void) rxChannel; + return 0; +} +#else +ChannelGUI* InmarsatDemodPlugin::createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const +{ + return InmarsatDemodGUI::create(m_pluginAPI, deviceUISet, rxChannel); +} +#endif + +ChannelWebAPIAdapter* InmarsatDemodPlugin::createChannelWebAPIAdapter() const +{ + return new InmarsatDemodWebAPIAdapter(); +} diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodplugin.h b/plugins/channelrx/demodinmarsat/inmarsatdemodplugin.h new file mode 100644 index 000000000..6c52653f7 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodplugin.h @@ -0,0 +1,51 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB // +// Copyright (C) 2020-2026 Jon Beniston, M7RCE // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_INMARSATDEMODPLUGIN_H +#define INCLUDE_INMARSATDEMODPLUGIN_H + +#include +#include "plugin/plugininterface.h" + +class DeviceUISet; +class BasebandSampleSink; + +class InmarsatDemodPlugin : public QObject, PluginInterface { + Q_OBJECT + Q_INTERFACES(PluginInterface) + Q_PLUGIN_METADATA(IID "sdrangel.channel.inmarsatdemod") + +public: + explicit InmarsatDemodPlugin(QObject* parent = NULL); + + const PluginDescriptor& getPluginDescriptor() const; + void initPlugin(PluginAPI* pluginAPI); + + virtual void createRxChannel(DeviceAPI *deviceAPI, BasebandSampleSink **bs, ChannelAPI **cs) const; + virtual ChannelGUI* createRxChannelGUI(DeviceUISet *deviceUISet, BasebandSampleSink *rxChannel) const; + virtual ChannelWebAPIAdapter* createChannelWebAPIAdapter() const; + +private: + static const PluginDescriptor m_pluginDescriptor; + + PluginAPI* m_pluginAPI; +}; + +#endif // INCLUDE_INMARSATDEMODPLUGIN_H diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodsettings.cpp b/plugins/channelrx/demodinmarsat/inmarsatdemodsettings.cpp new file mode 100644 index 000000000..f1488d477 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodsettings.cpp @@ -0,0 +1,410 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// Copyright (C) 2015-2022 Edouard Griffiths, F4EXB // +// Copyright (C) 2020-2026 Jon Beniston, M7RCE // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "util/simpleserializer.h" +#include "settings/serializable.h" +#include "inmarsatdemodsettings.h" + +InmarsatDemodSettings::InmarsatDemodSettings() : + m_channelMarker(nullptr), + m_scopeGUI(nullptr), + m_rollupState(nullptr) +{ + appendDefaultColumnSettings(); + resetToDefaults(); +} + +void InmarsatDemodSettings::appendDefaultColumnSettings() +{ + appendDefaultColumnIndexes(m_packetsColumnIndexes, INMARSATDEMOD_PACKETS_COLUMNS); + appendDefaultColumnSizes(m_packetsColumnSizes, INMARSATDEMOD_PACKETS_COLUMNS); + appendDefaultColumnIndexes(m_messagesColumnIndexes, INMARSATDEMOD_MESSAGES_COLUMNS); + appendDefaultColumnSizes(m_messagesColumnSizes, INMARSATDEMOD_MESSAGES_COLUMNS); +} + +void InmarsatDemodSettings::appendDefaultColumnIndexes(QList& list, int size) +{ + while (list.size() < size) { + list.append(list.size()); + } +} + +void InmarsatDemodSettings::appendDefaultColumnSizes(QList& list, int size) +{ + while (list.size() < size) { + list.append(-1); + } +} + +void InmarsatDemodSettings::resetToDefaults() +{ + m_inputFrequencyOffset = 0; + m_rfBandwidth = 4000.0f; + m_equalizer = NONE; + m_rrcRolloff = 1.0f; + m_pllBW = 2*M_PI/100.0; + m_ssBW = 2*M_PI/100.0; + m_filterType = ""; + m_filterMessage = ""; + m_udpEnabled = false; + m_udpAddress = "127.0.0.1"; + m_udpPort = 9999; + m_logFilename = "inmarsat_c_log.csv"; + m_logEnabled = false; + m_useFileTime = false; + for (int i = 0; i < INMARSATDEMOD_PACKETS_COLUMNS; i++) + { + m_packetsColumnIndexes[i] = i; + m_packetsColumnSizes[i] = -1; // Autosize + } + for (int i = 0; i < INMARSATDEMOD_MESSAGES_COLUMNS; i++) + { + m_messagesColumnIndexes[i] = i; + m_messagesColumnSizes[i] = -1; // Autosize + } + m_rgbColor = QColor(40, 180, 75).rgb(); + m_title = "Inmarsat C Demodulator"; + m_streamIndex = 0; + m_useReverseAPI = false; + m_reverseAPIAddress = "127.0.0.1"; + m_reverseAPIPort = 8888; + m_reverseAPIDeviceIndex = 0; + m_reverseAPIChannelIndex = 0; + m_workspaceIndex = 0; + m_hidden = false; +} + +QByteArray InmarsatDemodSettings::serialize() const +{ + SimpleSerializer s(1); + s.writeS32(1, m_inputFrequencyOffset); + s.writeS32(2, m_streamIndex); + s.writeString(3, m_filterType); + s.writeString(4, m_filterMessage); + + s.writeFloat(5, m_rfBandwidth); + s.writeFloat(6, m_rrcRolloff); + s.writeFloat(7, m_pllBW); + s.writeFloat(8, m_ssBW); + + s.writeS32(9, (int) m_equalizer); + + s.writeBool(10, m_udpEnabled); + s.writeString(11, m_udpAddress); + s.writeU32(12, m_udpPort); + + s.writeString(13, m_logFilename); + s.writeBool(14, m_logEnabled); + + s.writeBool(15, m_useFileTime); + + s.writeList(16, m_packetsColumnIndexes); + s.writeList(17, m_packetsColumnSizes); + s.writeList(18, m_messagesColumnIndexes); + s.writeList(19, m_messagesColumnSizes); + + if (m_channelMarker) { + s.writeBlob(20, m_channelMarker->serialize()); + } + s.writeU32(21, m_rgbColor); + s.writeString(22, m_title); + s.writeBool(23, m_useReverseAPI); + s.writeString(24, m_reverseAPIAddress); + s.writeU32(25, m_reverseAPIPort); + s.writeU32(26, m_reverseAPIDeviceIndex); + s.writeU32(27, m_reverseAPIChannelIndex); + if (m_rollupState) { + s.writeBlob(28, m_rollupState->serialize()); + } + s.writeS32(29, m_workspaceIndex); + s.writeBlob(30, m_geometryBytes); + s.writeBool(31, m_hidden); + if (m_scopeGUI) { + s.writeBlob(32, m_scopeGUI->serialize()); + } + + return s.final(); +} + +bool InmarsatDemodSettings::deserialize(const QByteArray& data) +{ + SimpleDeserializer d(data); + + if(!d.isValid()) + { + resetToDefaults(); + return false; + } + + if(d.getVersion() == 1) + { + QByteArray bytetmp; + uint32_t utmp; + QString strtmp; + + d.readS32(1, &m_inputFrequencyOffset, 0); + d.readS32(2, &m_streamIndex, 0); + d.readString(3, &m_filterType, ""); + d.readString(4, &m_filterMessage, ""); + + d.readFloat(5, &m_rfBandwidth, 4000.0f); + d.readFloat(6, &m_rrcRolloff, 1.0f); + d.readFloat(7, &m_pllBW, 2*M_PI/100.0); + d.readFloat(8, &m_ssBW, 2*M_PI/100.0); + + d.readS32(9, (int *) &m_equalizer, (int) NONE); + + d.readBool(10, &m_udpEnabled); + d.readString(11, &m_udpAddress); + d.readU32(12, &utmp); + + d.readString(13, &m_logFilename, "inmarsat_c_log.csv"); + d.readBool(14, &m_logEnabled, false); + + d.readBool(15, &m_useFileTime, false); + + d.readList(16, &m_packetsColumnIndexes); + d.readList(17, &m_packetsColumnSizes); + d.readList(18, &m_messagesColumnIndexes); + d.readList(19, &m_messagesColumnSizes); + appendDefaultColumnSettings(); + + if (m_channelMarker) + { + d.readBlob(20, &bytetmp); + m_channelMarker->deserialize(bytetmp); + } + d.readU32(21, &m_rgbColor, QColor(40, 180, 75).rgb()); + d.readString(22, &m_title, "Inmarsat C Demodulator"); + d.readBool(23, &m_useReverseAPI, false); + d.readString(24, &m_reverseAPIAddress, "127.0.0.1"); + d.readU32(25, &utmp, 0); + + if ((utmp > 1023) && (utmp < 65535)) { + m_reverseAPIPort = utmp; + } else { + m_reverseAPIPort = 8888; + } + + d.readU32(26, &utmp, 0); + m_reverseAPIDeviceIndex = utmp > 99 ? 99 : utmp; + d.readU32(27, &utmp, 0); + m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; + + if ((utmp > 1023) && (utmp < 65535)) { + m_udpPort = utmp; + } else { + m_udpPort = 9999; + } + + if (m_rollupState) + { + d.readBlob(28, &bytetmp); + m_rollupState->deserialize(bytetmp); + } + + d.readS32(29, &m_workspaceIndex, 0); + d.readBlob(30, &m_geometryBytes); + d.readBool(31, &m_hidden, false); + + if (m_scopeGUI) + { + d.readBlob(32, &bytetmp); + m_scopeGUI->deserialize(bytetmp); + } + + return true; + } + else + { + resetToDefaults(); + return false; + } +} + +void InmarsatDemodSettings::applySettings(const QStringList& settingsKeys, const InmarsatDemodSettings& settings) +{ + if (settingsKeys.contains("inputFrequencyOffset")) { + m_inputFrequencyOffset = settings.m_inputFrequencyOffset; + } + if (settingsKeys.contains("rfBandwidth")) { + m_rfBandwidth = settings.m_rfBandwidth; + } + if (settingsKeys.contains("equalizer")) { + m_equalizer = settings.m_equalizer; + } + if (settingsKeys.contains("rrcRolloff")) { + m_rrcRolloff = settings.m_rrcRolloff; + } + if (settingsKeys.contains("pllBandwidth")) { + m_pllBW = settings.m_pllBW; + } + if (settingsKeys.contains("ssBandwidth")) { + m_ssBW = settings.m_ssBW; + } + if (settingsKeys.contains("filterType")) { + m_filterType = settings.m_filterType; + } + if (settingsKeys.contains("filterMessage")) { + m_filterMessage = settings.m_filterMessage; + } + if (settingsKeys.contains("udpEnabled")) { + m_udpEnabled = settings.m_udpEnabled; + } + if (settingsKeys.contains("udpAddress")) { + m_udpAddress = settings.m_udpAddress; + } + if (settingsKeys.contains("udpPort")) { + m_udpPort = settings.m_udpPort; + } + if (settingsKeys.contains("useFileTime")) { + m_useFileTime = settings.m_useFileTime; + } + if (settingsKeys.contains("logFilename")) { + m_logFilename = settings.m_logFilename; + } + if (settingsKeys.contains("logEnabled")) { + m_logEnabled = settings.m_logEnabled; + } + if (settingsKeys.contains("packetsColumnIndexes")) { + m_packetsColumnIndexes = settings.m_packetsColumnIndexes; + } + if (settingsKeys.contains("packetsColumnSizes")) { + m_packetsColumnSizes = settings.m_packetsColumnSizes; + } + if (settingsKeys.contains("messagesColumnIndexes")) { + m_messagesColumnIndexes = settings.m_messagesColumnIndexes; + } + if (settingsKeys.contains("messagesColumnSizes")) { + m_messagesColumnSizes = settings.m_messagesColumnSizes; + } + if (settingsKeys.contains("rgbColor")) { + m_rgbColor = settings.m_rgbColor; + } + if (settingsKeys.contains("title")) { + m_title = settings.m_title; + } + if (settingsKeys.contains("streamIndex")) { + m_streamIndex = settings.m_streamIndex; + } + if (settingsKeys.contains("useReverseAPI")) { + m_useReverseAPI = settings.m_useReverseAPI; + } + if (settingsKeys.contains("reverseAPIAddress")) { + m_reverseAPIAddress = settings.m_reverseAPIAddress; + } + if (settingsKeys.contains("reverseAPIPort")) { + m_reverseAPIPort = settings.m_reverseAPIPort; + } + if (settingsKeys.contains("reverseAPIDeviceIndex")) { + m_reverseAPIDeviceIndex = settings.m_reverseAPIDeviceIndex; + } + if (settingsKeys.contains("reverseAPIChannelIndex")) { + m_reverseAPIChannelIndex = settings.m_reverseAPIChannelIndex; + } + if (settingsKeys.contains("workspaceIndex")) { + m_workspaceIndex = settings.m_workspaceIndex; + } + if (settingsKeys.contains("hidden")) { + m_hidden = settings.m_hidden; + } +} + +QString InmarsatDemodSettings::getDebugString(const QStringList& settingsKeys, bool force) const +{ + std::ostringstream ostr; + + if (settingsKeys.contains("inputFrequencyOffset") || force) { + ostr << " m_inputFrequencyOffset: " << m_inputFrequencyOffset; + } + if (settingsKeys.contains("rfBandwidth") || force) { + ostr << " m_rfBandwidth: " << m_rfBandwidth; + } + if (settingsKeys.contains("equalizer") || force) { + ostr << " m_equalizer: " << m_equalizer; + } + if (settingsKeys.contains("rrcRolloff") || force) { + ostr << " m_rrcRolloff: " << m_rrcRolloff; + } + if (settingsKeys.contains("pllBandwidth") || force) { + ostr << " m_pllBW: " << m_pllBW; + } + if (settingsKeys.contains("ssBandwidth") || force) { + ostr << " m_ssBW: " << m_ssBW; + } + if (settingsKeys.contains("filterType") || force) { + ostr << " m_filterType: " << m_filterType.toStdString(); + } + if (settingsKeys.contains("filterMessage") || force) { + ostr << " m_filterMessage: " << m_filterMessage.toStdString(); + } + if (settingsKeys.contains("udpEnabled") || force) { + ostr << " m_udpEnabled: " << m_udpEnabled; + } + if (settingsKeys.contains("udpAddress") || force) { + ostr << " m_udpAddress: " << m_udpAddress.toStdString(); + } + if (settingsKeys.contains("udpPort") || force) { + ostr << " m_udpPort: " << m_udpPort; + } + if (settingsKeys.contains("logFilename") || force) { + ostr << " m_logFilename: " << m_logFilename.toStdString(); + } + if (settingsKeys.contains("logEnabled") || force) { + ostr << " m_logEnabled: " << m_logEnabled; + } + if (settingsKeys.contains("useFileTime") || force) { + ostr << " m_useFileTime: " << m_useFileTime; + } + if (settingsKeys.contains("rgbColor") || force) { + ostr << " m_rgbColor: " << m_rgbColor; + } + if (settingsKeys.contains("title") || force) { + ostr << " m_title: " << m_title.toStdString(); + } + if (settingsKeys.contains("streamIndex") || force) { + ostr << " m_streamIndex: " << m_streamIndex; + } + if (settingsKeys.contains("useReverseAPI") || force) { + ostr << " m_useReverseAPI: " << m_useReverseAPI; + } + if (settingsKeys.contains("reverseAPIAddress") || force) { + ostr << " m_reverseAPIAddress: " << m_reverseAPIAddress.toStdString(); + } + if (settingsKeys.contains("reverseAPIPort") || force) { + ostr << " m_reverseAPIPort: " << m_reverseAPIPort; + } + if (settingsKeys.contains("reverseAPIDeviceIndex") || force) { + ostr << " m_reverseAPIDeviceIndex: " << m_reverseAPIDeviceIndex; + } + if (settingsKeys.contains("reverseAPIChannelIndex") || force) { + ostr << " m_reverseAPIChannelIndex: " << m_reverseAPIChannelIndex; + } + if (settingsKeys.contains("workspaceIndex") || force) { + ostr << " m_workspaceIndex: " << m_workspaceIndex; + } + if (settingsKeys.contains("hidden") || force) { + ostr << " m_hidden: " << m_hidden; + } + + return QString(ostr.str().c_str()); +} diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodsettings.h b/plugins/channelrx/demodinmarsat/inmarsatdemodsettings.h new file mode 100644 index 000000000..aaaf3d2f8 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodsettings.h @@ -0,0 +1,93 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// Copyright (C) 2015-2022 Edouard Griffiths, F4EXB // +// Copyright (C) 2021-2026 Jon Beniston, M7RCE // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_INMARSATDEMODSETTINGS_H +#define INCLUDE_INMARSATDEMODSETTINGS_H + +#include + +#include "dsp/dsptypes.h" + +class Serializable; + +// Number of columns in the table +#define INMARSATDEMOD_MESSAGES_COLUMNS 8 +#define INMARSATDEMOD_PACKETS_COLUMNS 16 + +struct InmarsatDemodSettings +{ + qint32 m_inputFrequencyOffset; + float m_rfBandwidth; + enum Equalizer { + NONE, + CMA, //!< Constant Modulus + LMS //!< Least Mean Square + } m_equalizer; + float m_rrcRolloff; + float m_pllBW; //!< Costas loop bandwidth in rad/sample + float m_ssBW; //!< Symbol sync bandwidth normalised to symbol rate (E.g. 0.05 for 5%) + QString m_filterType; + QString m_filterMessage; + bool m_udpEnabled; + QString m_udpAddress; + uint16_t m_udpPort; + QString m_logFilename; + bool m_logEnabled; + bool m_useFileTime; + QList m_packetsColumnIndexes;//!< How the columns are ordered in the table + QList m_packetsColumnSizes; //!< Size of the columns in the table + QList m_messagesColumnIndexes; + QList m_messagesColumnSizes; + + quint32 m_rgbColor; + QString m_title; + Serializable *m_channelMarker; + int m_streamIndex; //!< MIMO channel. Not relevant when connected to SI (single Rx). + bool m_useReverseAPI; + QString m_reverseAPIAddress; + uint16_t m_reverseAPIPort; + uint16_t m_reverseAPIDeviceIndex; + uint16_t m_reverseAPIChannelIndex; + + Serializable *m_scopeGUI; + Serializable *m_rollupState; + int m_workspaceIndex; + QByteArray m_geometryBytes; + bool m_hidden; + + static const int CHANNEL_SAMPLE_RATE = 48000; + static const int BAUD_RATE = 1200; + static const int m_scopeStreams = 13; + + InmarsatDemodSettings(); + void resetToDefaults(); + void setChannelMarker(Serializable *channelMarker) { m_channelMarker = channelMarker; } + void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; } + void setScopeGUI(Serializable *scopeGUI) { m_scopeGUI = scopeGUI; } + QByteArray serialize() const; + bool deserialize(const QByteArray& data); + void applySettings(const QStringList& settingsKeys, const InmarsatDemodSettings& settings); + QString getDebugString(const QStringList& settingsKeys, bool force = false) const; + void appendDefaultColumnSettings(); + void appendDefaultColumnIndexes(QList& list, int size); + void appendDefaultColumnSizes(QList& list, int size); +}; + +#endif /* INCLUDE_INMARSATDEMODSETTINGS_H */ diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodsink.cpp b/plugins/channelrx/demodinmarsat/inmarsatdemodsink.cpp new file mode 100644 index 000000000..0cb06b4f0 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodsink.cpp @@ -0,0 +1,736 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2021-2026 Jon Beniston, M7RCE // +// Copyright (C) 2021-2022 Edouard Griffiths, F4EXB // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include + +#include "dsp/datafifo.h" +#include "dsp/scopevis.h" +#include "dsp/fftfactory.h" +#include "dsp/dspengine.h" +#include "util/db.h" +#include "device/deviceapi.h" +#include "channel/channelwebapiutils.h" +#include "maincore.h" + +#include "inmarsatdemod.h" +#include "inmarsatdemodsink.h" + +AGC::AGC() : + m_gain(1.0f) +{ +} + +Complex AGC::processOneSample(const Complex& iq, bool locked) +{ + Complex z = m_gain * iq; + + m_agcMovingAverage(abs(z)); + + Real agcRef = 1.0f; // Target amplitude + Real agcMu = locked ? 0.03 : 0.3; // How fast we want to respond to changes in average + Real agcEr = agcRef - m_agcMovingAverage.instantAverage(); // Error between target and average + + m_gain += agcMu * agcEr; + m_gain = std::clamp(m_gain, 0.01f, 10000.0f); + + return z; +} + +FrequencyOffsetEstimate::FrequencyOffsetEstimate() : + m_fftSequence(-1), + m_fft(nullptr), + m_fftCounter(0), + m_fftSize(2048), // 23Hz per bin - which is roughly pull in range of costas loop + m_freqOffsetBin(-1), + m_freqOffsetHz(0.0f), + m_currentFreqOffsetHz(0.0f), + m_freqMagSq(0.0f), + m_currentFreqMagSq(0.0) +{ + FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory(); + m_fftSequence = fftFactory->getEngine(m_fftSize, false, &m_fft); + m_fftCounter = 0; + m_fftWindow.create(FFTWindow::Rectangle, m_fftSize); +} + +FrequencyOffsetEstimate::~FrequencyOffsetEstimate() +{ + if (m_fftSequence >= 0) + { + FFTFactory *fftFactory = DSPEngine::instance()->getFFTFactory(); + fftFactory->releaseEngine(m_fftSize, false, m_fftSequence); + } +} + +void FrequencyOffsetEstimate::processOneSample(Complex& iq, bool locked) +{ + // Square signal to remove modulation + Complex cSquared = iq * iq; + + // FFT of squared signal for coarse frequency offset estimation + m_fft->in()[m_fftCounter] = cSquared; + m_fftCounter++; + if (m_fftCounter == m_fftSize) + { + // Apply windowing function + m_fftWindow.apply(m_fft->in()); + + // Perform FFT + m_fft->transform(); + + // Find peak + int maxIdx = -1; + Real maxVal = 0; + for (int i = 0; i < m_fftSize; i++) + { + Complex co = m_fft->out()[i]; + Real v = co.real() * co.real() + co.imag() * co.imag(); + Real magsq = v / (m_fftSize * m_fftSize); + if (magsq > maxVal) + { + maxIdx = i; + maxVal = magsq; + } + } + + // Calculate power for current peak bin and frequency we're locked too + m_currentFreqMagSq = magSq(maxIdx); + m_freqMagSq = magSq(m_freqOffsetBin); + + Real hzPerBin = InmarsatDemodSettings::CHANNEL_SAMPLE_RATE / (Real) m_fftSize; + int idx = maxIdx; + if (maxIdx > m_fftSize / 2) { + idx -= m_fftSize; // Negative freqs are in second half + } + m_currentFreqOffsetHz = ((idx * hzPerBin) / 2); // Divide by two, as the squaring operation doubles the freq + + Real magRatio = sqrt(m_currentFreqMagSq) / sqrt(m_freqMagSq); + + // Don't change frequency if Costas loop is locked, unless there's a big + // signal more than 2 bins away. Costas loop locked signal can falsely indicate lock + // if offset is multiple of data rate. + if (!locked || ((magRatio >= 3) && (abs(maxIdx - m_freqOffsetBin) >= 2))) + { + if (m_freqOffsetBin != maxIdx) // Only update if changed, to reduce NCO debug messages + { + m_freqOffsetBin = maxIdx; + m_freqOffsetHz = m_currentFreqOffsetHz; + m_freqOffsetNCO.setFreq(-m_freqOffsetHz, InmarsatDemodSettings::CHANNEL_SAMPLE_RATE); + } + } + + m_fftCounter = 0; + } + + // Correct for coarse frequency offset + iq *= m_freqOffsetNCO.nextIQ(); +} + +Real FrequencyOffsetEstimate::magSq(int bin) const +{ + Complex c = m_fft->out()[bin]; + Real v = c.real() * c.real() + c.imag() * c.imag(); + return v / (m_fftSize * m_fftSize); +} + +SymbolBuffer::SymbolBuffer(int size) : + m_totalError(0), + m_idx(0), + m_count(0) +{ + m_bits.resize(size); + m_symbols.resize(size); + m_error.resize(size); +} + +void SymbolBuffer::push(quint8 bit, Complex symbol) +{ + m_bits[m_idx] = bit; + m_symbols[m_idx] = symbol; + + m_totalError -= m_error[m_idx]; + Complex error; + if (bit) { + error = symbol - Complex(1.0f, 0.0f); + } else { + error = symbol - Complex(-1.0f, 0.0f); + } + m_error[m_idx] = error.real() * error.real() + error.imag() * error.imag(); + m_totalError += m_error[m_idx]; + + m_idx = (m_idx + 1) % m_bits.size(); + if (m_count < m_bits.size()) { + m_count++; + } +} + +bool SymbolBuffer::checkUW() const +{ + qulonglong bits1 = 0; + qulonglong bits2 = 0; + + for (int i = 0; i < 64; i++) + { + int idx1 = (m_idx + i * 162) % m_bits.size(); + int idx2 = (m_idx + i * 162 + 1) % m_bits.size(); + bits1 |= (qulonglong)m_bits[idx1] << (63-i); + bits2 |= (qulonglong)m_bits[idx2] << (63-i); + } + return ( (bits1 == 0x07eacdda4e2f28c2ULL) + || (bits1 == ~0x07eacdda4e2f28c2ULL) + ) + && (bits1 == bits2); +} + +Complex SymbolBuffer::getSymbol(int idx) const +{ + return m_symbols[(m_idx + idx) % m_bits.size()]; +} + +// Calculate RMS EVM +float SymbolBuffer::evm() const +{ + return sqrt((float) (m_totalError / m_count)); +} + +Equalizer::Equalizer(int samplesPerSymbol) : + m_samplesPerSymbol(samplesPerSymbol), + m_idx(0) +{ + int taps = m_samplesPerSymbol * 8 + 1; + m_delay.resize(taps); + m_taps.resize(taps); + for (int i = 0; i < taps; i++) + { + m_delay[i] = 0.0f; + m_taps[i] = 0.0f; + } +} + +void Equalizer::printTaps() const +{ + printf("taps=["); + for (int i = 0; i < m_taps.size(); i++) { + printf("%f+i*%f ", m_taps[i].real(), m_taps[i].imag()); + } + printf("];\n"); +} + +CMAEqualizer::CMAEqualizer(int samplesPerSymbol) : + Equalizer(samplesPerSymbol) +{ + m_taps[m_delay.size()/2] = 1.0f; +} + +Complex CMAEqualizer::processOneSample(Complex x, bool update, bool training) +{ + (void) training; + + for (int i = m_taps.size() - 1; i > 0; i--) { + m_delay[i] = m_delay[i-1]; + } + m_delay[0] = x; + + Complex y = 0; + for (int i = 0; i < m_taps.size(); i++) { + y += std::conj(m_taps[i]) * m_delay[i]; + } + + Real mod = abs(y); + Real R = 1.0f; + Real error = mod * mod - R * R; + if (update) + { + Real mu = 0.001f; + for (int l = 0; l < m_taps.size(); l++) { + m_taps[l] = m_taps[l] - mu * std::conj(y) * m_delay[l] * error; + } + } + m_error = error; + + return y; +} + +LMSEqualizer::LMSEqualizer(int samplesPerSymbol) : + Equalizer(samplesPerSymbol) +{ +} + +Complex LMSEqualizer::processOneSample(Complex x, bool update, bool training) +{ + for (int i = m_taps.size() - 1; i > 0; i--) { + m_delay[i] = m_delay[i-1]; + } + m_delay[0] = x; + + Complex y = 0; + for (int i = 0; i < m_taps.size(); i++) { + y += std::conj(m_taps[i]) * m_delay[i]; // Could avoid conj here, by instead using mu * std::conj(m_delay[l]) * m_error below. + } + + Complex d = (training ? x.real() : y.real()) >= 0.0f ? Complex(1.0f, 0.0f) : Complex(-1.0f, 0.0f); + m_error = d - y; + if (update) + { + Real mu = 0.1f; + for (int l = 0; l < m_taps.size(); l++) { + m_taps[l] = m_taps[l] + mu * m_delay[l] * std::conj(m_error); + } + } + + return y; +} + +InmarsatDemodSink::InmarsatDemodSink(InmarsatDemod *stdCDemod) : + m_scopeSink(nullptr), + m_inmarsatDemod(stdCDemod), + m_channelSampleRate(InmarsatDemodSettings::CHANNEL_SAMPLE_RATE), + m_channelFrequencyOffset(0), + m_magsqSum(0.0f), + m_magsqPeak(0.0f), + m_magsqCount(0), + m_messageQueueToChannel(nullptr), + m_decoder(9), + m_symbolCounter(0), + m_syncedToUW(false), + m_equalizer(nullptr), + m_costasLoop(10*2*M_PI/100.0, 2), + m_rrcBufferIndex(0), + m_sampleBufferIndexA(0), + m_sampleBufferIndexB(0) +{ + m_magsq = 0.0; + + m_rrcFilter = new fftfilt(m_settings.m_rfBandwidth / (float) m_channelSampleRate, RRC_FILTER_SIZE); + + m_rrcI.create(m_settings.m_rrcRolloff, 5, SAMPLES_PER_SYMBOL, RootRaisedCosine::Gain); + m_rrcQ.create(m_settings.m_rrcRolloff, 5, SAMPLES_PER_SYMBOL, RootRaisedCosine::Gain); + + m_sampleIdx = 0; + + m_adjustedSPS = MAX_SAMPLES_PER_SYMBOL; + m_adjustment = 0; + m_totalSampleCount = 0; + m_error = 0; + m_errorSum = 0; + m_mu = 0.0; + m_bit = 0; + m_bitCount = 0; + m_filteredSamples.enqueue(0); // Prev sample for first iteration + + for (int i = 0; i < InmarsatDemodSettings::m_scopeStreams; i++) { + m_sampleBuffer[i].resize(SAMPLE_BUFFER_SIZE); + } + + applySettings(m_settings, QStringList(), true); + applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, true); +} + +InmarsatDemodSink::~InmarsatDemodSink() +{ + delete m_rrcFilter; + m_rrcFilter = nullptr; + delete m_equalizer; + m_equalizer = nullptr; +} + +void InmarsatDemodSink::sampleToScopeA(Complex sample, Real magsq, Complex postAGC, Real agcGain, Real agcAvg, Complex postCFO) +{ + if (m_scopeSink) + { + m_sampleBuffer[0][m_sampleBufferIndexA] = sample; + m_sampleBuffer[1][m_sampleBufferIndexA] = Complex(magsq, 0.0f); + m_sampleBuffer[2][m_sampleBufferIndexA] = postAGC; + m_sampleBuffer[3][m_sampleBufferIndexA] = Complex(agcGain, agcAvg); + m_sampleBuffer[4][m_sampleBufferIndexA] = postCFO; + m_sampleBufferIndexA++; + } +} + +void InmarsatDemodSink::sampleToScopeB(Complex rrc, Complex tedError, Complex tedErrorSum, Complex ref, Complex derot, Complex eq, Complex eqError, int bit, Real mu) +{ + if (m_scopeSink) + { + m_sampleBuffer[5][m_sampleBufferIndexB] = rrc; + m_sampleBuffer[6][m_sampleBufferIndexB] = tedError; + m_sampleBuffer[7][m_sampleBufferIndexB] = tedErrorSum; + m_sampleBuffer[8][m_sampleBufferIndexB] = ref; + m_sampleBuffer[9][m_sampleBufferIndexB] = derot; + m_sampleBuffer[10][m_sampleBufferIndexB] = eq; + m_sampleBuffer[11][m_sampleBufferIndexB] = eqError; + m_sampleBuffer[12][m_sampleBufferIndexB] = Complex(bit, mu); + m_sampleBufferIndexB++; + if (m_sampleBufferIndexB == SAMPLE_BUFFER_SIZE) + { + std::vector vbegin; + + for (int i = 0; i < InmarsatDemodSettings::m_scopeStreams; i++) { + vbegin.push_back(m_sampleBuffer[i].begin()); + } + + m_scopeSink->feed(vbegin, SAMPLE_BUFFER_SIZE); + if (m_sampleBufferIndexA != m_sampleBufferIndexB) { + qDebug() << "m_sampleBufferIndexA != m_sampleBufferIndexB" << m_sampleBufferIndexA << m_sampleBufferIndexB; + } + m_sampleBufferIndexA = 0; + m_sampleBufferIndexB = 0; + } + } +} + +void InmarsatDemodSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) +{ + Complex ci; + + for (SampleVector::const_iterator it = begin; it != end; ++it) + { + Complex c(it->real(), it->imag()); + c *= m_nco.nextIQ(); + + if (m_interpolatorDistance < 1.0f) // interpolate + { + while (!m_interpolator.interpolate(&m_interpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } + else // decimate + { + if (m_interpolator.decimate(&m_interpolatorDistanceRemain, c, &ci)) + { + processOneSample(ci); + m_interpolatorDistanceRemain += m_interpolatorDistance; + } + } + } +} + +// Wrap to range [-0.5, 0.5) +static Real wrap(Real v) +{ + if (v > 0.0f) { + return fmod(v + 0.5f, 1.0f) - 0.5f; + } else { + return fmod(v - 0.5f, 1.0f) + 0.5f; + } +} + +void InmarsatDemodSink::processOneSample(Complex &ci) +{ + // Calculate average and peak levels for level meter + Real re = ci.real() / SDR_RX_SCALEF; + Real im = ci.imag() / SDR_RX_SCALEF; + Real magsq = re*re + im*im; + m_movingAverage(magsq); + m_magsq = m_movingAverage.asDouble(); + m_magsqSum += magsq; + if (magsq > m_magsqPeak) { + m_magsqPeak = magsq; + } + m_magsqCount++; + + // AGC + + Complex cScaled = Complex(re, im); + Complex agcZ = m_agc.processOneSample(cScaled, m_locked); + + // Coarse frequency offset correction + + Complex cCFO = agcZ; + m_frequencyOffsetEst.processOneSample(cCFO, m_locked); + + // Send signals to scope that are updated for each invocation of this method + sampleToScopeA(cScaled, magsq, agcZ, m_agc.getGain(), m_agc.getAverage(), cCFO); + + // RRC Matched filter + fftfilt::cmplx *rrcFilterOut = nullptr; + int n_out = m_rrcFilter->runFilt(cCFO, &rrcFilterOut); + + m_rrcBuffer[m_rrcBufferIndex++] = cCFO; + + if (m_rrcBufferIndex == RRC_FILTER_SIZE/2) + { + n_out = m_rrcBufferIndex; + m_rrcBufferIndex = 0; + } + else + { + n_out = 0; + } + + for (int i = 0; i < n_out; i++) + { + //Complex rrc = rrcFilterOut[i]; + Complex rrc (m_rrcI.filter(m_rrcBuffer[i].real()), m_rrcQ.filter(m_rrcBuffer[i].imag())); + + // Symbol synchronizer + + m_totalSampleCount++; + m_filteredSamples.enqueue(rrc); + while (m_filteredSamples.size() > MAX_SAMPLES_PER_SYMBOL) + { + m_filteredSamples.dequeue(); + + if (m_sampleIdx == m_adjustedSPS - 1) + { + // Gardner timing error detector + + int currentIdx = m_filteredSamples.size()-1; + int midIdx = currentIdx - (SAMPLES_PER_SYMBOL/2.0f); + int previousIdx = currentIdx - SAMPLES_PER_SYMBOL; + + Complex previous = m_filteredSamples[previousIdx]; + Complex mid = m_filteredSamples[midIdx]; + Complex current = m_filteredSamples[currentIdx]; + + m_error = (current.real() - previous.real()) * mid.real() + + (current.imag() - previous.imag()) * mid.imag(); + m_errorSum += m_error; + + // Symbol synchronizer adjustment + + m_mu = m_kp * m_error + m_ki * m_errorSum; // Positive error is late + m_mu = wrap(m_mu); + + int adjustment = (int)round(m_mu * SAMPLES_PER_SYMBOL); + + m_adjustedSPS = SAMPLES_PER_SYMBOL - m_adjustment; // Positve mu indicates late, so reduce time to next sample + m_adjustment = adjustment; + m_prevTotalSampleCount = m_totalSampleCount; + + // Costas loop for fine phase/freq correction - runs at symbol rate + + m_costasLoop.feed(current.real(), current.imag()); + m_ref = -std::conj(m_costasLoop.getComplex()); + m_derot = current * m_ref; + + // Determine whether Costas loop is locked - BPSK so Q part should average as zero when locked, and will be similar to I when not locked + Real derotMag = abs(m_derot); + Real iNorm = abs(m_derot.real()) / derotMag; + Real qNorm = abs(m_derot.imag()) / derotMag; + m_lockAverage(iNorm - qNorm); + float lockThresh = 0.45; + m_locked = m_lockAverage.instantAverage() > lockThresh; + + // Equalizer (runs at symbol rate) + if ( (m_settings.m_equalizer == InmarsatDemodSettings::CMA) + || (m_settings.m_equalizer == InmarsatDemodSettings::LMS && m_syncedToUW) + ) + { + m_eq = m_equalizer->processOneSample(m_derot, true, false); + } else { + m_eq = m_derot; + } + + // Symbol to bit + m_bit = m_eq.real() >= 0; + m_bits[m_bitCount++] = m_bit; + + // Look for Unique Words to synchronize to start of frame + m_symbolBuffer.push(m_bit, m_eq); + if (m_symbolBuffer.checkUW()) + { + m_symbolCounter = 0; + if (!m_syncedToUW && (m_settings.m_equalizer == InmarsatDemodSettings::LMS)) + { + // Train equalizer on unique word symbols + for (int i = 0; i < 64; i++) { + m_equalizer->processOneSample(m_symbolBuffer.getSymbol(i * 162), true, true); + } + for (int i = 0; i < 64; i++) { + m_equalizer->processOneSample(m_symbolBuffer.getSymbol(i * 162 + 1), true, true); + } + } + m_syncedToUW = true; + } + else if (m_syncedToUW) + { + // Allow 3 frames without UW before assuming sync loss (only currently effects whether we run LMS eq) + // Could allow a certain number of bit errors in Unique Words instead + if (m_symbolCounter < 3 * m_symbolBuffer.size()) + { + m_symbolCounter++; + } + else + { + m_symbolCounter = 0; + m_syncedToUW = false; + } + } + + // Pass demodulated bits to decoder + if (m_bitCount == DEMODULATOR_SYMBOLSPERCHUNK) + { + decodeBits(); + m_bitCount = 0; + } + + // Calculate EVM + m_evm = m_symbolBuffer.evm(); + + m_sampleIdx = 0; + } + else + { + m_sampleIdx++; + } + } + + // Send signals to scope that are updated only whenever the RRC filter output is updated + sampleToScopeB(rrc, m_error, m_errorSum, m_ref, m_derot, m_eq, m_equalizer ? m_equalizer->getError() : Complex(0.0), m_bit*2-1, m_mu); + } +} + +void InmarsatDemodSink::decodeBits() +{ + std::vector decoderResults; + + decoderResults = m_decoder.decode(m_bits); + + if (decoderResults.size() > 0) + { + for (int j = 0; j < (int) decoderResults.size(); j++) + { + QByteArray rxPacket; + rxPacket.resize(sizeof(inmarsatc::decoder::Decoder::decoder_result)); + memcpy(rxPacket.data(), &decoderResults[j], sizeof(inmarsatc::decoder::Decoder::decoder_result)); + + if (getMessageQueueToChannel()) + { + QDateTime dateTime = QDateTime::currentDateTime(); + if (m_settings.m_useFileTime) + { + QString hardwareId = m_inmarsatDemod->getDeviceAPI()->getHardwareId(); + + if ((hardwareId == "FileInput") || (hardwareId == "SigMFFileInput")) + { + QString dateTimeStr; + int deviceIdx = m_inmarsatDemod->getDeviceSetIndex(); + + if (ChannelWebAPIUtils::getDeviceReportValue(deviceIdx, "absoluteTime", dateTimeStr)) { + dateTime = QDateTime::fromString(dateTimeStr, Qt::ISODateWithMs); + } + } + } + + MainCore::MsgPacket *msg = MainCore::MsgPacket::create(m_inmarsatDemod, rxPacket, dateTime); + getMessageQueueToChannel()->push(msg); + } + } + } +} + +void InmarsatDemodSink::getPLLStatus(bool &locked, Real &coarseFreqCurrent, Real &coarseFreqCurrentPower, Real &coarseFreq, Real &coarseFreqPower, Real &fineFreq, Real &evm, bool &synced) const +{ + locked = m_locked; + coarseFreqCurrent = m_frequencyOffsetEst.getCurrentFreqHz(); + coarseFreqCurrentPower = m_frequencyOffsetEst.getCurrentFreqMagSq(); + coarseFreq = m_frequencyOffsetEst.getFreqHz(); + coarseFreqPower = m_frequencyOffsetEst.getFreqMagSq(); + fineFreq = m_costasLoop.getFreq() * COSTAS_LOOP_RATE / (2 * M_PI); + evm = m_evm; + synced = m_syncedToUW; +} + +void InmarsatDemodSink::applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force) +{ + qDebug() << "InmarsatDemodSink::applyChannelSettings:" + << " channelSampleRate: " << channelSampleRate + << " channelFrequencyOffset: " << channelFrequencyOffset; + + if ((m_channelFrequencyOffset != channelFrequencyOffset) || + (m_channelSampleRate != channelSampleRate) || force) + { + m_nco.setFreq(-channelFrequencyOffset, channelSampleRate); + } + + if ((m_channelSampleRate != channelSampleRate) || force) + { + m_interpolator.create(16, channelSampleRate, m_settings.m_rfBandwidth / 2.2); + m_interpolatorDistance = (Real) channelSampleRate / (Real) InmarsatDemodSettings::CHANNEL_SAMPLE_RATE; + m_interpolatorDistanceRemain = m_interpolatorDistance; + } + + m_channelSampleRate = channelSampleRate; + m_channelFrequencyOffset = channelFrequencyOffset; +} + +void InmarsatDemodSink::applySettings(const InmarsatDemodSettings& settings, const QStringList& settingsKeys, bool force) +{ + qDebug() << "InmarsatDemodSink::applySettings:" + << settings.getDebugString(settingsKeys, force) + << " force: " << force; + + if (settingsKeys.contains("rfBandwidth") || force) + { + m_interpolator.create(16, m_channelSampleRate, settings.m_rfBandwidth / 2.2); + m_interpolatorDistance = (Real) m_channelSampleRate / (Real) InmarsatDemodSettings::CHANNEL_SAMPLE_RATE; + m_interpolatorDistanceRemain = m_interpolatorDistance; + + // Limit costas loop frequency range to stay within RF bandwidth - it probably can't track this far anyway + Real freqMaxHz = (settings.m_rfBandwidth / 2.0f - InmarsatDemodSettings::BAUD_RATE / 2.0f); + //qDebug() << "InmarsatDemodSink::applySettings: Costas loop freq limit: +/-" << freqMaxHz << "Hz"; + Real freqMax = freqMaxHz * 2 * M_PI / COSTAS_LOOP_RATE; + m_costasLoop.setMaxFreq(freqMax); + m_costasLoop.setMinFreq(-freqMax); + } + + if (settingsKeys.contains("rfBandwidth") || settingsKeys.contains("rrcRolloff") || force) { + m_rrcFilter->create_rrc_filter(settings.m_rfBandwidth / (float) m_channelSampleRate, settings.m_rrcRolloff); + } + + if (settingsKeys.contains("pllBandwidth") || force) { + m_costasLoop.computeCoefficients(settings.m_pllBW); + } + + if (settingsKeys.contains("ssBandwidth") || force) + { + /* + float kd = 1.5f; // TED gain + float zeta = 1.0f / sqrtf(2.0f); // Damping factor - critical damping + float theta = (m_settings.m_ssBW / SAMPLES_PER_SYMBOL) / (zeta + (1.0f / (4.0f * zeta))); + float denom = ((1.0f + 2.0f * zeta * theta + theta * theta) * kd); + + m_kp = (4.0f * zeta * theta) / denom; + m_ki = (4.0f * theta * theta) / denom; + + qDebug() << "m_kp" << m_kp << "m_ki" << m_ki; + */ + + m_kp = 0.1f; + m_ki = 0.01f; + } + + if (settingsKeys.contains("equalizer") || force) + { + delete m_equalizer; + m_equalizer = nullptr; + if (settings.m_equalizer == InmarsatDemodSettings::CMA) { + m_equalizer = new CMAEqualizer(1); + } else if (settings.m_equalizer == InmarsatDemodSettings::LMS) { + m_equalizer = new LMSEqualizer(1); + } + } + + if (force) { + m_settings = settings; + } else { + m_settings.applySettings(settingsKeys, settings); + } +} diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodsink.h b/plugins/channelrx/demodinmarsat/inmarsatdemodsink.h new file mode 100644 index 000000000..62d8f0fe9 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodsink.h @@ -0,0 +1,256 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019-2021 Edouard Griffiths, F4EXB // +// Copyright (C) 2020-2026 Jon Beniston, M7RCE // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_INMARSATDEMODSINK_H +#define INCLUDE_INMARSATDEMODSINK_H + +#include +#include + +#include "dsp/channelsamplesink.h" +#include "dsp/nco.h" +#include "dsp/interpolator.h" +#include "dsp/fftfilt.h" +#include "dsp/fftengine.h" +#include "dsp/fftwindow.h" +#include "dsp/costasloop.h" +#include "dsp/rootraisedcosine.h" +#include "util/movingaverage.h" +#include "util/messagequeue.h" + +#include "inmarsatdemodsettings.h" + +class ChannelAPI; +class InmarsatDemod; +class ScopeVis; + +// Automatic Gain Control +class AGC { +public: + AGC(); + Complex processOneSample(const Complex &iq, bool locked); + Real getGain() const { return m_gain; } + Real getAverage() const { return m_agcMovingAverage.instantAverage(); } + +private: + Real m_gain; + MovingAverageUtil m_agcMovingAverage; +}; + +class FrequencyOffsetEstimate { +public: + FrequencyOffsetEstimate(); + ~FrequencyOffsetEstimate(); + void processOneSample(Complex& iq, bool locked); + Real getFreqHz() const { return m_freqOffsetHz; } + Real getCurrentFreqHz() const { return m_currentFreqOffsetHz; } + Real getFreqMagSq() const { return m_freqMagSq; } + Real getCurrentFreqMagSq() const { return m_currentFreqMagSq; } + +private: + int m_fftSequence; + FFTEngine *m_fft; + int m_fftCounter; + FFTWindow m_fftWindow; + int m_fftSize; + int m_freqOffsetBin; + Real m_freqOffsetHz; + Real m_currentFreqOffsetHz; + Real m_freqMagSq; + Real m_currentFreqMagSq; + NCO m_freqOffsetNCO; + + Real magSq(int bin) const; +}; + +// Circular symbol/bit buffer for unique word detection, EVM calculation and equalizer training +class SymbolBuffer { +public: + SymbolBuffer(int size=64*162); + void push(quint8 bit, Complex symbol); + bool checkUW() const; + Complex getSymbol(int idx) const; + float evm() const; + qsizetype size() const { return m_bits.size(); } +private: + QVector m_bits; + QVector m_symbols; + QVector m_error; + double m_totalError; + int m_idx; + int m_count; +}; + +class Equalizer { +public: + Equalizer(int samplesPerSymbol); + virtual ~Equalizer() {} + virtual Complex processOneSample(Complex x, bool update, bool training=false) = 0; + Complex getError() const { return m_error; } + void printTaps() const; +protected: + int m_samplesPerSymbol; + QVector m_delay; + QVector m_taps; + int m_idx; + Complex m_error; +}; + +// Constant Modulus Equalizer +class CMAEqualizer : public Equalizer { +public: + CMAEqualizer(int samplesPerSymbol); + Complex processOneSample(Complex x, bool update, bool training=false) override; +}; + +// Least Mean Square Equalizer +class LMSEqualizer : public Equalizer { +public: + LMSEqualizer(int samplesPerSymbol); + Complex processOneSample(Complex x, bool update, bool training=false) override; +}; + +class InmarsatDemodSink : public ChannelSampleSink { +public: + InmarsatDemodSink(InmarsatDemod *stdcDemod); + ~InmarsatDemodSink(); + + virtual void feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end); + + void setScopeSink(ScopeVis* scopeSink) { m_scopeSink = scopeSink; } + void applyChannelSettings(int channelSampleRate, int channelFrequencyOffset, bool force = false); + void applySettings(const InmarsatDemodSettings& settings, const QStringList& settingsKeys, bool force = false); + void setMessageQueueToChannel(MessageQueue *messageQueue) { m_messageQueueToChannel = messageQueue; } + void setChannel(ChannelAPI *channel) { m_channel = channel; } + + double getMagSq() const { return m_magsq; } + + void getMagSqLevels(double& avg, double& peak, int& nbSamples) + { + if (m_magsqCount > 0) + { + m_magsq = m_magsqSum / m_magsqCount; + m_magSqLevelStore.m_magsq = m_magsq; + m_magSqLevelStore.m_magsqPeak = m_magsqPeak; + } + + avg = m_magSqLevelStore.m_magsq; + peak = m_magSqLevelStore.m_magsqPeak; + nbSamples = m_magsqCount == 0 ? 1 : m_magsqCount; + + m_magsqSum = 0.0f; + m_magsqPeak = 0.0f; + m_magsqCount = 0; + } + + void getPLLStatus(bool &locked, Real &coarseFreqCurrent, Real &coarseFreqCurrentPower, Real &coarseFreq, Real &coarseFreqPower, + Real &fineFreq, Real& evm, bool &synced) const; + +private: + struct MagSqLevelsStore + { + MagSqLevelsStore() : + m_magsq(1e-12), + m_magsqPeak(1e-12) + {} + double m_magsq; + double m_magsqPeak; + }; + + ScopeVis* m_scopeSink; // Scope GUI to display baseband waveform + InmarsatDemod *m_inmarsatDemod; + InmarsatDemodSettings m_settings; + ChannelAPI *m_channel; + int m_channelSampleRate; + int m_channelFrequencyOffset; + + NCO m_nco; + Interpolator m_interpolator; + Real m_interpolatorDistance; + Real m_interpolatorDistanceRemain; + + double m_magsq; + double m_magsqSum; + double m_magsqPeak; + int m_magsqCount; + MagSqLevelsStore m_magSqLevelStore; + + MessageQueue *m_messageQueueToChannel; + + MovingAverageUtil m_movingAverage; + + inmarsatc::decoder::Decoder m_decoder; + inmarsatc::frameParser::PacketDecoder m_parser; + + SymbolBuffer m_symbolBuffer; + int m_symbolCounter; + bool m_syncedToUW; + + Equalizer *m_equalizer; + Complex m_eq; + Complex m_eqError; + float m_evm; + + CostasLoop m_costasLoop; + Complex m_ref; + Complex m_derot; + MovingAverageUtil m_lockAverage; + bool m_locked; + + static const int RRC_FILTER_SIZE = 256; + fftfilt *m_rrcFilter; + Complex m_rrcBuffer[RRC_FILTER_SIZE]; + RootRaisedCosine m_rrcI; //!< Square root raised cosine filter for I samples + RootRaisedCosine m_rrcQ; //!< Square root raised cosine filter for Q samples + int m_rrcBufferIndex; + + static const int SAMPLES_PER_SYMBOL = InmarsatDemodSettings::CHANNEL_SAMPLE_RATE / InmarsatDemodSettings::BAUD_RATE; + static const int MAX_SAMPLES_PER_SYMBOL = SAMPLES_PER_SYMBOL * 2; + static const int COSTAS_LOOP_RATE = InmarsatDemodSettings::BAUD_RATE; // Costas loop is run at symbol rate + + int m_sampleIdx; + QQueue m_filteredSamples; + float m_ki; + float m_kp; + Real m_error; + Real m_errorSum; + Real m_mu; + int m_adjustedSPS; + int m_adjustment; + qint64 m_totalSampleCount; + qint64 m_prevTotalSampleCount; + int m_bit; + uint8_t m_bits[DEMODULATOR_SYMBOLSPERCHUNK]; + int m_bitCount; + + AGC m_agc; + FrequencyOffsetEstimate m_frequencyOffsetEst; + + ComplexVector m_sampleBuffer[InmarsatDemodSettings::m_scopeStreams]; + static const int SAMPLE_BUFFER_SIZE = RRC_FILTER_SIZE * 10; // Needs to be a multiple of m_rrcFilterSize, so A & B fill up at the same time + int m_sampleBufferIndexA; + int m_sampleBufferIndexB; + + void processOneSample(Complex &ci); + void decodeBits(); + MessageQueue *getMessageQueueToChannel() { return m_messageQueueToChannel; } + void sampleToScopeA(Complex sample, Real magsq, Complex postAGC, Real agcGain, Real agcAvg, Complex postCFO); + void sampleToScopeB(Complex rrc, Complex tedError, Complex tedErrorSum, Complex ref, Complex derot, Complex eq, Complex eqEerror, int bit, Real mu); +}; + +#endif // INCLUDE_INMARSATDEMODSINK_H diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodwebapiadapter.cpp b/plugins/channelrx/demodinmarsat/inmarsatdemodwebapiadapter.cpp new file mode 100644 index 000000000..32f6cb625 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodwebapiadapter.cpp @@ -0,0 +1,54 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2012 maintech GmbH, Otto-Hahn-Str. 15, 97204 Hoechberg, Germany // +// written by Christian Daniel // +// Copyright (C) 2015-2020 Edouard Griffiths, F4EXB // +// Copyright (C) 2021-2026 Jon Beniston, M7RCE // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "SWGChannelSettings.h" +#include "inmarsatdemod.h" +#include "inmarsatdemodwebapiadapter.h" + +InmarsatDemodWebAPIAdapter::InmarsatDemodWebAPIAdapter() +{} + +InmarsatDemodWebAPIAdapter::~InmarsatDemodWebAPIAdapter() +{} + +int InmarsatDemodWebAPIAdapter::webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) errorMessage; + response.setInmarsatDemodSettings(new SWGSDRangel::SWGInmarsatDemodSettings()); + response.getInmarsatDemodSettings()->init(); + InmarsatDemod::webapiFormatChannelSettings(response, m_settings); + + return 200; +} + +int InmarsatDemodWebAPIAdapter::webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage) +{ + (void) force; + (void) errorMessage; + InmarsatDemod::webapiUpdateChannelSettings(m_settings, channelSettingsKeys, response); + + return 200; +} diff --git a/plugins/channelrx/demodinmarsat/inmarsatdemodwebapiadapter.h b/plugins/channelrx/demodinmarsat/inmarsatdemodwebapiadapter.h new file mode 100644 index 000000000..bfe4c74e5 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/inmarsatdemodwebapiadapter.h @@ -0,0 +1,50 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 Edouard Griffiths, F4EXB // +// Copyright (C) 2020-2026 Jon Beniston, M7RCE // +// // +// 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 as version 3 of the License, or // +// (at your option) any later version. // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_INMARSATDEMOD_WEBAPIADAPTER_H +#define INCLUDE_INMARSATDEMOD_WEBAPIADAPTER_H + +#include "channel/channelwebapiadapter.h" +#include "inmarsatdemodsettings.h" + +/** + * Standalone API adapter only for the settings + */ +class InmarsatDemodWebAPIAdapter : public ChannelWebAPIAdapter { +public: + InmarsatDemodWebAPIAdapter(); + virtual ~InmarsatDemodWebAPIAdapter(); + + virtual QByteArray serialize() const { return m_settings.serialize(); } + virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); } + + virtual int webapiSettingsGet( + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + + virtual int webapiSettingsPutPatch( + bool force, + const QStringList& channelSettingsKeys, + SWGSDRangel::SWGChannelSettings& response, + QString& errorMessage); + +private: + InmarsatDemodSettings m_settings; +}; + +#endif // INCLUDE_INMARSATDEMOD_WEBAPIADAPTER_H diff --git a/plugins/channelrx/demodinmarsat/readme.md b/plugins/channelrx/demodinmarsat/readme.md new file mode 100644 index 000000000..271741550 --- /dev/null +++ b/plugins/channelrx/demodinmarsat/readme.md @@ -0,0 +1,123 @@ +

Inmarsat C Demodulator Plugin

+ +

Introduction

+ +This plugin can be used to demodulate Inmarsat C data packets. They are transmitted by four geostationary satellites, using right-hand circular polarization (RHCP). + +The packets are BPSK modulated with a symbol rate of 1,200 symbols/s and a data rate of 600 bit/s. +The packets are decoded from within frames of 639 bytes that take 8.64 seconds to receive. + +Satellites and corresponding frequencies are: + +| Region | Id | Satellite | Longitude | Frequency | +|---------------------|-------|---------------------------|-----------|--------------| +| Atlantic Ocean West | AOR-W | Inmarsat 4-F3 | 98W | 1,537.70 MHz | +| Atlantic Ocean East | AOR-E | Inmarsat 3-F5 | 54W | 1,541.45 MHz | +| Pacific Ocean | POR | Inmarsat 4-F1 | 178E | 1,541.45 MHz | +| Indian Ocean | IOR | Inmarsat 4A-F4 / Alphasat | 25E | 1,537.10 MHz | + +The direction from your anntenna to the satellites can be determined in the [Satellite Tracker Feature](../../feature/satellitetracker/readme.md): + +Messages containing coordinates in the address or message fields can be displayed on the [Map Feature](../../feature/map/readme.md): + +![Inmarsat Demodulator plugin GUI](../../../doc/img/InmarsatDemod_map.png) + +More information about the encoding of EGC (Enhanced Group Call) messages can be found in: +[Safety NET Manual](https://www.navcen.uscg.gov/sites/default/files/pdf/gmdss/Safety_NET_Manual.pdf) + +

Interface

+ +The top and bottom bars of the channel window are described [here](../../../sdrgui/channel/readme.md) + +![Inmarsat Demodulator plugin GUI](../../../doc/img/InmarsatDemod_plugin.png) + +

1: Frequency shift from center frequency of reception

+ +Use the wheels to adjust the frequency shift in Hz from the center frequency of reception. Left click on a digit sets the cursor position at this digit. Right click on a digit sets all digits on the right to zero. This effectively floors value at the digit position. Wheels are moved with the mousewheel while pointing at the wheel or by selecting the wheel with the left mouse click and using the keyboard arrows. Pressing shift simultaneously moves digit by 5 and pressing control moves it by 2. + +

2: Channel power

+ +Average total power in dB relative to a +/- 1.0 amplitude signal received in the pass band. + +

3: Level meter in dB

+ + - top bar (green): average value + - bottom bar (blue green): instantaneous peak value + - tip vertical bar (bright green): peak hold value + +

4: RF Bandwidth

+ +This specifies the bandwidth of a LPF that is applied to the input signal to limit the RF bandwidth. This should be at least 2.4 kHz. + +

5: Equalizer

+ +The Equalizer combobox allows selection of an equalizer to use, which can help to overcome some forms of channel distortion. Options are: + + - None: No equalizer will be used. + - CMA: Constant Modulus Algorithm equalizer. + - LMS: Least Mean Squares equalizer. + +

6: EVM

+ +EVM (Error Vector Magnitude) measurement in % over the last 10,240 received symbols (one frames worth). Lower values indicate better quality signals. + +

7: Status

+ +The status icon shows the status of the demodulator: + + - When the background is green, the demodulator is synchronized to the start of a frame. + - When the background is yellow, the demodulator Costas Loop is locked, but it is not yet synchronized to the start of a frame. + - When the background is grey, the Costas Loop is not locked, which suggests either no signal or too weak a signal to lock on to. + +Checking the icon will diplay additional PLL status and parameters, but these typically shouldn't require modification. + +

8: UDP

+ +When checked, received packets are forwarded to the specified UDP address (9) and port (10). + +

9: UDP address

+ +IP address of the host to forward received packets to via UDP. + +

10: UDP port

+ +UDP port number to forward received packets to. + +

11: Filter Packets by Type

+ +Entering a regular expression in the Type field displays only packets where the type, displayed in the Type column, matches the regular expression. + +

12: Filter Packets by Message

+ +Entering a regular expression in the Message field displays only packets where the message, displayed in the Message column, matches the regular expression. + +

13: Use Date and Time from File

+ +When checked, if the source device is a File Input device, the date and time used for +packet reception time is taken from the file playback time. Otherwise, the current system clock time is used. + +

14: Start/stop Logging Packets to .csv File

+ +When checked, writes all received packets to a .csv file. + +

15: .csv Log Filename

+ +Click to specify the name of the .csv file which received packets are logged to. + +

16: Read Data from .csv File

+ +Click to specify a previously written .csv log file, which is read and used to update the table. + +

17: Clear Packets from table

+ +Pressing this button clears all packets from the tables. + +

Received Packets

+ +The received packets area has two tabs, which each contain a table. The Packets table displays the contents of all packets that have been received. The Messages table displays only packets that contain messages. +Left clicking a row in the table will highlight it. The highlighted packet will be decoded in the area below the tables. + +Right click on the table headers to select which columns are visible. Columns can be resized and reordered by dragging. + +Right clicking on a cell in the table will show a context menu, allowing the contents to be copied. +If the message contains coordinates, an option to find that location on the [Map Feature](../../feature/map/readme.md) will be shown. diff --git a/plugins/channelrx/freqscanner/freqscannergui.cpp b/plugins/channelrx/freqscanner/freqscannergui.cpp index 02887781d..b0167df34 100644 --- a/plugins/channelrx/freqscanner/freqscannergui.cpp +++ b/plugins/channelrx/freqscanner/freqscannergui.cpp @@ -90,7 +90,11 @@ bool FreqScannerGUI::handleMessage(const Message& message) { qDebug("FreqScannerGUI::handleMessage: FreqScanner::MsgConfigureFreqScanner"); const FreqScanner::MsgConfigureFreqScanner& cfg = (FreqScanner::MsgConfigureFreqScanner&) message; - m_settings = cfg.getSettings(); + if (cfg.getForce()) { + m_settings = cfg.getSettings(); + } else { + m_settings.applySettings(cfg.getSettingsKeys(), cfg.getSettings()); + } blockApplySettings(true); m_channelMarker.updateSettings(static_cast(m_settings.m_channelMarker)); displaySettings(); diff --git a/plugins/channelrx/freqscanner/freqscannersink.cpp b/plugins/channelrx/freqscanner/freqscannersink.cpp index a7069162d..c0bb5cd27 100644 --- a/plugins/channelrx/freqscanner/freqscannersink.cpp +++ b/plugins/channelrx/freqscanner/freqscannersink.cpp @@ -40,13 +40,17 @@ FreqScannerSink::FreqScannerSink() : m_binsPerChannel(16), m_averageCount(0) { - applySettings(m_settings, QStringList(), true); applyChannelSettings(m_channelSampleRate, m_channelFrequencyOffset, 16, 4, true); } FreqScannerSink::~FreqScannerSink() { + if (m_fftSequence >= 0) + { + FFTFactory* fftFactory = DSPEngine::instance()->getFFTFactory(); + fftFactory->releaseEngine(m_fftSize, false, m_fftSequence); + } } void FreqScannerSink::feed(const SampleVector::const_iterator& begin, const SampleVector::const_iterator& end) diff --git a/plugins/channelrx/radioastronomy/radioastronomygui.cpp b/plugins/channelrx/radioastronomy/radioastronomygui.cpp index 6f4ad839f..28356cf8a 100644 --- a/plugins/channelrx/radioastronomy/radioastronomygui.cpp +++ b/plugins/channelrx/radioastronomy/radioastronomygui.cpp @@ -1506,6 +1506,8 @@ void RadioAstronomyGUI::savePowerData(const QString& filename) // Save power data in table to a CSV file void RadioAstronomyGUI::on_savePowerData_clicked(bool checked) { + (void) checked; + ui->savePowerData->setChecked(!m_settings.m_powerAutoSaveCSVFilename.isEmpty()); // Get filename to save to @@ -1799,6 +1801,8 @@ void RadioAstronomyGUI::saveSpectrumData(const QString& filename) void RadioAstronomyGUI::on_saveSpectrumData_clicked(bool checked) { + (void) checked; + ui->saveSpectrumData->setChecked(!m_settings.m_spectrumAutoSaveCSVFilename.isEmpty()); // Get filename to save to diff --git a/plugins/channeltx/moddatv/datvmodsource.cpp b/plugins/channeltx/moddatv/datvmodsource.cpp index e3d43ec9c..9f1d6e739 100644 --- a/plugins/channeltx/moddatv/datvmodsource.cpp +++ b/plugins/channeltx/moddatv/datvmodsource.cpp @@ -684,8 +684,8 @@ void DATVModSource::applyChannelSettings(int channelSampleRate, int channelFrequ if (m_settings.m_symbolRate > 0) m_samplesPerSymbol = m_channelSampleRate/m_settings.m_symbolRate; - m_pulseShapeI.create(m_settings.m_rollOff, 8, m_samplesPerSymbol, false); - m_pulseShapeQ.create(m_settings.m_rollOff, 8, m_samplesPerSymbol, false); + m_pulseShapeI.create(m_settings.m_rollOff, 8, m_samplesPerSymbol, RootRaisedCosine::Energy); + m_pulseShapeQ.create(m_settings.m_rollOff, 8, m_samplesPerSymbol, RootRaisedCosine::Energy); } void DATVModSource::applySettings(const DATVModSettings& settings, bool force) @@ -901,8 +901,8 @@ void DATVModSource::applySettings(const DATVModSettings& settings, bool force) if (m_settings.m_symbolRate > 0) m_samplesPerSymbol = m_channelSampleRate/m_settings.m_symbolRate; - m_pulseShapeI.create(m_settings.m_rollOff, 8, m_samplesPerSymbol, false); - m_pulseShapeQ.create(m_settings.m_rollOff, 8, m_samplesPerSymbol, false); + m_pulseShapeI.create(m_settings.m_rollOff, 8, m_samplesPerSymbol, RootRaisedCosine::Energy); + m_pulseShapeQ.create(m_settings.m_rollOff, 8, m_samplesPerSymbol, RootRaisedCosine::Energy); checkBitrates(); } diff --git a/plugins/feature/map/mapsettings.cpp b/plugins/feature/map/mapsettings.cpp index 7630a67c0..14d8a5fe2 100644 --- a/plugins/feature/map/mapsettings.cpp +++ b/plugins/feature/map/mapsettings.cpp @@ -38,6 +38,7 @@ const QStringList MapSettings::m_pipeTypes = { QStringLiteral("FT8Demod"), QStringLiteral("HeatMap"), QStringLiteral("ILSDemod"), + QStringLiteral("InmarsatDemod"), QStringLiteral("PagerDemod"), QStringLiteral("Radiosonde"), QStringLiteral("StarTracker"), @@ -56,6 +57,7 @@ const QStringList MapSettings::m_pipeURIs = { QStringLiteral("sdrangel.channel.ft8demod"), QStringLiteral("sdrangel.channel.heatmap"), QStringLiteral("sdrangel.channel.ilsdemod"), + QStringLiteral("sdrangel.channel.inmarsatdemod"), QStringLiteral("sdrangel.channel.pagerdemod"), QStringLiteral("sdrangel.feature.radiosonde"), QStringLiteral("sdrangel.feature.startracker"), @@ -117,6 +119,7 @@ MapSettings::MapSettings() : m_itemSettings.insert("Navtex", new MapItemSettings("Navtex", false, QColor(255, 0, 255), false, true, 8)); m_itemSettings.insert("ILSDemod", new MapItemSettings("ILSDemod", true, QColor(0, 205, 200), true, true, 10)); + m_itemSettings.insert("InmarsatDemod", new MapItemSettings("InmarsatDemod", true, QColor(40, 180, 75), false, true, 5)); MapItemSettings *navAidSettings = new MapItemSettings("NavAid", false, QColor(255, 0, 255), false, true, 11); navAidSettings->m_filterDistance = 500000; diff --git a/plugins/feature/map/readme.md b/plugins/feature/map/readme.md index 0141362bc..773fcc6c0 100644 --- a/plugins/feature/map/readme.md +++ b/plugins/feature/map/readme.md @@ -16,6 +16,7 @@ On top of this, it can plot data from other plugins, such as: * Radials and estimated position from the VOR localizer feature, * ILS course line and glide path from the ILS Demodulator, * DSC geographic call areas, +* Inmarsat C message geographic areas, * SID paths, * Pager messages that contain coordinates. diff --git a/plugins/feature/satellitetracker/satellitetrackersettings.h b/plugins/feature/satellitetracker/satellitetrackersettings.h index 8bad64d72..7bc13ccaa 100644 --- a/plugins/feature/satellitetracker/satellitetrackersettings.h +++ b/plugins/feature/satellitetracker/satellitetrackersettings.h @@ -29,7 +29,7 @@ class Serializable; #define DEAFULT_TARGET "ISS" -#define DEFAULT_TLES {"https://db.satnogs.org/api/tle/", "https://www.amsat.org/tle/current/nasabare.txt", "https://celestrak.org/NORAD/elements/gp.php?GROUP=weather&FORMAT=tle", "https://celestrak.org/NORAD/elements/gp.php?GROUP=gps-ops&FORMAT=tle", "https://celestrak.org/NORAD/elements/gp.php?CATNR=36395&FORMAT=tle"} +#define DEFAULT_TLES {"https://db.satnogs.org/api/tle/", "https://www.amsat.org/tle/current/nasabare.txt", "https://celestrak.org/NORAD/elements/gp.php?GROUP=weather&FORMAT=tle", "https://celestrak.org/NORAD/elements/gp.php?GROUP=gps-ops&FORMAT=tle", "https://celestrak.org/NORAD/elements/gp.php?CATNR=36395&FORMAT=tle", "https://celestrak.org/NORAD/elements/gp.php?CATNR=25153&FORMAT=tle"} #define DEFAULT_DATE_FORMAT "yyyy/MM/dd" #define DEFAULT_AOS_SPEECH "${name} is visible for ${duration} minutes. Max elevation, ${elevation} degrees." #define DEFAULT_LOS_SPEECH "${name} is no longer visible." diff --git a/sdrbase/dsp/costasloop.h b/sdrbase/dsp/costasloop.h index f641e8d4d..f8371c4c6 100644 --- a/sdrbase/dsp/costasloop.h +++ b/sdrbase/dsp/costasloop.h @@ -38,6 +38,8 @@ public: void setPskOrder(unsigned int pskOrder) { m_pskOrder = pskOrder; } void reset(); void setSampleRate(unsigned int sampleRate); + void setMaxFreq(float freq) { m_maxFreq = freq; } + void setMinFreq(float freq) { m_minFreq = freq; } void feed(float re, float im); const std::complex& getComplex() const { return m_y; } float getReal() const { return m_y.real(); } @@ -81,16 +83,6 @@ private: m_freq = m_minFreq; } - void setMaxFreq(float freq) - { - m_maxFreq = freq; - } - - void setMinFreq(float freq) - { - m_minFreq = freq; - } - float phaseDetector2(std::complex sample) const // for BPSK { return (sample.real() * sample.imag()); diff --git a/sdrbase/dsp/rootraisedcosine.h b/sdrbase/dsp/rootraisedcosine.h index 776545640..a5c5d6b64 100644 --- a/sdrbase/dsp/rootraisedcosine.h +++ b/sdrbase/dsp/rootraisedcosine.h @@ -26,25 +26,29 @@ // Root-raised-cosine low-pass filter for pulse shaping, without intersymbol interference (ISI) // https://en.wikipedia.org/wiki/Root-raised-cosine_filter -// This could be optimised in to a polyphase filter, as samplesPerSymbol-1 inputs +// This could be optimised in to a polyphase filter for TX, as samplesPerSymbol-1 inputs // to filter() should be zero, as the data is upsampled to the sample rate template class RootRaisedCosine { public: RootRaisedCosine() : m_ptr(0) { } + enum Normalise { + Energy, // impulse response convolved with itself will be a raised cosine with value of 1 at the peak + Amplitude, // bipolar sequence (E.g. [1 0 0 -1 0 0..]) has maximum output values close to (1,-1) for TX + Gain // 0dB gain - coefficients sum to 1 + }; + // beta - roll-off factor // symbolSpan - number of symbols over which the filter is spread // samplesPerSymbol - number of samples per symbol - // normaliseUpsampledAmplitude - when true, scale the filter such that an upsampled - // (by samplesPerSymbol) bipolar sequence (E.g. [1 0 0 -1 0 0..]) has maximum - // output values close to (1,-1) - void create(double beta, int symbolSpan, int samplesPerSymbol, bool normaliseUpsampledAmplitude = false) + // normalise - how to normalise the filter + void create(double beta, int symbolSpan, int samplesPerSymbol, Normalise normalise) { int nTaps = symbolSpan * samplesPerSymbol + 1; int i, j; // check constraints - if(!(nTaps & 1)) { + if (!(nTaps & 1)) { qDebug("Root raised cosine filter has to have an odd number of taps"); nTaps++; } @@ -57,7 +61,7 @@ public: m_taps.resize(nTaps / 2 + 1); // calculate filter taps - for(i = 0; i < nTaps / 2 + 1; i++) + for (i = 0; i < nTaps / 2 + 1; i++) { double t = (i - (nTaps / 2)) / (double)samplesPerSymbol; double Ts = 1.0; @@ -75,18 +79,19 @@ public: } // normalize - if (!normaliseUpsampledAmplitude) + if (normalise == Normalise::Energy) { // normalize energy double sum = 0; - for(i = 0; i < (int)m_taps.size() - 1; i++) + for (i = 0; i < (int)m_taps.size() - 1; i++) sum += std::pow(m_taps[i], 2.0) * 2; sum += std::pow(m_taps[i], 2.0); sum = std::sqrt(sum); - for(i = 0; i < (int)m_taps.size(); i++) + for (i = 0; i < (int)m_taps.size(); i++) m_taps[i] /= sum; + } - else + else if (normalise == Normalise::Amplitude) { // Calculate maximum output of filter, assuming upsampled bipolar input E.g. [1 0 0 -1 0 0..] // This doesn't necessarily include the centre tap, so we try each offset @@ -102,9 +107,24 @@ public: maxGain = g; } // Scale up so maximum out is 1 - for(i = 0; i < (int)m_taps.size(); i++) + for (i = 0; i < (int)m_taps.size(); i++) m_taps[i] /= maxGain; } + else if (normalise == Gain) + { + // Normalise to 0dB gain + double sum = 0; + for (i = 0; i < (int)m_taps.size() - 1; i++) + sum += m_taps[i] * 2; + sum += m_taps[i]; + for (i = 0; i < (int)m_taps.size(); i++) + m_taps[i] /= sum; + } + /*printf("rrc=["); + for (int i = 0; i < m_taps.size(); i++) { + printf("%f ", m_taps[i]); + } + printf("];\n");*/ } Type filter(Type sample) diff --git a/sdrbase/resources/webapi/doc/html2/index.html b/sdrbase/resources/webapi/doc/html2/index.html index e995e1a89..56d6b80e5 100644 --- a/sdrbase/resources/webapi/doc/html2/index.html +++ b/sdrbase/resources/webapi/doc/html2/index.html @@ -3988,6 +3988,9 @@ margin-bottom: 20px; "ILSDemodReport" : { "$ref" : "#/definitions/ILSDemodReport" }, + "InmarsatDemodReport" : { + "$ref" : "#/definitions/InmarsatDemodReport" + }, "M17DemodReport" : { "$ref" : "#/definitions/M17DemodReport" }, @@ -4176,6 +4179,9 @@ margin-bottom: 20px; "ILSDemodSettings" : { "$ref" : "#/definitions/ILSDemodSettings" }, + "InmarsatDemodSettings" : { + "$ref" : "#/definitions/InmarsatDemodSettings" + }, "InterferometerSettings" : { "$ref" : "#/definitions/InterferometerSettings" }, @@ -8564,6 +8570,100 @@ margin-bottom: 20px; } }, "description" : "ILSDemod" +}; + defs.InmarsatDemodReport = { + "properties" : { + "channelPowerDB" : { + "type" : "number", + "format" : "float", + "description" : "power received in channel (dB)" + }, + "channelSampleRate" : { + "type" : "integer" + } + }, + "description" : "InmarsatDemod" +}; + defs.InmarsatDemodSettings = { + "properties" : { + "inputFrequencyOffset" : { + "type" : "integer", + "format" : "int64" + }, + "rfBandwidth" : { + "type" : "number", + "format" : "float" + }, + "rrcRolloff" : { + "type" : "number", + "format" : "float" + }, + "pllBandwidth" : { + "type" : "number", + "format" : "float" + }, + "udpEnabled" : { + "type" : "integer", + "description" : "Whether to forward received messages to specified UDP port" + }, + "udpAddress" : { + "type" : "string", + "description" : "UDP address to forward received messages to" + }, + "udpPort" : { + "type" : "integer", + "description" : "UDP port to forward received messages to" + }, + "udpFormat" : { + "type" : "integer", + "description" : "0 for binary, 1 for NMEA" + }, + "logFilename" : { + "type" : "string" + }, + "logEnabled" : { + "type" : "integer" + }, + "useFileTime" : { + "type" : "integer" + }, + "rgbColor" : { + "type" : "integer" + }, + "title" : { + "type" : "string" + }, + "streamIndex" : { + "type" : "integer", + "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, + "scopeConfig" : { + "$ref" : "#/definitions/GLScope" + }, + "channelMarker" : { + "$ref" : "#/definitions/ChannelMarker" + }, + "rollupState" : { + "$ref" : "#/definitions/RollupState" + } + }, + "description" : "InmarsatDemod" }; defs.InstanceChannelsResponse = { "required" : [ "channelcount" ], diff --git a/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml b/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml index f2eeb924e..e59ad2003 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/ChannelReport.yaml @@ -63,6 +63,8 @@ ChannelReport: $ref: "/doc/swagger/include/HeatMap.yaml#/HeatMapReport" ILSDemodReport: $ref: "/doc/swagger/include/ILSDemod.yaml#/ILSDemodReport" + InmarsatDemodReport: + $ref: "/doc/swagger/include/InmarsatDemod.yaml#/InmarsatDemodReport" M17DemodReport: $ref: "/doc/swagger/include/M17Demod.yaml#/M17DemodReport" M17ModReport: diff --git a/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml b/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml index 47bee152a..a7718035a 100644 --- a/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml +++ b/sdrbase/resources/webapi/doc/swagger/include/ChannelSettings.yaml @@ -77,6 +77,8 @@ ChannelSettings: $ref: "/doc/swagger/include/HeatMap.yaml#/HeatMapSettings" ILSDemodSettings: $ref: "/doc/swagger/include/ILSDemod.yaml#/ILSDemodSettings" + InmarsatDemodSettings: + $ref: "/doc/swagger/include/InmarsatDemod.yaml#/InmarsatDemodSettings" InterferometerSettings: $ref: "/doc/swagger/include/Interferometer.yaml#/InterferometerSettings" IEEE_802_15_4_ModSettings: diff --git a/sdrbase/resources/webapi/doc/swagger/include/InmarsatDemod.yaml b/sdrbase/resources/webapi/doc/swagger/include/InmarsatDemod.yaml new file mode 100644 index 000000000..cb6b971f5 --- /dev/null +++ b/sdrbase/resources/webapi/doc/swagger/include/InmarsatDemod.yaml @@ -0,0 +1,67 @@ +InmarsatDemodSettings: + description: InmarsatDemod + properties: + inputFrequencyOffset: + type: integer + format: int64 + rfBandwidth: + type: number + format: float + rrcRolloff: + type: number + format: float + pllBandwidth: + type: number + format: float + udpEnabled: + description: "Whether to forward received messages to specified UDP port" + type: integer + udpAddress: + description: "UDP address to forward received messages to" + type: string + udpPort: + description: "UDP port to forward received messages to" + type: integer + udpFormat: + description: "0 for binary, 1 for NMEA" + type: integer + logFilename: + type: string + logEnabled: + type: integer + useFileTime: + type: integer + rgbColor: + type: integer + title: + type: string + streamIndex: + description: MIMO channel. Not relevant when connected to SI (single Rx). + type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer + scopeConfig: + $ref: "/doc/swagger/include/GLScope.yaml#/GLScope" + channelMarker: + $ref: "/doc/swagger/include/ChannelMarker.yaml#/ChannelMarker" + rollupState: + $ref: "/doc/swagger/include/RollupState.yaml#/RollupState" + +InmarsatDemodReport: + description: InmarsatDemod + properties: + channelPowerDB: + description: power received in channel (dB) + type: number + format: float + channelSampleRate: + type: integer diff --git a/sdrbase/webapi/webapirequestmapper.cpp b/sdrbase/webapi/webapirequestmapper.cpp index bd501385a..edc59c566 100644 --- a/sdrbase/webapi/webapirequestmapper.cpp +++ b/sdrbase/webapi/webapirequestmapper.cpp @@ -4595,6 +4595,12 @@ bool WebAPIRequestMapper::getChannelSettings( channelSettings->getIlsDemodSettings()->init(); channelSettings->getIlsDemodSettings()->fromJsonObject(settingsJsonObject); } + else if (channelSettingsKey == "InmarsatDemodSettings") + { + channelSettings->setInmarsatDemodSettings(new SWGSDRangel::SWGInmarsatDemodSettings()); + channelSettings->getInmarsatDemodSettings()->init(); + channelSettings->getInmarsatDemodSettings()->fromJsonObject(settingsJsonObject); + } else if (channelSettingsKey == "InterferometerSettings") { channelSettings->setInterferometerSettings(new SWGSDRangel::SWGInterferometerSettings()); @@ -5565,6 +5571,7 @@ void WebAPIRequestMapper::resetChannelSettings(SWGSDRangel::SWGChannelSettings& channelSettings.setHeatMapSettings(nullptr); channelSettings.setIeee802154ModSettings(nullptr); channelSettings.setIlsDemodSettings(nullptr); + channelSettings.setInmarsatDemodSettings(nullptr); channelSettings.setNavtexDemodSettings(nullptr); channelSettings.setNfmDemodSettings(nullptr); channelSettings.setNfmModSettings(nullptr); @@ -5610,6 +5617,7 @@ void WebAPIRequestMapper::resetChannelReport(SWGSDRangel::SWGChannelReport& chan channelReport.setFreqTrackerReport(nullptr); channelReport.setHeatMapReport(nullptr); channelReport.setIlsDemodReport(nullptr); + channelReport.setInmarsatDemodReport(nullptr); channelReport.setNavtexDemodReport(nullptr); channelReport.setNfmDemodReport(nullptr); channelReport.setNfmModReport(nullptr); diff --git a/sdrbase/webapi/webapiutils.cpp b/sdrbase/webapi/webapiutils.cpp index 5babcc316..920828454 100644 --- a/sdrbase/webapi/webapiutils.cpp +++ b/sdrbase/webapi/webapiutils.cpp @@ -53,6 +53,7 @@ const QMap WebAPIUtils::m_channelURIToSettingsKey = { {"sdrangel.channel.freqtracker", "FreqTrackerSettings"}, {"sdrangel.channel.heatmap", "HeatMapSettings"}, {"sdrangel.channel.ilsdemod", "ILSDemodSettings"}, + {"sdrangel.channel.inmarsatdemod", "InmarsatDemodSettings"}, {"sdrangel.channel.navtexemod", "NavtexDemodSettings"}, {"sdrangel.channel.m17demod", "M17DemodSettings"}, {"sdrangel.channeltx.modm17", "M17ModSettings"}, diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 511076aeb..3e83b944e 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -150,7 +150,7 @@ parts: override-pull: | snapcraftctl pull snapcraftctl set-version "$(git describe --tags --abbrev=0 | sed 's/v//')" - after: [apt, libdab, mbelib, serialdv, dsdcc, codec2, sgp4, cm265cc, libsigmf, airspy, rtlsdr, pluto, bladerf, hackrf, limesuite, airspyhf, uhd, uhdfpga, soapysdr, soapyremote] + after: [apt, libdab, mbelib, serialdv, dsdcc, codec2, sgp4, inmarsatc, cm265cc, libsigmf, airspy, rtlsdr, pluto, bladerf, hackrf, limesuite, airspyhf, uhd, uhdfpga, soapysdr, soapyremote] cmake-parameters: - -DDEBUG_OUTPUT=OFF - -DBUILD_TYPE=RELEASE @@ -175,6 +175,7 @@ parts: - -DMBE_DIR=$SNAPCRAFT_STAGE/opt/install/sdrangel - -DCODEC2_DIR=$SNAPCRAFT_STAGE/opt/install/sdrangel - -DSGP4_DIR=$SNAPCRAFT_STAGE/opt/install/sdrangel + - -DINMARSATC_DIR=$SNAPCRAFT_STAGE/opt/install/sdrangel - -DLIBSIGMF_DIR=$SNAPCRAFT_STAGE/opt/install/sdrangel - -DDAB_DIR=$SNAPCRAFT_STAGE/opt/install/sdrangel - -DARCH_OPT=nehalem @@ -475,6 +476,14 @@ parts: cmake-parameters: - -DCMAKE_INSTALL_PREFIX=/opt/install/sdrangel + inmarsatc: + plugin: cmake + source: https://github.com/srcejon/inmarsatc.git + source-type: git + source-tag: msvc + cmake-parameters: + - -DCMAKE_INSTALL_PREFIX=/opt/install/sdrangel + cm265cc: plugin: cmake source: https://github.com/f4exb/cm256cc.git diff --git a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml index 2c5d79550..5afdbafbc 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelReport.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelReport.yaml @@ -63,6 +63,8 @@ ChannelReport: $ref: "http://swgserver:8081/api/swagger/include/HeatMap.yaml#/HeatMapReport" ILSDemodReport: $ref: "http://swgserver:8081/api/swagger/include/ILSDemod.yaml#/ILSDemodReport" + InmarsatDemodReport: + $ref: "http://swgserver:8081/api/swagger/include/InmarsatDemod.yaml#/InmarsatDemodReport" M17DemodReport: $ref: "http://swgserver:8081/api/swagger/include/M17Demod.yaml#/M17DemodReport" M17ModReport: diff --git a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml index acbd7569f..7d35c9fe1 100644 --- a/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml +++ b/swagger/sdrangel/api/swagger/include/ChannelSettings.yaml @@ -77,6 +77,8 @@ ChannelSettings: $ref: "http://swgserver:8081/api/swagger/include/HeatMap.yaml#/HeatMapSettings" ILSDemodSettings: $ref: "http://swgserver:8081/api/swagger/include/ILSDemod.yaml#/ILSDemodSettings" + InmarsatDemodSettings: + $ref: "http://swgserver:8081/api/swagger/include/InmarsatDemod.yaml#/InmarsatDemodSettings" InterferometerSettings: $ref: "http://swgserver:8081/api/swagger/include/Interferometer.yaml#/InterferometerSettings" IEEE_802_15_4_ModSettings: diff --git a/swagger/sdrangel/api/swagger/include/InmarsatDemod.yaml b/swagger/sdrangel/api/swagger/include/InmarsatDemod.yaml new file mode 100644 index 000000000..52cc5cc2f --- /dev/null +++ b/swagger/sdrangel/api/swagger/include/InmarsatDemod.yaml @@ -0,0 +1,67 @@ +InmarsatDemodSettings: + description: InmarsatDemod + properties: + inputFrequencyOffset: + type: integer + format: int64 + rfBandwidth: + type: number + format: float + rrcRolloff: + type: number + format: float + pllBandwidth: + type: number + format: float + udpEnabled: + description: "Whether to forward received messages to specified UDP port" + type: integer + udpAddress: + description: "UDP address to forward received messages to" + type: string + udpPort: + description: "UDP port to forward received messages to" + type: integer + udpFormat: + description: "0 for binary, 1 for NMEA" + type: integer + logFilename: + type: string + logEnabled: + type: integer + useFileTime: + type: integer + rgbColor: + type: integer + title: + type: string + streamIndex: + description: MIMO channel. Not relevant when connected to SI (single Rx). + type: integer + useReverseAPI: + description: Synchronize with reverse API (1 for yes, 0 for no) + type: integer + reverseAPIAddress: + type: string + reverseAPIPort: + type: integer + reverseAPIDeviceIndex: + type: integer + reverseAPIChannelIndex: + type: integer + scopeConfig: + $ref: "http://swgserver:8081/api/swagger/include/GLScope.yaml#/GLScope" + channelMarker: + $ref: "http://swgserver:8081/api/swagger/include/ChannelMarker.yaml#/ChannelMarker" + rollupState: + $ref: "http://swgserver:8081/api/swagger/include/RollupState.yaml#/RollupState" + +InmarsatDemodReport: + description: InmarsatDemod + properties: + channelPowerDB: + description: power received in channel (dB) + type: number + format: float + channelSampleRate: + type: integer diff --git a/swagger/sdrangel/code/html2/index.html b/swagger/sdrangel/code/html2/index.html index e995e1a89..56d6b80e5 100644 --- a/swagger/sdrangel/code/html2/index.html +++ b/swagger/sdrangel/code/html2/index.html @@ -3988,6 +3988,9 @@ margin-bottom: 20px; "ILSDemodReport" : { "$ref" : "#/definitions/ILSDemodReport" }, + "InmarsatDemodReport" : { + "$ref" : "#/definitions/InmarsatDemodReport" + }, "M17DemodReport" : { "$ref" : "#/definitions/M17DemodReport" }, @@ -4176,6 +4179,9 @@ margin-bottom: 20px; "ILSDemodSettings" : { "$ref" : "#/definitions/ILSDemodSettings" }, + "InmarsatDemodSettings" : { + "$ref" : "#/definitions/InmarsatDemodSettings" + }, "InterferometerSettings" : { "$ref" : "#/definitions/InterferometerSettings" }, @@ -8564,6 +8570,100 @@ margin-bottom: 20px; } }, "description" : "ILSDemod" +}; + defs.InmarsatDemodReport = { + "properties" : { + "channelPowerDB" : { + "type" : "number", + "format" : "float", + "description" : "power received in channel (dB)" + }, + "channelSampleRate" : { + "type" : "integer" + } + }, + "description" : "InmarsatDemod" +}; + defs.InmarsatDemodSettings = { + "properties" : { + "inputFrequencyOffset" : { + "type" : "integer", + "format" : "int64" + }, + "rfBandwidth" : { + "type" : "number", + "format" : "float" + }, + "rrcRolloff" : { + "type" : "number", + "format" : "float" + }, + "pllBandwidth" : { + "type" : "number", + "format" : "float" + }, + "udpEnabled" : { + "type" : "integer", + "description" : "Whether to forward received messages to specified UDP port" + }, + "udpAddress" : { + "type" : "string", + "description" : "UDP address to forward received messages to" + }, + "udpPort" : { + "type" : "integer", + "description" : "UDP port to forward received messages to" + }, + "udpFormat" : { + "type" : "integer", + "description" : "0 for binary, 1 for NMEA" + }, + "logFilename" : { + "type" : "string" + }, + "logEnabled" : { + "type" : "integer" + }, + "useFileTime" : { + "type" : "integer" + }, + "rgbColor" : { + "type" : "integer" + }, + "title" : { + "type" : "string" + }, + "streamIndex" : { + "type" : "integer", + "description" : "MIMO channel. Not relevant when connected to SI (single Rx)." + }, + "useReverseAPI" : { + "type" : "integer", + "description" : "Synchronize with reverse API (1 for yes, 0 for no)" + }, + "reverseAPIAddress" : { + "type" : "string" + }, + "reverseAPIPort" : { + "type" : "integer" + }, + "reverseAPIDeviceIndex" : { + "type" : "integer" + }, + "reverseAPIChannelIndex" : { + "type" : "integer" + }, + "scopeConfig" : { + "$ref" : "#/definitions/GLScope" + }, + "channelMarker" : { + "$ref" : "#/definitions/ChannelMarker" + }, + "rollupState" : { + "$ref" : "#/definitions/RollupState" + } + }, + "description" : "InmarsatDemod" }; defs.InstanceChannelsResponse = { "required" : [ "channelcount" ], diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp index f7e5e304a..46d9f65f7 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.cpp @@ -84,6 +84,8 @@ SWGChannelReport::SWGChannelReport() { m_heat_map_report_isSet = false; ils_demod_report = nullptr; m_ils_demod_report_isSet = false; + inmarsat_demod_report = nullptr; + m_inmarsat_demod_report_isSet = false; m17_demod_report = nullptr; m_m17_demod_report_isSet = false; m17_mod_report = nullptr; @@ -198,6 +200,8 @@ SWGChannelReport::init() { m_heat_map_report_isSet = false; ils_demod_report = new SWGILSDemodReport(); m_ils_demod_report_isSet = false; + inmarsat_demod_report = new SWGInmarsatDemodReport(); + m_inmarsat_demod_report_isSet = false; m17_demod_report = new SWGM17DemodReport(); m_m17_demod_report_isSet = false; m17_mod_report = new SWGM17ModReport(); @@ -334,6 +338,9 @@ SWGChannelReport::cleanup() { if(ils_demod_report != nullptr) { delete ils_demod_report; } + if(inmarsat_demod_report != nullptr) { + delete inmarsat_demod_report; + } if(m17_demod_report != nullptr) { delete m17_demod_report; } @@ -478,6 +485,8 @@ SWGChannelReport::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&ils_demod_report, pJson["ILSDemodReport"], "SWGILSDemodReport", "SWGILSDemodReport"); + ::SWGSDRangel::setValue(&inmarsat_demod_report, pJson["InmarsatDemodReport"], "SWGInmarsatDemodReport", "SWGInmarsatDemodReport"); + ::SWGSDRangel::setValue(&m17_demod_report, pJson["M17DemodReport"], "SWGM17DemodReport", "SWGM17DemodReport"); ::SWGSDRangel::setValue(&m17_mod_report, pJson["M17ModReport"], "SWGM17ModReport", "SWGM17ModReport"); @@ -628,6 +637,9 @@ SWGChannelReport::asJsonObject() { if((ils_demod_report != nullptr) && (ils_demod_report->isSet())){ toJsonValue(QString("ILSDemodReport"), ils_demod_report, obj, QString("SWGILSDemodReport")); } + if((inmarsat_demod_report != nullptr) && (inmarsat_demod_report->isSet())){ + toJsonValue(QString("InmarsatDemodReport"), inmarsat_demod_report, obj, QString("SWGInmarsatDemodReport")); + } if((m17_demod_report != nullptr) && (m17_demod_report->isSet())){ toJsonValue(QString("M17DemodReport"), m17_demod_report, obj, QString("SWGM17DemodReport")); } @@ -987,6 +999,16 @@ SWGChannelReport::setIlsDemodReport(SWGILSDemodReport* ils_demod_report) { this->m_ils_demod_report_isSet = true; } +SWGInmarsatDemodReport* +SWGChannelReport::getInmarsatDemodReport() { + return inmarsat_demod_report; +} +void +SWGChannelReport::setInmarsatDemodReport(SWGInmarsatDemodReport* inmarsat_demod_report) { + this->inmarsat_demod_report = inmarsat_demod_report; + this->m_inmarsat_demod_report_isSet = true; +} + SWGM17DemodReport* SWGChannelReport::getM17DemodReport() { return m17_demod_report; @@ -1326,6 +1348,9 @@ SWGChannelReport::isSet(){ if(ils_demod_report && ils_demod_report->isSet()){ isObjectUpdated = true; break; } + if(inmarsat_demod_report && inmarsat_demod_report->isSet()){ + isObjectUpdated = true; break; + } if(m17_demod_report && m17_demod_report->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h index 9db426965..e8008d4ce 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelReport.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelReport.h @@ -48,6 +48,7 @@ #include "SWGHeatMapReport.h" #include "SWGIEEE_802_15_4_ModReport.h" #include "SWGILSDemodReport.h" +#include "SWGInmarsatDemodReport.h" #include "SWGM17DemodReport.h" #include "SWGM17ModReport.h" #include "SWGNFMDemodReport.h" @@ -177,6 +178,9 @@ public: SWGILSDemodReport* getIlsDemodReport(); void setIlsDemodReport(SWGILSDemodReport* ils_demod_report); + SWGInmarsatDemodReport* getInmarsatDemodReport(); + void setInmarsatDemodReport(SWGInmarsatDemodReport* inmarsat_demod_report); + SWGM17DemodReport* getM17DemodReport(); void setM17DemodReport(SWGM17DemodReport* m17_demod_report); @@ -340,6 +344,9 @@ private: SWGILSDemodReport* ils_demod_report; bool m_ils_demod_report_isSet; + SWGInmarsatDemodReport* inmarsat_demod_report; + bool m_inmarsat_demod_report_isSet; + SWGM17DemodReport* m17_demod_report; bool m_m17_demod_report_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp index a0a78938a..98fb7a081 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.cpp @@ -96,6 +96,8 @@ SWGChannelSettings::SWGChannelSettings() { m_heat_map_settings_isSet = false; ils_demod_settings = nullptr; m_ils_demod_settings_isSet = false; + inmarsat_demod_settings = nullptr; + m_inmarsat_demod_settings_isSet = false; interferometer_settings = nullptr; m_interferometer_settings_isSet = false; ieee_802_15_4_mod_settings = nullptr; @@ -234,6 +236,8 @@ SWGChannelSettings::init() { m_heat_map_settings_isSet = false; ils_demod_settings = new SWGILSDemodSettings(); m_ils_demod_settings_isSet = false; + inmarsat_demod_settings = new SWGInmarsatDemodSettings(); + m_inmarsat_demod_settings_isSet = false; interferometer_settings = new SWGInterferometerSettings(); m_interferometer_settings_isSet = false; ieee_802_15_4_mod_settings = new SWGIEEE_802_15_4_ModSettings(); @@ -396,6 +400,9 @@ SWGChannelSettings::cleanup() { if(ils_demod_settings != nullptr) { delete ils_demod_settings; } + if(inmarsat_demod_settings != nullptr) { + delete inmarsat_demod_settings; + } if(interferometer_settings != nullptr) { delete interferometer_settings; } @@ -570,6 +577,8 @@ SWGChannelSettings::fromJsonObject(QJsonObject &pJson) { ::SWGSDRangel::setValue(&ils_demod_settings, pJson["ILSDemodSettings"], "SWGILSDemodSettings", "SWGILSDemodSettings"); + ::SWGSDRangel::setValue(&inmarsat_demod_settings, pJson["InmarsatDemodSettings"], "SWGInmarsatDemodSettings", "SWGInmarsatDemodSettings"); + ::SWGSDRangel::setValue(&interferometer_settings, pJson["InterferometerSettings"], "SWGInterferometerSettings", "SWGInterferometerSettings"); ::SWGSDRangel::setValue(&ieee_802_15_4_mod_settings, pJson["IEEE_802_15_4_ModSettings"], "SWGIEEE_802_15_4_ModSettings", "SWGIEEE_802_15_4_ModSettings"); @@ -750,6 +759,9 @@ SWGChannelSettings::asJsonObject() { if((ils_demod_settings != nullptr) && (ils_demod_settings->isSet())){ toJsonValue(QString("ILSDemodSettings"), ils_demod_settings, obj, QString("SWGILSDemodSettings")); } + if((inmarsat_demod_settings != nullptr) && (inmarsat_demod_settings->isSet())){ + toJsonValue(QString("InmarsatDemodSettings"), inmarsat_demod_settings, obj, QString("SWGInmarsatDemodSettings")); + } if((interferometer_settings != nullptr) && (interferometer_settings->isSet())){ toJsonValue(QString("InterferometerSettings"), interferometer_settings, obj, QString("SWGInterferometerSettings")); } @@ -1187,6 +1199,16 @@ SWGChannelSettings::setIlsDemodSettings(SWGILSDemodSettings* ils_demod_settings) this->m_ils_demod_settings_isSet = true; } +SWGInmarsatDemodSettings* +SWGChannelSettings::getInmarsatDemodSettings() { + return inmarsat_demod_settings; +} +void +SWGChannelSettings::setInmarsatDemodSettings(SWGInmarsatDemodSettings* inmarsat_demod_settings) { + this->inmarsat_demod_settings = inmarsat_demod_settings; + this->m_inmarsat_demod_settings_isSet = true; +} + SWGInterferometerSettings* SWGChannelSettings::getInterferometerSettings() { return interferometer_settings; @@ -1604,6 +1626,9 @@ SWGChannelSettings::isSet(){ if(ils_demod_settings && ils_demod_settings->isSet()){ isObjectUpdated = true; break; } + if(inmarsat_demod_settings && inmarsat_demod_settings->isSet()){ + isObjectUpdated = true; break; + } if(interferometer_settings && interferometer_settings->isSet()){ isObjectUpdated = true; break; } diff --git a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h index 5baf78001..635a839a7 100644 --- a/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h +++ b/swagger/sdrangel/code/qt5/client/SWGChannelSettings.h @@ -53,6 +53,7 @@ #include "SWGHeatMapSettings.h" #include "SWGIEEE_802_15_4_ModSettings.h" #include "SWGILSDemodSettings.h" +#include "SWGInmarsatDemodSettings.h" #include "SWGInterferometerSettings.h" #include "SWGLocalSinkSettings.h" #include "SWGLocalSourceSettings.h" @@ -205,6 +206,9 @@ public: SWGILSDemodSettings* getIlsDemodSettings(); void setIlsDemodSettings(SWGILSDemodSettings* ils_demod_settings); + SWGInmarsatDemodSettings* getInmarsatDemodSettings(); + void setInmarsatDemodSettings(SWGInmarsatDemodSettings* inmarsat_demod_settings); + SWGInterferometerSettings* getInterferometerSettings(); void setInterferometerSettings(SWGInterferometerSettings* interferometer_settings); @@ -404,6 +408,9 @@ private: SWGILSDemodSettings* ils_demod_settings; bool m_ils_demod_settings_isSet; + SWGInmarsatDemodSettings* inmarsat_demod_settings; + bool m_inmarsat_demod_settings_isSet; + SWGInterferometerSettings* interferometer_settings; bool m_interferometer_settings_isSet; diff --git a/swagger/sdrangel/code/qt5/client/SWGInmarsatDemodReport.cpp b/swagger/sdrangel/code/qt5/client/SWGInmarsatDemodReport.cpp new file mode 100644 index 000000000..487478c2c --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGInmarsatDemodReport.cpp @@ -0,0 +1,131 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGInmarsatDemodReport.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGInmarsatDemodReport::SWGInmarsatDemodReport(QString* json) { + init(); + this->fromJson(*json); +} + +SWGInmarsatDemodReport::SWGInmarsatDemodReport() { + channel_power_db = 0.0f; + m_channel_power_db_isSet = false; + channel_sample_rate = 0; + m_channel_sample_rate_isSet = false; +} + +SWGInmarsatDemodReport::~SWGInmarsatDemodReport() { + this->cleanup(); +} + +void +SWGInmarsatDemodReport::init() { + channel_power_db = 0.0f; + m_channel_power_db_isSet = false; + channel_sample_rate = 0; + m_channel_sample_rate_isSet = false; +} + +void +SWGInmarsatDemodReport::cleanup() { + + +} + +SWGInmarsatDemodReport* +SWGInmarsatDemodReport::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGInmarsatDemodReport::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&channel_power_db, pJson["channelPowerDB"], "float", ""); + + ::SWGSDRangel::setValue(&channel_sample_rate, pJson["channelSampleRate"], "qint32", ""); + +} + +QString +SWGInmarsatDemodReport::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGInmarsatDemodReport::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_channel_power_db_isSet){ + obj->insert("channelPowerDB", QJsonValue(channel_power_db)); + } + if(m_channel_sample_rate_isSet){ + obj->insert("channelSampleRate", QJsonValue(channel_sample_rate)); + } + + return obj; +} + +float +SWGInmarsatDemodReport::getChannelPowerDb() { + return channel_power_db; +} +void +SWGInmarsatDemodReport::setChannelPowerDb(float channel_power_db) { + this->channel_power_db = channel_power_db; + this->m_channel_power_db_isSet = true; +} + +qint32 +SWGInmarsatDemodReport::getChannelSampleRate() { + return channel_sample_rate; +} +void +SWGInmarsatDemodReport::setChannelSampleRate(qint32 channel_sample_rate) { + this->channel_sample_rate = channel_sample_rate; + this->m_channel_sample_rate_isSet = true; +} + + +bool +SWGInmarsatDemodReport::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_channel_power_db_isSet){ + isObjectUpdated = true; break; + } + if(m_channel_sample_rate_isSet){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGInmarsatDemodReport.h b/swagger/sdrangel/code/qt5/client/SWGInmarsatDemodReport.h new file mode 100644 index 000000000..ae340d2e4 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGInmarsatDemodReport.h @@ -0,0 +1,64 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGInmarsatDemodReport.h + * + * InmarsatDemod + */ + +#ifndef SWGInmarsatDemodReport_H_ +#define SWGInmarsatDemodReport_H_ + +#include + + + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGInmarsatDemodReport: public SWGObject { +public: + SWGInmarsatDemodReport(); + SWGInmarsatDemodReport(QString* json); + virtual ~SWGInmarsatDemodReport(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGInmarsatDemodReport* fromJson(QString &jsonString) override; + + float getChannelPowerDb(); + void setChannelPowerDb(float channel_power_db); + + qint32 getChannelSampleRate(); + void setChannelSampleRate(qint32 channel_sample_rate); + + + virtual bool isSet() override; + +private: + float channel_power_db; + bool m_channel_power_db_isSet; + + qint32 channel_sample_rate; + bool m_channel_sample_rate_isSet; + +}; + +} + +#endif /* SWGInmarsatDemodReport_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGInmarsatDemodSettings.cpp b/swagger/sdrangel/code/qt5/client/SWGInmarsatDemodSettings.cpp new file mode 100644 index 000000000..755cb9f09 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGInmarsatDemodSettings.cpp @@ -0,0 +1,605 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + + +#include "SWGInmarsatDemodSettings.h" + +#include "SWGHelpers.h" + +#include +#include +#include +#include + +namespace SWGSDRangel { + +SWGInmarsatDemodSettings::SWGInmarsatDemodSettings(QString* json) { + init(); + this->fromJson(*json); +} + +SWGInmarsatDemodSettings::SWGInmarsatDemodSettings() { + input_frequency_offset = 0L; + m_input_frequency_offset_isSet = false; + rf_bandwidth = 0.0f; + m_rf_bandwidth_isSet = false; + rrc_rolloff = 0.0f; + m_rrc_rolloff_isSet = false; + pll_bandwidth = 0.0f; + m_pll_bandwidth_isSet = false; + udp_enabled = 0; + m_udp_enabled_isSet = false; + udp_address = nullptr; + m_udp_address_isSet = false; + udp_port = 0; + m_udp_port_isSet = false; + udp_format = 0; + m_udp_format_isSet = false; + log_filename = nullptr; + m_log_filename_isSet = false; + log_enabled = 0; + m_log_enabled_isSet = false; + use_file_time = 0; + m_use_file_time_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + title = nullptr; + m_title_isSet = false; + stream_index = 0; + m_stream_index_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = nullptr; + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; + scope_config = nullptr; + m_scope_config_isSet = false; + channel_marker = nullptr; + m_channel_marker_isSet = false; + rollup_state = nullptr; + m_rollup_state_isSet = false; +} + +SWGInmarsatDemodSettings::~SWGInmarsatDemodSettings() { + this->cleanup(); +} + +void +SWGInmarsatDemodSettings::init() { + input_frequency_offset = 0L; + m_input_frequency_offset_isSet = false; + rf_bandwidth = 0.0f; + m_rf_bandwidth_isSet = false; + rrc_rolloff = 0.0f; + m_rrc_rolloff_isSet = false; + pll_bandwidth = 0.0f; + m_pll_bandwidth_isSet = false; + udp_enabled = 0; + m_udp_enabled_isSet = false; + udp_address = new QString(""); + m_udp_address_isSet = false; + udp_port = 0; + m_udp_port_isSet = false; + udp_format = 0; + m_udp_format_isSet = false; + log_filename = new QString(""); + m_log_filename_isSet = false; + log_enabled = 0; + m_log_enabled_isSet = false; + use_file_time = 0; + m_use_file_time_isSet = false; + rgb_color = 0; + m_rgb_color_isSet = false; + title = new QString(""); + m_title_isSet = false; + stream_index = 0; + m_stream_index_isSet = false; + use_reverse_api = 0; + m_use_reverse_api_isSet = false; + reverse_api_address = new QString(""); + m_reverse_api_address_isSet = false; + reverse_api_port = 0; + m_reverse_api_port_isSet = false; + reverse_api_device_index = 0; + m_reverse_api_device_index_isSet = false; + reverse_api_channel_index = 0; + m_reverse_api_channel_index_isSet = false; + scope_config = new SWGGLScope(); + m_scope_config_isSet = false; + channel_marker = new SWGChannelMarker(); + m_channel_marker_isSet = false; + rollup_state = new SWGRollupState(); + m_rollup_state_isSet = false; +} + +void +SWGInmarsatDemodSettings::cleanup() { + + + + + + if(udp_address != nullptr) { + delete udp_address; + } + + + if(log_filename != nullptr) { + delete log_filename; + } + + + + if(title != nullptr) { + delete title; + } + + + if(reverse_api_address != nullptr) { + delete reverse_api_address; + } + + + + if(scope_config != nullptr) { + delete scope_config; + } + if(channel_marker != nullptr) { + delete channel_marker; + } + if(rollup_state != nullptr) { + delete rollup_state; + } +} + +SWGInmarsatDemodSettings* +SWGInmarsatDemodSettings::fromJson(QString &json) { + QByteArray array (json.toStdString().c_str()); + QJsonDocument doc = QJsonDocument::fromJson(array); + QJsonObject jsonObject = doc.object(); + this->fromJsonObject(jsonObject); + return this; +} + +void +SWGInmarsatDemodSettings::fromJsonObject(QJsonObject &pJson) { + ::SWGSDRangel::setValue(&input_frequency_offset, pJson["inputFrequencyOffset"], "qint64", ""); + + ::SWGSDRangel::setValue(&rf_bandwidth, pJson["rfBandwidth"], "float", ""); + + ::SWGSDRangel::setValue(&rrc_rolloff, pJson["rrcRolloff"], "float", ""); + + ::SWGSDRangel::setValue(&pll_bandwidth, pJson["pllBandwidth"], "float", ""); + + ::SWGSDRangel::setValue(&udp_enabled, pJson["udpEnabled"], "qint32", ""); + + ::SWGSDRangel::setValue(&udp_address, pJson["udpAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&udp_port, pJson["udpPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&udp_format, pJson["udpFormat"], "qint32", ""); + + ::SWGSDRangel::setValue(&log_filename, pJson["logFilename"], "QString", "QString"); + + ::SWGSDRangel::setValue(&log_enabled, pJson["logEnabled"], "qint32", ""); + + ::SWGSDRangel::setValue(&use_file_time, pJson["useFileTime"], "qint32", ""); + + ::SWGSDRangel::setValue(&rgb_color, pJson["rgbColor"], "qint32", ""); + + ::SWGSDRangel::setValue(&title, pJson["title"], "QString", "QString"); + + ::SWGSDRangel::setValue(&stream_index, pJson["streamIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&use_reverse_api, pJson["useReverseAPI"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_address, pJson["reverseAPIAddress"], "QString", "QString"); + + ::SWGSDRangel::setValue(&reverse_api_port, pJson["reverseAPIPort"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_device_index, pJson["reverseAPIDeviceIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&reverse_api_channel_index, pJson["reverseAPIChannelIndex"], "qint32", ""); + + ::SWGSDRangel::setValue(&scope_config, pJson["scopeConfig"], "SWGGLScope", "SWGGLScope"); + + ::SWGSDRangel::setValue(&channel_marker, pJson["channelMarker"], "SWGChannelMarker", "SWGChannelMarker"); + + ::SWGSDRangel::setValue(&rollup_state, pJson["rollupState"], "SWGRollupState", "SWGRollupState"); + +} + +QString +SWGInmarsatDemodSettings::asJson () +{ + QJsonObject* obj = this->asJsonObject(); + + QJsonDocument doc(*obj); + QByteArray bytes = doc.toJson(); + delete obj; + return QString(bytes); +} + +QJsonObject* +SWGInmarsatDemodSettings::asJsonObject() { + QJsonObject* obj = new QJsonObject(); + if(m_input_frequency_offset_isSet){ + obj->insert("inputFrequencyOffset", QJsonValue(input_frequency_offset)); + } + if(m_rf_bandwidth_isSet){ + obj->insert("rfBandwidth", QJsonValue(rf_bandwidth)); + } + if(m_rrc_rolloff_isSet){ + obj->insert("rrcRolloff", QJsonValue(rrc_rolloff)); + } + if(m_pll_bandwidth_isSet){ + obj->insert("pllBandwidth", QJsonValue(pll_bandwidth)); + } + if(m_udp_enabled_isSet){ + obj->insert("udpEnabled", QJsonValue(udp_enabled)); + } + if(udp_address != nullptr && *udp_address != QString("")){ + toJsonValue(QString("udpAddress"), udp_address, obj, QString("QString")); + } + if(m_udp_port_isSet){ + obj->insert("udpPort", QJsonValue(udp_port)); + } + if(m_udp_format_isSet){ + obj->insert("udpFormat", QJsonValue(udp_format)); + } + if(log_filename != nullptr && *log_filename != QString("")){ + toJsonValue(QString("logFilename"), log_filename, obj, QString("QString")); + } + if(m_log_enabled_isSet){ + obj->insert("logEnabled", QJsonValue(log_enabled)); + } + if(m_use_file_time_isSet){ + obj->insert("useFileTime", QJsonValue(use_file_time)); + } + if(m_rgb_color_isSet){ + obj->insert("rgbColor", QJsonValue(rgb_color)); + } + if(title != nullptr && *title != QString("")){ + toJsonValue(QString("title"), title, obj, QString("QString")); + } + if(m_stream_index_isSet){ + obj->insert("streamIndex", QJsonValue(stream_index)); + } + if(m_use_reverse_api_isSet){ + obj->insert("useReverseAPI", QJsonValue(use_reverse_api)); + } + if(reverse_api_address != nullptr && *reverse_api_address != QString("")){ + toJsonValue(QString("reverseAPIAddress"), reverse_api_address, obj, QString("QString")); + } + if(m_reverse_api_port_isSet){ + obj->insert("reverseAPIPort", QJsonValue(reverse_api_port)); + } + if(m_reverse_api_device_index_isSet){ + obj->insert("reverseAPIDeviceIndex", QJsonValue(reverse_api_device_index)); + } + if(m_reverse_api_channel_index_isSet){ + obj->insert("reverseAPIChannelIndex", QJsonValue(reverse_api_channel_index)); + } + if((scope_config != nullptr) && (scope_config->isSet())){ + toJsonValue(QString("scopeConfig"), scope_config, obj, QString("SWGGLScope")); + } + if((channel_marker != nullptr) && (channel_marker->isSet())){ + toJsonValue(QString("channelMarker"), channel_marker, obj, QString("SWGChannelMarker")); + } + if((rollup_state != nullptr) && (rollup_state->isSet())){ + toJsonValue(QString("rollupState"), rollup_state, obj, QString("SWGRollupState")); + } + + return obj; +} + +qint64 +SWGInmarsatDemodSettings::getInputFrequencyOffset() { + return input_frequency_offset; +} +void +SWGInmarsatDemodSettings::setInputFrequencyOffset(qint64 input_frequency_offset) { + this->input_frequency_offset = input_frequency_offset; + this->m_input_frequency_offset_isSet = true; +} + +float +SWGInmarsatDemodSettings::getRfBandwidth() { + return rf_bandwidth; +} +void +SWGInmarsatDemodSettings::setRfBandwidth(float rf_bandwidth) { + this->rf_bandwidth = rf_bandwidth; + this->m_rf_bandwidth_isSet = true; +} + +float +SWGInmarsatDemodSettings::getRrcRolloff() { + return rrc_rolloff; +} +void +SWGInmarsatDemodSettings::setRrcRolloff(float rrc_rolloff) { + this->rrc_rolloff = rrc_rolloff; + this->m_rrc_rolloff_isSet = true; +} + +float +SWGInmarsatDemodSettings::getPllBandwidth() { + return pll_bandwidth; +} +void +SWGInmarsatDemodSettings::setPllBandwidth(float pll_bandwidth) { + this->pll_bandwidth = pll_bandwidth; + this->m_pll_bandwidth_isSet = true; +} + +qint32 +SWGInmarsatDemodSettings::getUdpEnabled() { + return udp_enabled; +} +void +SWGInmarsatDemodSettings::setUdpEnabled(qint32 udp_enabled) { + this->udp_enabled = udp_enabled; + this->m_udp_enabled_isSet = true; +} + +QString* +SWGInmarsatDemodSettings::getUdpAddress() { + return udp_address; +} +void +SWGInmarsatDemodSettings::setUdpAddress(QString* udp_address) { + this->udp_address = udp_address; + this->m_udp_address_isSet = true; +} + +qint32 +SWGInmarsatDemodSettings::getUdpPort() { + return udp_port; +} +void +SWGInmarsatDemodSettings::setUdpPort(qint32 udp_port) { + this->udp_port = udp_port; + this->m_udp_port_isSet = true; +} + +qint32 +SWGInmarsatDemodSettings::getUdpFormat() { + return udp_format; +} +void +SWGInmarsatDemodSettings::setUdpFormat(qint32 udp_format) { + this->udp_format = udp_format; + this->m_udp_format_isSet = true; +} + +QString* +SWGInmarsatDemodSettings::getLogFilename() { + return log_filename; +} +void +SWGInmarsatDemodSettings::setLogFilename(QString* log_filename) { + this->log_filename = log_filename; + this->m_log_filename_isSet = true; +} + +qint32 +SWGInmarsatDemodSettings::getLogEnabled() { + return log_enabled; +} +void +SWGInmarsatDemodSettings::setLogEnabled(qint32 log_enabled) { + this->log_enabled = log_enabled; + this->m_log_enabled_isSet = true; +} + +qint32 +SWGInmarsatDemodSettings::getUseFileTime() { + return use_file_time; +} +void +SWGInmarsatDemodSettings::setUseFileTime(qint32 use_file_time) { + this->use_file_time = use_file_time; + this->m_use_file_time_isSet = true; +} + +qint32 +SWGInmarsatDemodSettings::getRgbColor() { + return rgb_color; +} +void +SWGInmarsatDemodSettings::setRgbColor(qint32 rgb_color) { + this->rgb_color = rgb_color; + this->m_rgb_color_isSet = true; +} + +QString* +SWGInmarsatDemodSettings::getTitle() { + return title; +} +void +SWGInmarsatDemodSettings::setTitle(QString* title) { + this->title = title; + this->m_title_isSet = true; +} + +qint32 +SWGInmarsatDemodSettings::getStreamIndex() { + return stream_index; +} +void +SWGInmarsatDemodSettings::setStreamIndex(qint32 stream_index) { + this->stream_index = stream_index; + this->m_stream_index_isSet = true; +} + +qint32 +SWGInmarsatDemodSettings::getUseReverseApi() { + return use_reverse_api; +} +void +SWGInmarsatDemodSettings::setUseReverseApi(qint32 use_reverse_api) { + this->use_reverse_api = use_reverse_api; + this->m_use_reverse_api_isSet = true; +} + +QString* +SWGInmarsatDemodSettings::getReverseApiAddress() { + return reverse_api_address; +} +void +SWGInmarsatDemodSettings::setReverseApiAddress(QString* reverse_api_address) { + this->reverse_api_address = reverse_api_address; + this->m_reverse_api_address_isSet = true; +} + +qint32 +SWGInmarsatDemodSettings::getReverseApiPort() { + return reverse_api_port; +} +void +SWGInmarsatDemodSettings::setReverseApiPort(qint32 reverse_api_port) { + this->reverse_api_port = reverse_api_port; + this->m_reverse_api_port_isSet = true; +} + +qint32 +SWGInmarsatDemodSettings::getReverseApiDeviceIndex() { + return reverse_api_device_index; +} +void +SWGInmarsatDemodSettings::setReverseApiDeviceIndex(qint32 reverse_api_device_index) { + this->reverse_api_device_index = reverse_api_device_index; + this->m_reverse_api_device_index_isSet = true; +} + +qint32 +SWGInmarsatDemodSettings::getReverseApiChannelIndex() { + return reverse_api_channel_index; +} +void +SWGInmarsatDemodSettings::setReverseApiChannelIndex(qint32 reverse_api_channel_index) { + this->reverse_api_channel_index = reverse_api_channel_index; + this->m_reverse_api_channel_index_isSet = true; +} + +SWGGLScope* +SWGInmarsatDemodSettings::getScopeConfig() { + return scope_config; +} +void +SWGInmarsatDemodSettings::setScopeConfig(SWGGLScope* scope_config) { + this->scope_config = scope_config; + this->m_scope_config_isSet = true; +} + +SWGChannelMarker* +SWGInmarsatDemodSettings::getChannelMarker() { + return channel_marker; +} +void +SWGInmarsatDemodSettings::setChannelMarker(SWGChannelMarker* channel_marker) { + this->channel_marker = channel_marker; + this->m_channel_marker_isSet = true; +} + +SWGRollupState* +SWGInmarsatDemodSettings::getRollupState() { + return rollup_state; +} +void +SWGInmarsatDemodSettings::setRollupState(SWGRollupState* rollup_state) { + this->rollup_state = rollup_state; + this->m_rollup_state_isSet = true; +} + + +bool +SWGInmarsatDemodSettings::isSet(){ + bool isObjectUpdated = false; + do{ + if(m_input_frequency_offset_isSet){ + isObjectUpdated = true; break; + } + if(m_rf_bandwidth_isSet){ + isObjectUpdated = true; break; + } + if(m_rrc_rolloff_isSet){ + isObjectUpdated = true; break; + } + if(m_pll_bandwidth_isSet){ + isObjectUpdated = true; break; + } + if(m_udp_enabled_isSet){ + isObjectUpdated = true; break; + } + if(udp_address && *udp_address != QString("")){ + isObjectUpdated = true; break; + } + if(m_udp_port_isSet){ + isObjectUpdated = true; break; + } + if(m_udp_format_isSet){ + isObjectUpdated = true; break; + } + if(log_filename && *log_filename != QString("")){ + isObjectUpdated = true; break; + } + if(m_log_enabled_isSet){ + isObjectUpdated = true; break; + } + if(m_use_file_time_isSet){ + isObjectUpdated = true; break; + } + if(m_rgb_color_isSet){ + isObjectUpdated = true; break; + } + if(title && *title != QString("")){ + isObjectUpdated = true; break; + } + if(m_stream_index_isSet){ + isObjectUpdated = true; break; + } + if(m_use_reverse_api_isSet){ + isObjectUpdated = true; break; + } + if(reverse_api_address && *reverse_api_address != QString("")){ + isObjectUpdated = true; break; + } + if(m_reverse_api_port_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_device_index_isSet){ + isObjectUpdated = true; break; + } + if(m_reverse_api_channel_index_isSet){ + isObjectUpdated = true; break; + } + if(scope_config && scope_config->isSet()){ + isObjectUpdated = true; break; + } + if(channel_marker && channel_marker->isSet()){ + isObjectUpdated = true; break; + } + if(rollup_state && rollup_state->isSet()){ + isObjectUpdated = true; break; + } + }while(false); + return isObjectUpdated; +} +} + diff --git a/swagger/sdrangel/code/qt5/client/SWGInmarsatDemodSettings.h b/swagger/sdrangel/code/qt5/client/SWGInmarsatDemodSettings.h new file mode 100644 index 000000000..be2c90cc6 --- /dev/null +++ b/swagger/sdrangel/code/qt5/client/SWGInmarsatDemodSettings.h @@ -0,0 +1,188 @@ +/** + * SDRangel + * This is the web REST/JSON API of SDRangel SDR software. SDRangel is an Open Source Qt5/OpenGL 3.0+ (4.3+ in Windows) GUI and server Software Defined Radio and signal analyzer in software. It supports Airspy, BladeRF, HackRF, LimeSDR, PlutoSDR, RTL-SDR, SDRplay RSP1 and FunCube --- Limitations and specifcities: * In SDRangel GUI the first Rx device set cannot be deleted. Conversely the server starts with no device sets and its number of device sets can be reduced to zero by as many calls as necessary to /sdrangel/deviceset with DELETE method. * Preset import and export from/to file is a server only feature. * Device set focus is a GUI only feature. * The following channels are not implemented (status 501 is returned): ATV and DATV demodulators, Channel Analyzer NG, LoRa demodulator * The device settings and report structures contains only the sub-structure corresponding to the device type. The DeviceSettings and DeviceReport structures documented here shows all of them but only one will be or should be present at a time * The channel settings and report structures contains only the sub-structure corresponding to the channel type. The ChannelSettings and ChannelReport structures documented here shows all of them but only one will be or should be present at a time --- + * + * OpenAPI spec version: 7.0.0 + * Contact: f4exb06@gmail.com + * + * NOTE: This class is auto generated by the swagger code generator program. + * https://github.com/swagger-api/swagger-codegen.git + * Do not edit the class manually. + */ + +/* + * SWGInmarsatDemodSettings.h + * + * InmarsatDemod + */ + +#ifndef SWGInmarsatDemodSettings_H_ +#define SWGInmarsatDemodSettings_H_ + +#include + + +#include "SWGChannelMarker.h" +#include "SWGGLScope.h" +#include "SWGRollupState.h" +#include + +#include "SWGObject.h" +#include "export.h" + +namespace SWGSDRangel { + +class SWG_API SWGInmarsatDemodSettings: public SWGObject { +public: + SWGInmarsatDemodSettings(); + SWGInmarsatDemodSettings(QString* json); + virtual ~SWGInmarsatDemodSettings(); + void init(); + void cleanup(); + + virtual QString asJson () override; + virtual QJsonObject* asJsonObject() override; + virtual void fromJsonObject(QJsonObject &json) override; + virtual SWGInmarsatDemodSettings* fromJson(QString &jsonString) override; + + qint64 getInputFrequencyOffset(); + void setInputFrequencyOffset(qint64 input_frequency_offset); + + float getRfBandwidth(); + void setRfBandwidth(float rf_bandwidth); + + float getRrcRolloff(); + void setRrcRolloff(float rrc_rolloff); + + float getPllBandwidth(); + void setPllBandwidth(float pll_bandwidth); + + qint32 getUdpEnabled(); + void setUdpEnabled(qint32 udp_enabled); + + QString* getUdpAddress(); + void setUdpAddress(QString* udp_address); + + qint32 getUdpPort(); + void setUdpPort(qint32 udp_port); + + qint32 getUdpFormat(); + void setUdpFormat(qint32 udp_format); + + QString* getLogFilename(); + void setLogFilename(QString* log_filename); + + qint32 getLogEnabled(); + void setLogEnabled(qint32 log_enabled); + + qint32 getUseFileTime(); + void setUseFileTime(qint32 use_file_time); + + qint32 getRgbColor(); + void setRgbColor(qint32 rgb_color); + + QString* getTitle(); + void setTitle(QString* title); + + qint32 getStreamIndex(); + void setStreamIndex(qint32 stream_index); + + qint32 getUseReverseApi(); + void setUseReverseApi(qint32 use_reverse_api); + + QString* getReverseApiAddress(); + void setReverseApiAddress(QString* reverse_api_address); + + qint32 getReverseApiPort(); + void setReverseApiPort(qint32 reverse_api_port); + + qint32 getReverseApiDeviceIndex(); + void setReverseApiDeviceIndex(qint32 reverse_api_device_index); + + qint32 getReverseApiChannelIndex(); + void setReverseApiChannelIndex(qint32 reverse_api_channel_index); + + SWGGLScope* getScopeConfig(); + void setScopeConfig(SWGGLScope* scope_config); + + SWGChannelMarker* getChannelMarker(); + void setChannelMarker(SWGChannelMarker* channel_marker); + + SWGRollupState* getRollupState(); + void setRollupState(SWGRollupState* rollup_state); + + + virtual bool isSet() override; + +private: + qint64 input_frequency_offset; + bool m_input_frequency_offset_isSet; + + float rf_bandwidth; + bool m_rf_bandwidth_isSet; + + float rrc_rolloff; + bool m_rrc_rolloff_isSet; + + float pll_bandwidth; + bool m_pll_bandwidth_isSet; + + qint32 udp_enabled; + bool m_udp_enabled_isSet; + + QString* udp_address; + bool m_udp_address_isSet; + + qint32 udp_port; + bool m_udp_port_isSet; + + qint32 udp_format; + bool m_udp_format_isSet; + + QString* log_filename; + bool m_log_filename_isSet; + + qint32 log_enabled; + bool m_log_enabled_isSet; + + qint32 use_file_time; + bool m_use_file_time_isSet; + + qint32 rgb_color; + bool m_rgb_color_isSet; + + QString* title; + bool m_title_isSet; + + qint32 stream_index; + bool m_stream_index_isSet; + + qint32 use_reverse_api; + bool m_use_reverse_api_isSet; + + QString* reverse_api_address; + bool m_reverse_api_address_isSet; + + qint32 reverse_api_port; + bool m_reverse_api_port_isSet; + + qint32 reverse_api_device_index; + bool m_reverse_api_device_index_isSet; + + qint32 reverse_api_channel_index; + bool m_reverse_api_channel_index_isSet; + + SWGGLScope* scope_config; + bool m_scope_config_isSet; + + SWGChannelMarker* channel_marker; + bool m_channel_marker_isSet; + + SWGRollupState* rollup_state; + bool m_rollup_state_isSet; + +}; + +} + +#endif /* SWGInmarsatDemodSettings_H_ */ diff --git a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h index e12058e8d..97b85c5b5 100644 --- a/swagger/sdrangel/code/qt5/client/SWGModelFactory.h +++ b/swagger/sdrangel/code/qt5/client/SWGModelFactory.h @@ -184,6 +184,8 @@ #include "SWGIEEE_802_15_4_ModSettings.h" #include "SWGILSDemodReport.h" #include "SWGILSDemodSettings.h" +#include "SWGInmarsatDemodReport.h" +#include "SWGInmarsatDemodSettings.h" #include "SWGInstanceChannelsResponse.h" #include "SWGInstanceConfigResponse.h" #include "SWGInstanceDevicesResponse.h" @@ -1244,6 +1246,16 @@ namespace SWGSDRangel { obj->init(); return obj; } + if(QString("SWGInmarsatDemodReport").compare(type) == 0) { + SWGInmarsatDemodReport *obj = new SWGInmarsatDemodReport(); + obj->init(); + return obj; + } + if(QString("SWGInmarsatDemodSettings").compare(type) == 0) { + SWGInmarsatDemodSettings *obj = new SWGInmarsatDemodSettings(); + obj->init(); + return obj; + } if(QString("SWGInstanceChannelsResponse").compare(type) == 0) { SWGInstanceChannelsResponse *obj = new SWGInstanceChannelsResponse(); obj->init();