Add SID (Sudden Ionospheric Disturbances) feature

This commit is contained in:
srcejon 2024-04-02 16:13:01 +01:00
parent 625513eaeb
commit 610c36004e
40 changed files with 6377 additions and 3 deletions

View File

@ -144,6 +144,7 @@ option(ENABLE_FEATURE_PERTESTER "Enable feature pertester plugin" ON)
option(ENABLE_FEATURE_GS232CONTROLLER "Enable feature gs232controller plugin" ON)
option(ENABLE_FEATURE_REMOTECONTROL "Enable feature remote control plugin" ON)
option(ENABLE_FEATURE_SKYMAP "Enable feature sky map plugin" ON)
option(ENABLE_FEATURE_SID "Enable feature sid plugin" ON)
# on windows always build external libraries
if(WIN32)

BIN
doc/img/SDI_plugin.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
doc/img/SID_plugin_xray.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@ -120,3 +120,9 @@ if (ENABLE_FEATURE_REMOTECONTROL)
else()
message(STATUS "Not building remotecontrol (ENABLE_FEATURE_REMOTECONTROL=${ENABLE_FEATURE_REMOTECONTROL})")
endif()
if (ENABLED_FEATURE_SID)
add_subdirectory(sid)
else()
message(STATUS "Not building SID (ENABLED_FEATURE_SID=${ENABLED_FEATURE_SID})")
endif()

View File

@ -0,0 +1,72 @@
project(sid)
set(sid_SOURCES
sid.cpp
sidsettings.cpp
sidplugin.cpp
sidwebapiadapter.cpp
sidworker.cpp
)
set(sid_HEADERS
sid.h
sidsettings.h
sidplugin.h
sidwebapiadapter.h
sidworker.h
)
include_directories(
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
)
if(NOT SERVER_MODE)
set(sid_SOURCES
${sid_SOURCES}
sidgui.cpp
sidgui.ui
sidsettingsdialog.cpp
sidsettingsdialog.ui
icons.qrc
)
set(sid_HEADERS
${sid_HEADERS}
sidgui.h
sidsettingsdialog.h
)
set(TARGET_NAME featuresid)
set(TARGET_LIB Qt::Widgets Qt::Charts Qt::Multimedia Qt::MultimediaWidgets)
set(TARGET_LIB_GUI "sdrgui")
set(INSTALL_FOLDER ${INSTALL_PLUGINS_DIR})
else()
set(TARGET_NAME featuresidsrv)
set(TARGET_LIB "")
set(TARGET_LIB_GUI "")
set(INSTALL_FOLDER ${INSTALL_PLUGINSSRV_DIR})
endif()
add_library(${TARGET_NAME} SHARED
${sid_SOURCES}
)
target_link_libraries(${TARGET_NAME}
Qt::Core
${TARGET_LIB}
sdrbase
${TARGET_LIB_GUI}
)
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
if(WIN32)
# Run deployqt for MultimediaWidgets, which isn't used in other plugins
include(DeployQt)
windeployqt(${TARGET_NAME} ${SDRANGEL_BINARY_BIN_DIR} "")
endif()
# Install debug symbols
if (WIN32)
install(FILES $<TARGET_PDB_FILE:${TARGET_NAME}> CONFIGURATIONS Debug RelWithDebInfo DESTINATION ${INSTALL_FOLDER} )
endif()

View File

@ -0,0 +1,16 @@
<RCC>
<qresource prefix="/sid/">
<file>icons/sun.png</file>
<file>icons/chartcombined.png</file>
<file>icons/chartseparate.png</file>
<file>icons/legend.png</file>
<file>icons/xlp.svg</file>
<file>icons/xls.svg</file>
<file>icons/xsp.svg</file>
<file>icons/xss.svg</file>
<file>icons/delta.svg</file>
<file>icons/gamma.svg</file>
<file>icons/proton.svg</file>
<file>icons/solar-orbiter.svg</file>
</qresource>
</RCC>

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 15.1 13" style="enable-background:new 0 0 15.1 13;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<path id="MJX-66-TEX-N-394" class="st0" d="M0.9-13c-0.1,0-0.1,0.1-0.1,0.1c0,0,1,2.1,3.1,6.3S7-0.2,7-0.1C7.1,0,7.2,0,7.5,0
C7.8,0,8,0,8-0.1c0,0,1.1-2.2,3.1-6.4s3.1-6.3,3.1-6.3c0,0,0-0.1-0.1-0.1H0.9z M9.2-6.7L7-2.2l-4.5-9.1l4.5,0h4.5
C11.4-11.3,10.7-9.8,9.2-6.7z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 779 B

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 11.7 13.8" style="enable-background:new 0 0 11.7 13.8;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<path id="MJX-56-TEX-I-1D6FE" class="st0" d="M1.3-4.4c-0.2,0-0.4,0.1-0.4,0.2C0.9-4,1-3.7,1.2-3.4s0.4,0.7,0.7,1.1
s0.7,0.7,1.1,1s1,0.4,1.4,0.4c0.3,0,0.5,0,0.6,0c0.6-0.1,1.1-0.5,1.4-1s0.7-1.3,1-2.4C7.6-5,7.7-5.5,7.7-5.8c0-0.1,0-0.1,0-0.1
l0.2,0.5c0.2,0.6,0.5,1.3,0.8,1.9s0.6,1.2,0.8,1.6s0.3,0.7,0.4,0.7c0,0,0.1,0,0.3,0h0.2c0.1-0.1,0.1-0.1,0.1-0.2
c0,0-0.1-0.3-0.4-0.8c-0.2-0.3-0.6-1.1-1-2.1c-0.5-1-0.9-2.1-1.3-3.3C7.7-7.8,7.6-8.2,7.5-8.8C7.4-9.4,7.3-10,7.2-10.7
C7-11.5,6.8-12,6.7-12.3s-0.3-0.5-0.5-0.5s-0.3,0.2-0.3,0.5c0,0.6,0.3,1.9,1,4L7-8v0.3c0,0.1,0,0.3,0,0.6c0,1.9-0.3,3.2-1,3.9
C5.5-2.7,4.9-2.5,4.2-2.5c-0.5,0-1.1-0.1-1.5-0.4s-0.8-0.7-1-1.2c0-0.1,0-0.2-0.1-0.2C1.5-4.4,1.4-4.4,1.3-4.4z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 B

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 20.6 18.4" style="enable-background:new 0 0 20.6 18.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<g>
<path id="MJX-5-TEX-I-1D45D" class="st0" d="M0.4-9.7c0,0,0,0.1,0,0.1c0,0.1,0,0.2,0.1,0.4c0,0.2,0.1,0.4,0.2,0.6S0.9-8.3,1-8
c0.1,0.2,0.2,0.4,0.4,0.5s0.3,0.3,0.5,0.4C2-7,2.2-6.9,2.4-6.9c0.9,0,1.5-0.4,1.7-1.2l0.2,0.2c0.7,0.7,1.5,1,2.1,1
c0.8,0,1.4-0.3,1.8-0.9C8.8-8.4,9-9,9-9.9c0-1.3-0.5-2.5-1.4-3.6s-2-1.7-3.1-1.7c-0.3,0-0.5,0-0.7,0.1
c-0.1,0.1-0.3,0.2-0.4,0.3c-0.1,0.1-0.3,0.2-0.3,0.3l-0.1,0.1c0,0-0.1-0.5-0.4-1.6s-0.4-1.6-0.4-1.6c0-0.1,0-0.1,0.1-0.1
c0.1,0,0.3,0,0.7-0.1h0.5c0.1-0.1,0.1-0.1,0.1-0.2c0,0,0-0.1-0.1-0.3c0-0.1-0.1-0.2-0.1-0.3c0,0-0.1-0.1-0.2-0.1
c0,0-0.1,0-0.2,0s-0.3,0-0.6,0s-0.7,0-1.1,0c-0.8,0-1.4,0-1.6,0h-0.1c-0.1,0.1-0.1,0.2-0.1,0.2c0,0.3,0.1,0.5,0.2,0.6h0.4
c0.4,0,0.6,0.1,0.7,0.2c0,0.1,0.4,1.5,1.1,4.3s1.1,4.3,1.1,4.5c0,0.1,0,0.1,0,0.3c0,0.5-0.2,0.7-0.5,0.7
c-0.3,0-0.5-0.2-0.7-0.5C1.5-8.5,1.4-8.8,1.3-9.2C1.2-9.6,1.1-9.8,1.1-9.9c0,0-0.1,0-0.3,0H0.5C0.5-9.8,0.4-9.8,0.4-9.7z
M3.2-13.1c0.3-0.9,0.7-1.4,1.3-1.4c0.4,0,0.7,0.1,1,0.4c0.3,0.3,0.6,0.6,0.8,1c0.2,0.4,0.4,1.1,0.7,2s0.3,1.6,0.3,2v0.1
c0,0.9-0.4,1.3-1.1,1.3c-0.1,0-0.3,0-0.4-0.1c-0.1,0-0.3-0.1-0.4-0.2C5.4-7.9,5.3-8,5.2-8.1C5.1-8.1,5-8.2,4.9-8.3
C4.8-8.4,4.7-8.5,4.6-8.6C4.5-8.7,4.4-8.8,4.4-8.9S4.3-9,4.3-9L4.2-9.1c0,0,0-0.1-0.1-0.3c0-0.2-0.1-0.4-0.2-0.8
c-0.1-0.4-0.2-0.7-0.3-0.9C3.4-12.4,3.2-13,3.2-13.1z"/>
</g>
</g>
<g transform="translate(536,413) scale(0.707)">
<g>
<path id="MJX-5-TEX-N-2B" class="st0" d="M-743.4-590.4c0,0,0,0.1,0,0.2c0,0.2,0.1,0.3,0.3,0.4h5.4v2.7l0,2.7
c0.1,0.2,0.2,0.2,0.3,0.2c0.2,0,0.3-0.1,0.4-0.3v-5.4h5.4c0.2-0.1,0.3-0.2,0.3-0.4s-0.1-0.3-0.3-0.4h-5.4v-5.4
c-0.1-0.2-0.2-0.3-0.3-0.3h0h0c-0.1,0-0.3,0.1-0.3,0.3v5.4h-5.4c-0.2,0.1-0.3,0.2-0.3,0.4V-590.4z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 89.49 83.83">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #fff;
stroke-miterlimit: 10;
stroke-width: 3px;
}
.cls-2 {
fill: #fff;
stroke-width: 0px;
}
</style>
</defs>
<circle class="cls-1" cx="35.5" cy="48.81" r="22.2"/>
<circle class="cls-2" cx="69.18" cy="16.79" r="6.33"/>
<rect class="cls-2" x="48.53" y="4.39" width="16.03" height="4.81" transform="translate(15.91 -33.1) rotate(37.62)"/>
<rect class="cls-2" x="73.67" y="24.72" width="16.03" height="4.81" transform="translate(33.54 -44.22) rotate(37.62)"/>
<rect class="cls-2" x="52.72" y="69.63" width="9.94" height="2.08" rx="1.04" ry="1.04" transform="translate(63.37 -20.38) rotate(42.8)"/>
<rect class="cls-2" x="10.15" y="25.57" width="9.94" height="2.08" rx="1.04" ry="1.04" transform="translate(22.11 -3.19) rotate(42.8)"/>
<rect class="cls-2" x="8.33" y="68.52" width="9.94" height="2.08" rx="1.04" ry="1.04" transform="translate(-45.29 29.78) rotate(-45)"/>
<rect class="cls-2" x="52.72" y="27.33" width="9.94" height="2.08" rx="1.04" ry="1.04" transform="translate(-3.17 49.11) rotate(-45)"/>
<rect class="cls-2" x="0" y="47.77" width="9.94" height="2.08" rx="1.04" ry="1.04"/>
<rect class="cls-2" x="60.18" y="47.77" width="9.94" height="2.08" rx="1.04" ry="1.04"/>
<rect class="cls-2" x="32.21" y="17.72" width="9.94" height="2.08" rx="1.04" ry="1.04" transform="translate(18.42 55.94) rotate(-90)"/>
<rect class="cls-2" x="31.17" y="77.82" width="9.94" height="2.08" rx="1.04" ry="1.04" transform="translate(-42.72 115) rotate(-90)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 25.2 15.1" style="enable-background:new 0 0 25.2 15.1;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<g>
<path id="MJX-38-TEX-I-1D44B" class="st0" d="M0.8-12.4L0.8-12.4c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0.1,0.3
c0,0.2,0.1,0.3,0.1,0.3c0,0,0.2,0,0.3,0.1c1,0,1.9,0.3,2.4,0.9c0.1,0.1,0.8,0.9,2.1,2.3s1.9,2.1,1.9,2.1
c-1.3,3.4-2,5.1-2.1,5.1C5.3-0.9,4.9-0.8,4.2-0.8H3.7C3.7-0.8,3.6-0.7,3.6-0.7s0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.3
c0.4,0,1.1,0,2.2,0C6.7,0,7,0,7.4,0S8,0,8.2,0s0.3,0,0.4,0c0.2,0,0.3-0.1,0.3-0.2c0,0,0-0.1,0-0.2c0-0.2-0.1-0.3-0.1-0.3
c0,0-0.1-0.1-0.3-0.1C8-0.9,7.7-1,7.4-1.1l1.4-3.5l1,1.1c1.3,1.4,1.9,2.1,1.9,2.3c0,0.2-0.1,0.4-0.4,0.5c-0.1,0-0.1,0-0.2,0
c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.2c0,0,0.2,0,0.5,0c0.3,0,0.6,0,1,0s0.6,0,0.7,0
c1.1,0,1.7,0,1.8,0.1h0.1c0.1-0.1,0.1-0.2,0.1-0.2c0-0.3-0.1-0.5-0.2-0.6h-0.3c-0.4,0-0.8-0.1-1.1-0.2s-0.6-0.2-0.7-0.3
c-0.2-0.1-0.3-0.2-0.4-0.3l-0.2-0.2c0,0-0.6-0.6-1.7-1.9L9.1-5.4c0,0,0.2-0.5,0.6-1.4s0.8-1.9,1.2-2.9c0.4-1,0.6-1.5,0.7-1.6
c0.1-0.1,0.5-0.2,1.1-0.2c0.4,0,0.6-0.1,0.6-0.2c0,0,0-0.1,0-0.3c0-0.2-0.1-0.3-0.1-0.3s-0.1-0.1-0.3-0.1c0,0-0.2,0-0.6,0
s-0.9,0-1.6,0c-0.7,0-1.3,0-1.7,0s-0.6,0-0.6,0c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.1,0,0.2,0.1,0.3s0.1,0.1,0.1,0.1
s0.1,0,0.2,0c0.1,0,0.2,0,0.3,0c0.1,0,0.3,0,0.5,0.1c0.2,0.1,0.3,0.2,0.3,0.2c0,0-0.3,0.7-0.9,2.1L7.8-7
c-2.3-2.5-3.4-3.8-3.5-3.9c0-0.1-0.1-0.2-0.1-0.2c0-0.2,0.2-0.4,0.5-0.5c0,0,0,0,0.1,0s0.1,0,0.1,0c0,0,0.1,0,0.1,0s0,0,0.1,0
c0,0,0,0,0.1-0.1s0-0.1,0-0.1c0-0.1,0-0.2,0-0.3c0-0.2-0.1-0.2-0.1-0.3c0,0-0.1,0-0.3-0.1c0,0-0.1,0-0.3,0s-0.4,0-0.8,0
s-0.7,0-1.1,0C1.6-12.3,1-12.3,0.8-12.4z"/>
</g>
</g>
<g transform="translate(861,-150) scale(0.707)">
<g>
<path id="MJX-38-TEX-I-1D43F" class="st0" d="M-1191.6,202.4c-0.4,0-0.6,0-0.7,0.1c0,0,0,0.1,0,0.1c0,0.3,0.1,0.5,0.2,0.6
c0,0,0.1,0,0.3,0c0.7,0,1.4-0.1,2.3-0.1c1.7,0,2.7,0,2.9,0.1h0.2c0.1-0.1,0.1-0.1,0.1-0.2c0,0,0-0.1,0-0.3
c-0.1-0.2-0.1-0.3-0.2-0.3h-0.7c-0.8,0-1.3-0.1-1.5-0.2c-0.1,0-0.2-0.2-0.3-0.5c-0.1-0.3-0.5-1.9-1.2-4.8
c-0.2-0.8-0.4-1.6-0.6-2.5c-0.2-0.9-0.4-1.5-0.5-2l-0.2-0.7c0,0,0.1-0.1,0.2-0.1c0.1,0,0.5,0,1.1,0h0.3c0.4,0,0.7,0,0.9,0
c0.2,0,0.5,0.1,0.9,0.1c0.4,0.1,0.7,0.2,0.9,0.3c0.2,0.1,0.5,0.3,0.8,0.6s0.5,0.6,0.8,1c0.2,0.4,0.4,0.8,0.5,1.2
s0.3,0.7,0.3,0.8c0.1,0,0.2,0.1,0.3,0.1h0.2c0.1-0.1,0.1-0.2,0.1-0.2s-0.1-0.4-0.4-1.1c-0.2-0.7-0.5-1.4-0.8-2.2
s-0.4-1.2-0.5-1.3c0-0.1,0-0.1-0.1-0.1s-0.1,0-0.3-0.1s-0.4,0-0.8,0c-0.1,0-0.6,0-1.4,0s-1.5,0-2.2,0h-3.2
c-1,0-1.5,0.1-1.5,0.2c0,0.1,0,0.2,0,0.3c0.1,0.2,0.1,0.4,0.2,0.4c0,0,0.1,0,0.3,0h0.1c0.3,0,0.7,0,1.1,0.1
c0.2,0,0.3,0.1,0.3,0.2c0,0,0.5,1.7,1.3,5c0.8,3.3,1.2,5.1,1.2,5.2C-1190.6,202.3-1191,202.3-1191.6,202.4z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 25.2 19.1" style="enable-background:new 0 0 25.2 19.1;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<g>
<path id="MJX-39-TEX-I-1D44B" class="st0" d="M0.8-14.6L0.8-14.6c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0.1,0.3
c0,0.2,0.1,0.3,0.1,0.3c0,0,0.2,0,0.3,0.1c1,0,1.9,0.3,2.4,0.9c0.1,0.1,0.8,0.9,2.1,2.3s1.9,2.1,1.9,2.1
c-1.3,3.4-2,5.1-2.1,5.1C5.3-3.2,4.9-3.1,4.2-3.1H3.7C3.7-3,3.6-3,3.6-3c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.3
c0.4,0,1.1,0,2.2,0c0.4,0,0.8,0,1.1,0s0.6,0,0.8,0c0.2,0,0.3,0,0.4,0c0.2,0,0.3-0.1,0.3-0.2c0,0,0-0.1,0-0.2
c0-0.2-0.1-0.3-0.1-0.3c0,0-0.1-0.1-0.3-0.1c-0.4,0-0.7-0.1-1-0.3l1.4-3.5l1,1.1c1.3,1.4,1.9,2.1,1.9,2.3
c0,0.2-0.1,0.4-0.4,0.5c-0.1,0-0.1,0-0.2,0c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.2c0,0,0.2,0,0.5,0
c0.3,0,0.6,0,1,0c0.4,0,0.6,0,0.7,0c1.1,0,1.7,0,1.8,0.1h0.1c0.1-0.1,0.1-0.2,0.1-0.2c0-0.3-0.1-0.5-0.2-0.6h-0.3
c-0.4,0-0.8-0.1-1.1-0.2c-0.3-0.1-0.6-0.2-0.7-0.3c-0.2-0.1-0.3-0.2-0.4-0.3l-0.2-0.2c0,0-0.6-0.6-1.7-1.9L9.1-7.7
c0,0,0.2-0.5,0.6-1.4c0.4-1,0.8-1.9,1.2-2.9s0.6-1.5,0.7-1.6c0.1-0.1,0.5-0.2,1.1-0.2c0.4,0,0.6-0.1,0.6-0.2c0,0,0-0.1,0-0.3
c0-0.2-0.1-0.3-0.1-0.3c0,0-0.1-0.1-0.3-0.1c0,0-0.2,0-0.6,0c-0.4,0-0.9,0-1.6,0c-0.7,0-1.3,0-1.7,0c-0.4,0-0.6,0-0.6,0
c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.1,0,0.2,0.1,0.3c0,0,0.1,0.1,0.1,0.1c0,0,0.1,0,0.2,0c0.1,0,0.2,0,0.3,0
c0.1,0,0.3,0,0.5,0.1c0.2,0.1,0.3,0.2,0.3,0.2c0,0-0.3,0.7-0.9,2.1L7.8-9.2c-2.3-2.5-3.4-3.8-3.5-3.9c0-0.1-0.1-0.2-0.1-0.2
c0-0.2,0.2-0.4,0.5-0.5c0,0,0,0,0.1,0s0.1,0,0.1,0c0,0,0.1,0,0.1,0s0,0,0.1,0c0,0,0,0,0.1-0.1c0,0,0-0.1,0-0.1
c0-0.1,0-0.2,0-0.3c0-0.2-0.1-0.2-0.1-0.3c0,0-0.1,0-0.3-0.1c0,0-0.1,0-0.3,0c-0.2,0-0.4,0-0.8,0c-0.3,0-0.7,0-1.1,0
C1.6-14.6,1-14.6,0.8-14.6z"/>
</g>
</g>
<g transform="translate(936.2,413) scale(0.707)">
<g>
<path id="MJX-39-TEX-V-2032" class="st0" d="M-1298.8-593.5c-0.1,0-0.2,0-0.5,0.1c-0.3,0.1-0.4,0.1-0.4,0.2
c0,0.1,0.3,1.5,1,4.2c0.7,2.7,1,4.1,1.1,4.3c0.2,0.4,0.5,0.6,0.9,0.6c0.2,0,0.5-0.1,0.8-0.3s0.4-0.4,0.4-0.8c0-0.1,0-0.2,0-0.3
c0-0.1-0.5-1.4-1.6-4C-1298.1-592.1-1298.7-593.5-1298.8-593.5C-1298.7-593.5-1298.8-593.5-1298.8-593.5z"/>
</g>
</g>
<g transform="translate(861,-247) scale(0.707)">
<g>
<path id="MJX-39-TEX-I-1D43F" class="st0" d="M-1191.7,333.9c-0.4,0-0.6,0-0.7,0.1c0,0,0,0.1,0,0.1c0,0.3,0.1,0.5,0.2,0.6
c0,0,0.1,0,0.3,0c0.7,0,1.4-0.1,2.3-0.1c1.7,0,2.7,0,2.9,0.1h0.2c0.1-0.1,0.1-0.1,0.1-0.2s0-0.1,0-0.3
c-0.1-0.2-0.1-0.3-0.2-0.3h-0.7c-0.8,0-1.3-0.1-1.5-0.2c-0.1,0-0.2-0.2-0.3-0.5c-0.1-0.3-0.5-1.9-1.2-4.8
c-0.2-0.8-0.4-1.6-0.6-2.5c-0.2-0.9-0.4-1.5-0.5-2l-0.2-0.7c0,0,0.1-0.1,0.2-0.1c0.1,0,0.5,0,1.1,0h0.3c0.4,0,0.7,0,0.9,0
c0.2,0,0.5,0.1,0.9,0.1s0.7,0.2,0.9,0.3c0.2,0.1,0.5,0.3,0.8,0.6s0.5,0.6,0.8,1c0.2,0.4,0.4,0.8,0.5,1.2
c0.2,0.4,0.3,0.7,0.3,0.8c0.1,0,0.2,0.1,0.3,0.1h0.2c0.1-0.1,0.1-0.2,0.1-0.2s-0.1-0.4-0.4-1.1s-0.5-1.4-0.8-2.2
s-0.4-1.2-0.5-1.3c0-0.1,0-0.1-0.1-0.1c0,0-0.1,0-0.3-0.1c-0.2,0-0.4,0-0.8,0c-0.1,0-0.6,0-1.4,0c-0.8,0-1.5,0-2.2,0h-3.2
c-1,0-1.5,0.1-1.5,0.2c0,0.1,0,0.2,0,0.3c0.1,0.2,0.1,0.4,0.2,0.4c0,0,0.1,0,0.3,0h0.1c0.3,0,0.7,0,1.1,0.1
c0.2,0,0.3,0.1,0.3,0.2c0,0,0.5,1.7,1.3,5c0.8,3.3,1.2,5,1.2,5.2C-1190.6,333.8-1191,333.8-1191.7,333.9z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24.7 15.4" style="enable-background:new 0 0 24.7 15.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<g>
<path id="MJX-40-TEX-I-1D44B" class="st0" d="M0.8-12.4L0.8-12.4c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0.1,0.3
c0,0.2,0.1,0.3,0.1,0.3c0,0,0.2,0,0.3,0.1c1,0,1.9,0.3,2.4,0.9c0.1,0.1,0.8,0.9,2.1,2.3s1.9,2.1,1.9,2.1
c-1.3,3.4-2,5.1-2.1,5.1C5.3-0.9,4.9-0.8,4.2-0.8H3.7C3.7-0.8,3.6-0.7,3.6-0.7c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.3
c0.4,0,1.1,0,2.2,0C6.7,0,7,0,7.4,0C7.7,0,8,0,8.2,0c0.2,0,0.3,0,0.4,0c0.2,0,0.3-0.1,0.3-0.2c0,0,0-0.1,0-0.2
c0-0.2-0.1-0.3-0.1-0.3S8.6-0.8,8.4-0.8C8-0.9,7.7-1,7.4-1.1l1.4-3.5l1,1.1c1.3,1.4,1.9,2.1,1.9,2.3c0,0.2-0.1,0.4-0.4,0.5
c-0.1,0-0.1,0-0.2,0c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.2c0,0,0.2,0,0.5,0c0.3,0,0.6,0,1,0
c0.4,0,0.6,0,0.7,0c1.1,0,1.7,0,1.8,0.1h0.1c0.1-0.1,0.1-0.2,0.1-0.2c0-0.3-0.1-0.5-0.2-0.6h-0.3c-0.4,0-0.8-0.1-1.1-0.2
s-0.6-0.2-0.7-0.3c-0.2-0.1-0.3-0.2-0.4-0.3l-0.2-0.2c0,0-0.6-0.6-1.7-1.9L9.1-5.4c0,0,0.2-0.5,0.6-1.4s0.8-1.9,1.2-2.9
c0.4-1,0.6-1.5,0.7-1.6c0.1-0.1,0.5-0.2,1.1-0.2c0.4,0,0.6-0.1,0.6-0.2c0,0,0-0.1,0-0.3c0-0.2-0.1-0.3-0.1-0.3
c0,0-0.1-0.1-0.3-0.1c0,0-0.2,0-0.6,0c-0.4,0-0.9,0-1.6,0c-0.7,0-1.3,0-1.7,0c-0.4,0-0.6,0-0.6,0c-0.2,0-0.3,0.1-0.3,0.2
c0,0,0,0.1,0,0.3c0,0.1,0,0.2,0.1,0.3c0,0,0.1,0.1,0.1,0.1c0,0,0.1,0,0.2,0c0.1,0,0.2,0,0.3,0c0.1,0,0.3,0,0.5,0.1
c0.2,0.1,0.3,0.2,0.3,0.2c0,0-0.3,0.7-0.9,2.1L7.8-7c-2.3-2.5-3.4-3.8-3.5-3.9c0-0.1-0.1-0.2-0.1-0.2c0-0.2,0.2-0.4,0.5-0.5
c0,0,0,0,0.1,0s0.1,0,0.1,0c0,0,0.1,0,0.1,0s0,0,0.1,0c0,0,0,0,0.1-0.1s0-0.1,0-0.1c0-0.1,0-0.2,0-0.3c0-0.2-0.1-0.2-0.1-0.3
c0,0-0.1,0-0.3-0.1c0,0-0.1,0-0.3,0c-0.2,0-0.4,0-0.8,0c-0.3,0-0.7,0-1.1,0C1.6-12.3,1-12.3,0.8-12.4z"/>
</g>
</g>
<g transform="translate(861,-150) scale(0.707)">
<g>
<path id="MJX-40-TEX-I-1D446" class="st0" d="M-1190.2,191.3c0.7,0,1.4,0.3,2,0.9c0.6,0.6,0.9,1.4,0.9,2.2
c0,0.8-0.3,1.3-0.9,1.6c-1.3,0.3-2.1,0.5-2.5,0.7c-0.4,0.1-0.6,0.2-0.8,0.4c-0.7,0.5-1.1,1.2-1.1,2.2c0,0.7,0.2,1.4,0.6,2
c0.4,0.6,0.8,1.1,1.2,1.4c0.3,0.3,0.8,0.5,1.3,0.7c0.5,0.2,0.9,0.3,1.2,0.3h0.2h0.1c1.2,0,2-0.4,2.5-1.2c0,0,0.1,0.1,0.2,0.2
s0.3,0.3,0.5,0.5s0.3,0.3,0.4,0.4c0,0,0,0,0.1,0c0,0,0.1,0,0.1,0c0.2,0,0.2,0,0.2-0.1c0-0.1-0.2-0.8-0.5-2.2
c-0.3-1.4-0.5-2.1-0.6-2.1c0,0-0.1-0.1-0.3-0.1c-0.2,0-0.4,0.1-0.4,0.2c0,0.1,0,0.1,0,0.2c0,0,0,0.2,0,0.3c0,0.2,0,0.3,0,0.5
c0,0.4-0.1,0.8-0.2,1.1c-0.1,0.3-0.2,0.5-0.3,0.7c-0.1,0.1-0.3,0.3-0.5,0.5c-0.4,0.3-0.9,0.4-1.5,0.4c-0.7,0-1.3-0.3-1.9-0.8
c-0.6-0.6-0.9-1.2-0.9-1.9c0-0.4,0.1-0.7,0.3-0.9c0.2-0.3,0.4-0.5,0.7-0.6c0,0,0.5-0.1,1.3-0.3c0.8-0.2,1.2-0.3,1.2-0.3
c0.4-0.1,0.8-0.4,1.2-0.9s0.6-1,0.6-1.8c0-0.3,0-0.7-0.1-1c-0.1-0.3-0.2-0.6-0.3-0.9c-0.2-0.3-0.4-0.7-0.7-1.1
c-0.3-0.4-0.7-0.7-1.2-1.1c-0.5-0.3-1.1-0.6-1.7-0.7c-0.1,0-0.3,0-0.6,0c-1.2,0-2.1,0.3-2.8,1l-0.2,0.2l-0.5-0.6
c-0.3-0.3-0.4-0.5-0.5-0.6s-0.1-0.1-0.2-0.1c-0.2,0-0.2,0-0.2,0.1c0,0,0.3,1.5,1,4.3c0,0.1,0.1,0.1,0.4,0.1h0.2
c0.1-0.1,0.1-0.1,0.1-0.2c0,0,0-0.1,0-0.2c0-0.1,0-0.2-0.1-0.4c0-0.2,0-0.4,0-0.6c0-0.5,0.1-0.9,0.3-1.2
c0.2-0.3,0.5-0.6,0.8-0.7c0.3-0.2,0.6-0.3,0.9-0.3C-1190.9,191.3-1190.5,191.3-1190.2,191.3z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 28.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24.7 19.4" style="enable-background:new 0 0 24.7 19.4;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FFFFFF;}
</style>
<g transform="scale(1,-1)">
<g>
<g>
<g>
<g>
<path id="MJX-41-TEX-I-1D44B" class="st0" d="M0.8-14.6L0.8-14.6c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0.1,0.3
c0,0.2,0.1,0.3,0.1,0.3s0.2,0,0.3,0.1c1,0,1.9,0.3,2.4,0.9c0.1,0.1,0.8,0.9,2.1,2.3s1.9,2.1,1.9,2.1c-1.3,3.4-2,5.1-2.1,5.1
C5.3-3.2,4.9-3.1,4.2-3.1H3.7C3.7-3,3.6-3,3.6-3c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.3c0.4,0,1.1,0,2.2,0
c0.4,0,0.8,0,1.1,0s0.6,0,0.8,0c0.2,0,0.3,0,0.4,0c0.2,0,0.3-0.1,0.3-0.2c0,0,0-0.1,0-0.2c0-0.2-0.1-0.3-0.1-0.3
S8.6-3.1,8.4-3.1c-0.4,0-0.7-0.1-1-0.3l1.4-3.5l1,1.1c1.3,1.4,1.9,2.1,1.9,2.3c0,0.2-0.1,0.4-0.4,0.5c-0.1,0-0.1,0-0.2,0
c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.2,0.1,0.3,0.2,0.3h0.2c0,0,0.2,0,0.5,0s0.6,0,1,0c0.4,0,0.6,0,0.7,0
c1.1,0,1.7,0,1.8,0.1h0.1c0.1-0.1,0.1-0.2,0.1-0.2c0-0.3-0.1-0.5-0.2-0.6h-0.3c-0.4,0-0.8-0.1-1.1-0.2
c-0.3-0.1-0.6-0.2-0.7-0.3c-0.2-0.1-0.3-0.2-0.4-0.3l-0.2-0.2c0,0-0.6-0.6-1.7-1.9L9.1-7.7c0,0,0.2-0.5,0.6-1.4
c0.4-1,0.8-1.9,1.2-2.9c0.4-1,0.6-1.5,0.7-1.6c0.1-0.1,0.5-0.2,1.1-0.2c0.4,0,0.6-0.1,0.6-0.2c0,0,0-0.1,0-0.3
c0-0.2-0.1-0.3-0.1-0.3c0,0-0.1-0.1-0.3-0.1c0,0-0.2,0-0.6,0c-0.4,0-0.9,0-1.6,0c-0.7,0-1.3,0-1.7,0s-0.6,0-0.6,0
c-0.2,0-0.3,0.1-0.3,0.2c0,0,0,0.1,0,0.3c0,0.1,0,0.2,0.1,0.3c0,0,0.1,0.1,0.1,0.1c0,0,0.1,0,0.2,0c0.1,0,0.2,0,0.3,0
s0.3,0,0.5,0.1c0.2,0.1,0.3,0.2,0.3,0.2c0,0-0.3,0.7-0.9,2.1L7.8-9.2c-2.3-2.5-3.4-3.8-3.5-3.9c0-0.1-0.1-0.2-0.1-0.2
c0-0.2,0.2-0.4,0.5-0.5c0,0,0,0,0.1,0c0.1,0,0.1,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0,0,0.1,0c0,0,0,0,0.1-0.1c0,0,0-0.1,0-0.1
c0-0.1,0-0.2,0-0.3c0-0.2-0.1-0.2-0.1-0.3c0,0-0.1,0-0.3-0.1c0,0-0.1,0-0.3,0c-0.2,0-0.4,0-0.8,0c-0.3,0-0.7,0-1.1,0
C1.6-14.6,1-14.6,0.8-14.6z"/>
</g>
</g>
<g transform="translate(936.2,413) scale(0.707)">
<g>
<path id="MJX-41-TEX-V-2032" class="st0" d="M-1298.8-593.5c-0.1,0-0.2,0-0.5,0.1c-0.3,0.1-0.4,0.1-0.4,0.2
c0,0.1,0.3,1.5,1,4.2c0.7,2.7,1,4.1,1.1,4.3c0.2,0.4,0.5,0.6,0.9,0.6c0.2,0,0.5-0.1,0.8-0.3c0.3-0.2,0.4-0.4,0.4-0.8
c0-0.1,0-0.2,0-0.3c0-0.1-0.5-1.4-1.6-4C-1298.1-592.1-1298.7-593.5-1298.8-593.5C-1298.7-593.5-1298.8-593.5-1298.8-593.5z"/>
</g>
</g>
<g transform="translate(861,-247) scale(0.707)">
<g>
<path id="MJX-41-TEX-I-1D446" class="st0" d="M-1190.2,322.8c0.7,0,1.4,0.3,2,0.9c0.6,0.6,0.9,1.4,0.9,2.2
c0,0.8-0.3,1.3-0.9,1.6c-1.3,0.3-2.1,0.5-2.5,0.7c-0.4,0.1-0.6,0.2-0.8,0.4c-0.7,0.5-1.1,1.2-1.1,2.2c0,0.7,0.2,1.4,0.6,2
c0.4,0.6,0.8,1.1,1.2,1.4c0.3,0.3,0.8,0.5,1.3,0.7c0.5,0.2,0.9,0.3,1.2,0.3h0.2h0.1c1.2,0,2-0.4,2.5-1.2c0,0,0.1,0.1,0.2,0.2
c0.1,0.2,0.3,0.3,0.5,0.5c0.2,0.2,0.3,0.3,0.4,0.4c0,0,0,0,0.1,0c0,0,0.1,0,0.1,0c0.2,0,0.2,0,0.2-0.1c0-0.1-0.2-0.8-0.5-2.2
c-0.3-1.4-0.5-2.1-0.6-2.1c0,0-0.1-0.1-0.3-0.1c-0.2,0-0.4,0.1-0.4,0.2c0,0.1,0,0.1,0,0.2c0,0,0,0.2,0,0.3s0,0.3,0,0.5
c0,0.4-0.1,0.8-0.2,1.1c-0.1,0.3-0.2,0.5-0.3,0.7c-0.1,0.1-0.3,0.3-0.5,0.5c-0.4,0.3-0.9,0.4-1.5,0.4c-0.7,0-1.3-0.3-1.9-0.8
c-0.6-0.6-0.9-1.2-0.9-1.9c0-0.4,0.1-0.7,0.3-0.9c0.2-0.3,0.4-0.5,0.7-0.6c0,0,0.5-0.1,1.3-0.3c0.8-0.2,1.2-0.3,1.2-0.3
c0.4-0.1,0.8-0.4,1.2-0.9c0.4-0.4,0.6-1,0.6-1.8c0-0.3,0-0.7-0.1-1c-0.1-0.3-0.2-0.6-0.3-0.9c-0.2-0.3-0.4-0.7-0.7-1.1
c-0.3-0.4-0.7-0.7-1.2-1.1s-1.1-0.6-1.7-0.7c-0.1,0-0.3,0-0.6,0c-1.2,0-2.1,0.3-2.8,1l-0.2,0.2l-0.5-0.6
c-0.3-0.3-0.4-0.5-0.5-0.6c-0.1,0-0.1-0.1-0.2-0.1c-0.2,0-0.2,0-0.2,0.1c0,0,0.3,1.5,1,4.3c0,0.1,0.1,0.1,0.4,0.1h0.2
c0.1-0.1,0.1-0.1,0.1-0.2c0,0,0-0.1,0-0.2c0-0.1,0-0.2-0.1-0.4c0-0.2,0-0.4,0-0.6c0-0.5,0.1-0.9,0.3-1.2
c0.2-0.3,0.5-0.6,0.8-0.7c0.3-0.2,0.6-0.3,0.9-0.3C-1190.9,322.8-1190.5,322.8-1190.2,322.8z"/>
</g>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,252 @@
<h1>SID Feature Plugin</h1>
<h2>Introduction</h2>
The SID feature displays a chart that plots channel power vs time, which can be useful for detecting solar flares, CMEs (Coronal Mass Ejections)
and GRBs (Gamma Ray Bursts) via SIDs (Sudden Ionospheric Disturbances).
The signal source for which power is measured should typically be a VLF signal from a distant transmitter with near constant day-time power output,
such as the VLF transmitters that are used by navies to communicate with submarines
(E.g. [GQD](https://en.wikipedia.org/wiki/Anthorn_Radio_Station) / [HWU](https://en.wikipedia.org/wiki/HWU_transmitter) / [NAA](https://en.wikipedia.org/wiki/VLF_Transmitter_Cutler)).
This can be measured within SDRangel using the [Channel Power]](../../channelrx/channelpower/readme.md) plugin.
When a solar flare occurs, EUV (Extreme Ultraviolet) and X-ray radiation is emitted from the Sun. When the radiation reaches the Earth's atmosphere (after ~8 minutes),
it can increase the ionization of the D and E regions in the ionosphere, enhancing VLF propagation. Gamma-rays from powerful GRBs can also have a similar effect on the ionosphere.
The enhancement of the VLF sky-wave can interfere with the ground-wave, causing constructive or destructive interference depending on the phase. If strong enough, this can be observed
in the plot of received power vs time.
The SID chart can plot multiple series, allowing different signals from different transmitters to be monitored.
This can be useful as SIDs can be localized to specific regions in the atmosphere, thus not all signals may be affected.
Data can come from multiple [Channel Power]](../../channelrx/channelpower/readme.md) plugins within a single device, or separate devices.
To help determine the cause of a SID, addtional data can be plotted from a variety of sources:
* the chart can plot X-ray data from the GOES satellites, to allow visual correlation of spikes in the X-ray flux measurement with spikes in the VLF power measurements,
* it can display images and video from the Solar Dynamics Observatory at EUV wavelengths, which may visually show the solar flare,
* it can display GRB events on the chart, measured by satellites such as Fermi and Swift,
* it can display solar flare events detected by the STIX X-ray instrument on the Solar Orbiter satellite,
* it can display proton flux measured by the GOES satellites,
* it can control the time in a 3D Map, to see the corresponding effect on MUF (Maximum Usable Frequency) and foF2 (F2 layer critical frequency).
![SID feature plugin](../../../doc/img/SID_plugin.jpg)
<h2>Interface</h2>
![SID feature plugin GUI](../../../doc/img/SID_plugin_settings.png)
<h3>Start/stop</h3>
Press to start/stop collection and plotting of data.
<h3>Open .csv</h3>
Press to open a .csv file to read data from.
<h3>Save to .csv</h3>
Press to select a .csv file to write data to.
<h3>Save chart to image</h3>
Press to save the chart to a .png or .jpg file.
<h3>Clear all data</h3>
Press to clear all data.
<h3>Average</h3>
Number of samples to use in a moving average filter that can be applied to the data. Set to 1 for no filtering.
<h3>Display Primary Long Wavelength X-Ray Data</h3>
Check to display long wavelength (0.1-0.8nm) X-Ray data from the primary GOES satellite (Currently GOES 16) on the chart.
This is probably the most useful data in order to see when a solar flare has occured, as there will typically be a sharp peak.
The GOES satellites are in a geostationary orbit around the Earth, so the measured increase in X-ray flux from a flare will be approximately 8 minutes
after it has occured.
The Y-axis indicates the flare classification. M and X class flares are those most likely to have a measurable impact on the ionosphere.
![X-Ray data showing M class flare](../../../doc/img/SID_plugin_xray.png)
<h3>Display Secondary Long Wavelength X-Ray Data</h3>
Check to display long wavelength (0.1-0.8nm) X-Ray data from the secondary GOES satellite (Currently GOES 18) on the chart.
Data from the secondary satellite may be useful when the primary is unavailable, such as when it is in eclipse.
In the following plot we can see the primary and secondary data is nearly identical, apart from where there are dropouts
while in eclipse:
![X-Ray data during eclipse](../../../doc/img/SID_plugin_eclipse.png)
<h3>Display Primary Short Wavelength X-Ray Data</h3>
Check to display short wavelength (0.1-0.8nm) X-Ray data from the primary GOES satellite (Currently GOES 16) on the chart.
<h3>Display Secondary Short Wavelength X-Ray Data</h3>
Check to display short wavelength (0.05-0.4nm) X-Ray data from the secondary GOES satellite (Currently GOES 18) on the chart.
Data from the secondary satellite may be useful when the primary is in eclipse.
<h3>Display Proton Flux</h3>
Check to display 10 MeV and 100 MeV proton flux measurements from the primary GOES satellte on the chart.
A peak in the proton flux can occur one to three days after a CME (Coronal Mass Ejection) is directed towards Earth.
Whereas X-rays from flares can impact any part of the ionosphere that are facing the sun, the Earth's magnetosphere typically directs
the particles in the CME towards the poles, so a corresponding SID is most likely to be detected if you are receiving
a signal from a transmitter crossing the polar region.
<h3>Display GRBs</h3>
Check to display Gamma Ray Bursts (GRB) on the chart. GRBs are plotted as a scatter plot. You can right click on a GRB to display the context
menu, which contains a number of links to additional data from the Fermi satellite for the GRB. The GRB data is not realtime, and it may take
up to 7 days for a GRB to appear in the data, so this is typically only useful for the analysis of historical data.
The context menu also has an item to display the location of the GRB in the [Sky Map](../../feature/skymap.readme.md) feature.
<h3>Display Solar Flares</h3>
Check to display solar flares on the chart as record by the STIX X-ray instrument on the Solar Oribter satellite.
You can right click on a solar flare to display the context menu, which contains a number of links to additional data from the STIX instrument.
The solar flare data is not realtime and can sometimes be delayed by 24 hours.
<h3>Combined or Separate Charts</h3>
When unchecked, data from [Channel Power]](../../channelrx/channelpower/readme.md) plugins is displayed on a separate chart to other data such as X-ray and proton flux and GRBs.
When checked, all data is displayed on a single combined chart.
<h3>Display Legend</h3>
Check to display a legend on the chart. When unchecked the legend will be hidden. You can click on items in the legend to temporarily hide and then show the corresponding series on the chart.
The position of the legend can be set in the Settings Dialog.
<h3>Open Settings Dialog</h3>
Click to open the Settings Dialog. The settings dialog allows a user to:
- Select which channels data is recorded from.
- What colours are used for the data series.
- Whether auto-save is enabled. When auto-save is enabled, data will be automatically saved as the specified interval.
- Whether auto-load is enabled. When auto-load is enabled, auto-save data will be automatically loaded when the SID feature is opened.
- The filename is use for auto-save.
- Where the chart legend should be positioned.
<h3>Display SDO/SOHO Imagery</h3>
When checked, displays imagary from NASA's SDO (Solar Dynamic Observatory) and ESA/NASA's SOHO (Solar and Heliospheric Observatory) satellites.
SDOs images the Sun in a variety of UV and EUV wavelengths. SOHO shows images of the solar corona. The images are near real-time, updated every 15 minutes.
Solar flares are particularly visibible in the AIA 131 Å images.
<h3>Image or Video Selection</h3>
Selects whether to display images (unchecked) or video (checked).
<h3>Image/Wavelength Selection</h3>
Selects which image / wavelength to view.
* AIA 94 Å to 1700 Å - The AIA (Atmospheric Imaging Assembly) images the solar atmosphere at multiple EUV (Extreme Ultraviolet) and UV (Ultraviolet) wavelengths:
| Band | Region |
|---------|-----------------------------------------|
| 94 Å | Flaring |
| 131 Å | Flaring |
| 171 Å | Quiet corona, upper transition region |
| 193 Å | Corona and hot flare plasma |
| 211 Å | Active corona |
| 304 Å | Chromosphere, transition region |
| 335 Å | Active corona |
| 1600 Å | Transition region, uppoer photoshere |
| 1700 Å | Temperature minimum, photosphere |
[Ref](https://sdo.gsfc.nasa.gov/data/channels.php)
* MHI Magnetogram - HMI (Helioseismic and Magnetic Imager) Magnetogram shows the magnetic field in the photosphere, with black and white indicating opposite polarities.
* MHI Intensitygram - Brightness in a visible light band (6173 Å - Red - Iron spectral line), useful for observing sun spots.
* Dopplergram - Shows velocities along the line-of-sight.
* LASCO (Large Angle Spectrometric Coronagraph) shows solar corona. C2 shows corona up to 8.4Mkm. C3 shows corona up to 23Mkm.
<h3>Show GOES 16, 18 and SDO</h3>
When checked, opens a [Satellite Tracker](../../feature/satellitetracker/readme.md) feature and sets it to display data for the GOES 16, GOES 18 and SDO satellites.
The position and tracks of the satellites will then be visible on a [Map](../../feature/map/readme.md) feature.
<h3>Autoscale X</h3>
When clicked, the chart X-axis is automatically scaled so that all power data is visible. When right-clicked, autoscaling of the X-axis will occur whenever new data is added to the chart.
<h3>Autoscale Y</h3>
When clicked, the chart Y-axis is automatically scaled so that all power data is visible. When right-clicked, autoscaling of the Y-axis will occur whenever new data is added to the chart.
<h3>Set X-axis to Today</h3>
When clicked, the X-axis is set to show today, from midnight to midnight.
When right-clicked, the X-axis is set to show sunrise to sunset. This uses latitude and longitude from Preferences > My position.
<h3>Set X-axis to -1 day</h3>
When clicked, the X-axis is set 1 day earlier than the current setting, at the same time.
<h3>Set X-axis to +1 day</h3>
When clicked, the X-axis is set 1 day later than the current setting, at the same time.
<h3>Start Time</h3>
Displays/sets the current start time of the chart (X-axis minimum). It's possible to scroll through hours/days/months by clicking on the relevent segment and using the mouse scroll wheel.
<h3>End Time</h3>
Displays/sets the current end time of the chart (X-axis maximum). It's possible to scroll through hours/days/months by clicking on the relevent segment and using the mouse scroll wheel.
<h3>Min</h3>
Displays/sets the minimum Y-axis value.
<h3>Max</h3>
Displays/sets the maximum Y-axis value.
<h3>Now</h3>
When checked, the latest SDO imagery is displayed. When unchecked, you can enter a date and time for which imagery should be displayed.
<h3>Date Time</h3>
Specifies the date and time for which SDR imagery should be displayed. Images are updated every 15 minutes. The data and time can also be set by clicking on the chart.
<h3>Map</h3>
Select a Map to link to the SID feature. When a time is selected on the SID charts, the Map feature will have it's time set accordingly.
This allows you, for example, to see the corresponding impact on MUF/foF2.
<h2>Tips</h2>
In order to check that a peak in the spectrum is a real VLF signal, you can:
* If using a magnetic loop or other directional antenna, rotate it and make sure the amplitude varies, as mag loops should have a null orthogonal to the plane of the loop.
* Check that the signal has diurnal variation (it should vary with the time of day, due to the changes in the ionosphere).
* Check with online lists of VLF signals (E.g. https://sidstation.loudet.org/stations-list-en.xhtml or https://www.mwlist.org/vlf.php). A number of these are plotted on the Map feature.
Occasionally, the X-ray flux data may drop to 0. This is typically when the GOES satellite is in eclipse (The Earth or moon is inbetween the satellite and the Sun).
SIDs are most likely to be detected when it's day time in the path between the signal source and receiver, as at night, the atmosphere is shielded from the X-rays by the Earth.
Also, as the D layer in the ionosphere essentially disappears at night, the received power is not as constant as during the day.
<h2>Codecs</h2>
On Windows, you may need to install an mp4 codec to view the SDO videos. Try [K-Lite Codecs](https://www.codecguide.com/download_k-lite_codec_pack_basic.htm).
<h2>Attribution</h2>
X-Ray and proton data is from [NOAA](https://www.swpc.noaa.gov/products/goes-x-ray-flux).
Solar images are from [SDO | Solar Dynamics Observatory](https://sdo.gsfc.nasa.gov/).
Corona images are from [SOHO](https://soho.nascom.nasa.gov/home.html).
GRB data is from [GRBweb](https://user-web.icecube.wisc.edu/~grbweb_public/index.html).
Solar flare data is from [Solar Orbiter STIX Data Center](https://datacenter.stix.i4ds.net/).

361
plugins/feature/sid/sid.cpp Normal file
View File

@ -0,0 +1,361 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QBuffer>
#include "SWGFeatureSettings.h"
#include "SWGDeviceState.h"
#include "dsp/dspengine.h"
#include "device/deviceset.h"
#include "feature/featureset.h"
#include "settings/serializable.h"
#include "maincore.h"
#include "sid.h"
#include "sidworker.h"
MESSAGE_CLASS_DEFINITION(SIDMain::MsgConfigureSID, Message)
MESSAGE_CLASS_DEFINITION(SIDMain::MsgStartStop, Message)
MESSAGE_CLASS_DEFINITION(SIDMain::MsgReportWorker, Message)
MESSAGE_CLASS_DEFINITION(SIDMain::MsgMeasurement, Message)
const char* const SIDMain::m_featureIdURI = "sdrangel.feature.sid";
const char* const SIDMain::m_featureId = "SID";
SIDMain::SIDMain(WebAPIAdapterInterface *webAPIAdapterInterface) :
Feature(m_featureIdURI, webAPIAdapterInterface),
m_thread(nullptr),
m_worker(nullptr)
{
qDebug("SIDMain::SID: webAPIAdapterInterface: %p", webAPIAdapterInterface);
setObjectName(m_featureId);
m_state = StIdle;
m_errorMessage = "SID error";
m_networkManager = new QNetworkAccessManager();
QObject::connect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&SIDMain::networkManagerFinished
);
}
SIDMain::~SIDMain()
{
QObject::disconnect(
m_networkManager,
&QNetworkAccessManager::finished,
this,
&SIDMain::networkManagerFinished
);
delete m_networkManager;
}
void SIDMain::start()
{
qDebug("SIDMain::start");
m_thread = new QThread();
m_worker = new SIDWorker(this, m_webAPIAdapterInterface);
m_worker->moveToThread(m_thread);
QObject::connect(m_thread, &QThread::started, m_worker, &SIDWorker::startWork);
QObject::connect(m_thread, &QThread::finished, m_worker, &QObject::deleteLater);
QObject::connect(m_thread, &QThread::finished, m_thread, &QThread::deleteLater);
m_worker->setMessageQueueToFeature(getInputMessageQueue());
m_worker->setMessageQueueToGUI(getMessageQueueToGUI());
m_thread->start();
m_state = StRunning;
MsgConfigureSID *msg = MsgConfigureSID::create(m_settings, QList<QString>(), true);
m_worker->getInputMessageQueue()->push(msg);
}
void SIDMain::stop()
{
qDebug("SIDMain::stop");
m_state = StIdle;
if (m_thread)
{
m_thread->quit();
m_thread->wait();
m_thread = nullptr;
m_worker = nullptr;
}
}
bool SIDMain::handleMessage(const Message& cmd)
{
if (MsgConfigureSID::match(cmd))
{
MsgConfigureSID& cfg = (MsgConfigureSID&) cmd;
qDebug() << "SIDMain::handleMessage: MsgConfigureSID";
applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce());
return true;
}
else if (MsgStartStop::match(cmd))
{
MsgStartStop& cfg = (MsgStartStop&) cmd;
qDebug() << "SIDMain::handleMessage: MsgStartStop: start:" << cfg.getStartStop();
if (cfg.getStartStop()) {
start();
} else {
stop();
}
return true;
}
else if (MsgReportWorker::match(cmd))
{
MsgReportWorker& report = (MsgReportWorker&) cmd;
m_state = StError;
m_errorMessage = report.getMessage();
return true;
}
else
{
return false;
}
}
QByteArray SIDMain::serialize() const
{
return m_settings.serialize();
}
bool SIDMain::deserialize(const QByteArray& data)
{
if (m_settings.deserialize(data))
{
MsgConfigureSID *msg = MsgConfigureSID::create(m_settings, QList<QString>(), true);
m_inputMessageQueue.push(msg);
return true;
}
else
{
m_settings.resetToDefaults();
MsgConfigureSID *msg = MsgConfigureSID::create(m_settings, QList<QString>(), true);
m_inputMessageQueue.push(msg);
return false;
}
}
void SIDMain::applySettings(const SIDSettings& settings, const QList<QString>& settingsKeys, bool force)
{
qDebug() << "SIDMain::applySettings:" << settings.getDebugString(settingsKeys, force) << " force: " << force;
if (m_worker)
{
MsgConfigureSID *msg = MsgConfigureSID::create(settings, settingsKeys, force);
m_worker->getInputMessageQueue()->push(msg);
}
if (settingsKeys.contains("useReverseAPI"))
{
bool fullUpdate = (settingsKeys.contains("useReverseAPI") && settings.m_useReverseAPI) ||
settingsKeys.contains("reverseAPIAddress") ||
settingsKeys.contains("reverseAPIPort") ||
settingsKeys.contains("reverseAPIFeatureSetIndex") ||
settingsKeys.contains("m_reverseAPIFeatureIndex");
webapiReverseSendSettings(settingsKeys, settings, fullUpdate || force);
}
if (force) {
m_settings = settings;
} else {
m_settings.applySettings(settingsKeys, settings);
}
}
int SIDMain::webapiRun(bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage)
{
(void) errorMessage;
getFeatureStateStr(*response.getState());
MsgStartStop *msg = MsgStartStop::create(run);
getInputMessageQueue()->push(msg);
return 202;
}
int SIDMain::webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setSidSettings(new SWGSDRangel::SWGSIDSettings());
response.getSidSettings()->init();
webapiFormatFeatureSettings(response, m_settings);
return 200;
}
int SIDMain::webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
SIDSettings settings = m_settings;
webapiUpdateFeatureSettings(settings, featureSettingsKeys, response);
MsgConfigureSID *msg = MsgConfigureSID::create(settings, featureSettingsKeys, force);
m_inputMessageQueue.push(msg);
if (m_guiMessageQueue) // forward to GUI if any
{
MsgConfigureSID *msgToGUI = MsgConfigureSID::create(settings, featureSettingsKeys, force);
m_guiMessageQueue->push(msgToGUI);
}
webapiFormatFeatureSettings(response, settings);
return 200;
}
void SIDMain::webapiFormatFeatureSettings(
SWGSDRangel::SWGFeatureSettings& response,
const SIDSettings& settings)
{
if (response.getSidSettings()->getTitle()) {
*response.getSidSettings()->getTitle() = settings.m_title;
} else {
response.getSidSettings()->setTitle(new QString(settings.m_title));
}
response.getSidSettings()->setRgbColor(settings.m_rgbColor);
response.getSidSettings()->setUseReverseApi(settings.m_useReverseAPI ? 1 : 0);
if (response.getSidSettings()->getReverseApiAddress()) {
*response.getSidSettings()->getReverseApiAddress() = settings.m_reverseAPIAddress;
} else {
response.getSidSettings()->setReverseApiAddress(new QString(settings.m_reverseAPIAddress));
}
response.getSidSettings()->setReverseApiPort(settings.m_reverseAPIPort);
response.getSidSettings()->setReverseApiFeatureSetIndex(settings.m_reverseAPIFeatureSetIndex);
response.getSidSettings()->setReverseApiFeatureIndex(settings.m_reverseAPIFeatureIndex);
if (settings.m_rollupState)
{
if (response.getSidSettings()->getRollupState())
{
settings.m_rollupState->formatTo(response.getSidSettings()->getRollupState());
}
else
{
SWGSDRangel::SWGRollupState *swgRollupState = new SWGSDRangel::SWGRollupState();
settings.m_rollupState->formatTo(swgRollupState);
response.getSidSettings()->setRollupState(swgRollupState);
}
}
}
void SIDMain::webapiUpdateFeatureSettings(
SIDSettings& settings,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response)
{
if (featureSettingsKeys.contains("title")) {
settings.m_title = *response.getSidSettings()->getTitle();
}
if (featureSettingsKeys.contains("rgbColor")) {
settings.m_rgbColor = response.getSidSettings()->getRgbColor();
}
if (featureSettingsKeys.contains("useReverseAPI")) {
settings.m_useReverseAPI = response.getSidSettings()->getUseReverseApi() != 0;
}
if (featureSettingsKeys.contains("reverseAPIAddress")) {
settings.m_reverseAPIAddress = *response.getSidSettings()->getReverseApiAddress();
}
if (featureSettingsKeys.contains("reverseAPIPort")) {
settings.m_reverseAPIPort = response.getSidSettings()->getReverseApiPort();
}
if (featureSettingsKeys.contains("reverseAPIFeatureSetIndex")) {
settings.m_reverseAPIFeatureSetIndex = response.getSidSettings()->getReverseApiFeatureSetIndex();
}
if (featureSettingsKeys.contains("reverseAPIFeatureIndex")) {
settings.m_reverseAPIFeatureIndex = response.getSidSettings()->getReverseApiFeatureIndex();
}
if (settings.m_rollupState && featureSettingsKeys.contains("rollupState")) {
settings.m_rollupState->updateFrom(featureSettingsKeys, response.getSidSettings()->getRollupState());
}
}
void SIDMain::webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const SIDSettings& settings, bool force)
{
SWGSDRangel::SWGFeatureSettings *swgFeatureSettings = new SWGSDRangel::SWGFeatureSettings();
// swgFeatureSettings->setOriginatorFeatureIndex(getIndexInDeviceSet());
// swgFeatureSettings->setOriginatorFeatureSetIndex(getDeviceSetIndex());
swgFeatureSettings->setFeatureType(new QString("SID"));
swgFeatureSettings->setSidSettings(new SWGSDRangel::SWGSIDSettings());
SWGSDRangel::SWGSIDSettings *swgSIDSettings = swgFeatureSettings->getSidSettings();
// transfer data that has been modified. When force is on transfer all data except reverse API data
if (featureSettingsKeys.contains("title") || force) {
swgSIDSettings->setTitle(new QString(settings.m_title));
}
if (featureSettingsKeys.contains("rgbColor") || force) {
swgSIDSettings->setRgbColor(settings.m_rgbColor);
}
QString channelSettingsURL = QString("http://%1:%2/sdrangel/featureset/%3/feature/%4/settings")
.arg(settings.m_reverseAPIAddress)
.arg(settings.m_reverseAPIPort)
.arg(settings.m_reverseAPIFeatureSetIndex)
.arg(settings.m_reverseAPIFeatureIndex);
m_networkRequest.setUrl(QUrl(channelSettingsURL));
m_networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QBuffer *buffer = new QBuffer();
buffer->open((QBuffer::ReadWrite));
buffer->write(swgFeatureSettings->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 swgFeatureSettings;
}
void SIDMain::networkManagerFinished(QNetworkReply *reply)
{
QNetworkReply::NetworkError replyError = reply->error();
if (replyError)
{
qWarning() << "SIDMain::networkManagerFinished:"
<< " error(" << (int) replyError
<< "): " << replyError
<< ": " << reply->errorString();
}
else
{
QString answer = reply->readAll();
answer.chop(1); // remove last \n
qDebug("SIDMain::networkManagerFinished: reply:\n%s", answer.toStdString().c_str());
}
reply->deleteLater();
}

188
plugins/feature/sid/sid.h Normal file
View File

@ -0,0 +1,188 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SID_H_
#define INCLUDE_FEATURE_SID_H_
#include <QThread>
#include <QNetworkRequest>
#include <QDateTime>
#include "feature/feature.h"
#include "util/message.h"
#include "sidsettings.h"
class WebAPIAdapterInterface;
class QNetworkAccessManager;
class QNetworkReply;
class SIDWorker;
namespace SWGSDRangel {
class SWGDeviceState;
}
// There's a structure in winnt.h named SID
class SIDMain : public Feature
{
Q_OBJECT
public:
class MsgConfigureSID : public Message {
MESSAGE_CLASS_DECLARATION
public:
const SIDSettings& getSettings() const { return m_settings; }
const QList<QString>& getSettingsKeys() const { return m_settingsKeys; }
bool getForce() const { return m_force; }
static MsgConfigureSID* create(const SIDSettings& settings, const QList<QString>& settingsKeys, bool force) {
return new MsgConfigureSID(settings, settingsKeys, force);
}
private:
SIDSettings m_settings;
QList<QString> m_settingsKeys;
bool m_force;
MsgConfigureSID(const SIDSettings& settings, const QList<QString>& settingsKeys, bool force) :
Message(),
m_settings(settings),
m_settingsKeys(settingsKeys),
m_force(force)
{ }
};
class MsgStartStop : public Message {
MESSAGE_CLASS_DECLARATION
public:
bool getStartStop() const { return m_startStop; }
static MsgStartStop* create(bool startStop) {
return new MsgStartStop(startStop);
}
protected:
bool m_startStop;
MsgStartStop(bool startStop) :
Message(),
m_startStop(startStop)
{ }
};
class MsgReportWorker : public Message {
MESSAGE_CLASS_DECLARATION
public:
QString getMessage() { return m_message; }
static MsgReportWorker* create(QString message) {
return new MsgReportWorker(message);
}
private:
QString m_message;
MsgReportWorker(QString message) :
Message(),
m_message(message)
{}
};
class MsgMeasurement : public Message {
MESSAGE_CLASS_DECLARATION
public:
QDateTime getDateTime() const { return m_dateTime; }
QString getId() const { return m_id; }
double getMeasurement() const { return m_measurement; }
static MsgMeasurement* create(QDateTime dateTime, const QString& id, double measurement) {
return new MsgMeasurement(dateTime, id, measurement);
}
private:
QDateTime m_dateTime;
const QString m_id;
double m_measurement;
MsgMeasurement(QDateTime dateTime, const QString& id, double measurement) :
Message(),
m_dateTime(dateTime),
m_id(id),
m_measurement(measurement)
{}
};
SIDMain(WebAPIAdapterInterface *webAPIAdapterInterface);
virtual ~SIDMain();
virtual void destroy() { delete this; }
virtual bool handleMessage(const Message& cmd);
virtual void getIdentifier(QString& id) const { id = objectName(); }
virtual QString getIdentifier() const { return objectName(); }
virtual void getTitle(QString& title) const { title = m_settings.m_title; }
virtual QByteArray serialize() const;
virtual bool deserialize(const QByteArray& data);
virtual int webapiRun(bool run,
SWGSDRangel::SWGDeviceState& response,
QString& errorMessage);
virtual int webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
static void webapiFormatFeatureSettings(
SWGSDRangel::SWGFeatureSettings& response,
const SIDSettings& settings);
static void webapiUpdateFeatureSettings(
SIDSettings& settings,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response);
static const char* const m_featureIdURI;
static const char* const m_featureId;
private:
QThread *m_thread;
SIDWorker *m_worker;
SIDSettings m_settings;
QNetworkAccessManager *m_networkManager;
QNetworkRequest m_networkRequest;
void start();
void stop();
void applySettings(const SIDSettings& settings, const QList<QString>& settingsKeys, bool force = false);
void webapiReverseSendSettings(const QList<QString>& featureSettingsKeys, const SIDSettings& settings, bool force);
private slots:
void networkManagerFinished(QNetworkReply *reply);
};
#endif // INCLUDE_FEATURE_SID_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,311 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023-2024 Jon Beniston, M7RCE //
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SIDGUI_H_
#define INCLUDE_FEATURE_SIDGUI_H_
#include <QTimer>
#include <QMenu>
#include <QToolButton>
#include <QDateTime>
#include <QtCharts>
#include <QMediaPlayer>
#include "feature/featuregui.h"
#include "util/messagequeue.h"
#include "util/movingaverage.h"
#include "util/grb.h"
#include "util/goesxray.h"
#include "util/solardynamicsobservatory.h"
#include "util/stix.h"
#include "settings/rollupstate.h"
#include "availablechannelorfeaturehandler.h"
#include "sidsettings.h"
class PluginAPI;
class FeatureUISet;
class SIDMain;
namespace Ui {
class SIDGUI;
}
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
using namespace QtCharts;
#endif
class SIDGUI : public FeatureGUI {
Q_OBJECT
struct Measurement {
QDateTime m_dateTime;
double m_measurement;
Measurement(QDateTime dateTime, double measurement) :
m_dateTime(dateTime),
m_measurement(measurement)
{
}
};
struct ChannelMeasurement {
QString m_id;
QList<Measurement> m_measurements;
QXYSeries *m_series;
double m_minMeasurement;
double m_maxMeasurement;
MovingAverageUtilVar<double, double> m_movingAverage;
ChannelMeasurement() :
m_series(nullptr),
m_minMeasurement(std::numeric_limits<double>::quiet_NaN()),
m_maxMeasurement(std::numeric_limits<double>::quiet_NaN()),
m_movingAverage(1)
{
}
ChannelMeasurement(const QString& id, int averageSamples) :
m_id(id),
m_series(nullptr),
m_minMeasurement(std::numeric_limits<double>::quiet_NaN()),
m_maxMeasurement(std::numeric_limits<double>::quiet_NaN()),
m_movingAverage(averageSamples)
{
}
void append(QDateTime dateTime, double measurement, bool updateSeries=true)
{
m_measurements.append(Measurement(dateTime, measurement));
if (std::isnan(m_minMeasurement)) {
m_minMeasurement = measurement;
} else {
m_minMeasurement = std::min(m_minMeasurement, measurement);
}
if (std::isnan(m_maxMeasurement)) {
m_maxMeasurement = measurement;
} else {
m_maxMeasurement = std::max(m_maxMeasurement, measurement);
}
if (m_series && updateSeries) {
appendSeries(dateTime, measurement);
}
}
void newSeries(QXYSeries *series, int samples)
{
m_series = series;
m_movingAverage.resize(samples);
}
void appendSeries(QDateTime dateTime, double measurement)
{
m_movingAverage(measurement);
m_series->append(dateTime.toMSecsSinceEpoch(), m_movingAverage.instantAverage());
}
void clear()
{
m_minMeasurement = std::numeric_limits<double>::quiet_NaN();
m_maxMeasurement = std::numeric_limits<double>::quiet_NaN();
m_measurements.clear();
m_series = nullptr;
}
};
public:
static SIDGUI* create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature);
virtual void destroy();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
virtual MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
virtual void setWorkspaceIndex(int 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; }
private:
Ui::SIDGUI* ui;
PluginAPI* m_pluginAPI;
FeatureUISet* m_featureUISet;
SIDSettings m_settings;
QList<QString> m_settingsKeys;
RollupState m_rollupState;
bool m_doApplySettings;
SIDMain* m_sid;
MessageQueue m_inputMessageQueue;
QTimer m_statusTimer;
QTimer m_autosaveTimer;
int m_lastFeatureState;
QFileDialog m_fileDialog;
QList<ChannelMeasurement> m_channelMeasurements;
QDateTimeAxis *m_chartXAxis;
QValueAxis *m_chartY1Axis;
QCategoryAxis *m_chartY2Axis;
QLogValueAxis *m_chartY3Axis;
QLogValueAxis *m_chartProtonAxis;
double m_minMeasurement;
double m_maxMeasurement;
QDateTime m_minDateTime;
QDateTime m_maxDateTime;
QDateTimeAxis *m_xRayChartXAxis;
QCategoryAxis *m_xRayChartYAxis;
GOESXRay *m_goesXRay;
ChannelMeasurement m_xrayShortMeasurements[2]; // Primary and secondary
ChannelMeasurement m_xrayLongMeasurements[2];
ChannelMeasurement m_protonMeasurements[4]; // 4 energy bands
static const QStringList m_protonEnergies;
SolarDynamicsObservatory *m_solarDynamicsObservatory;
QStringList m_sdoImageNames;
QMediaPlayer *m_player;
GRB *m_grb;
QList<GRB::Data> m_grbData;
QScatterSeries *m_grbSeries;
float m_grbMin;
float m_grbMax;
STIX *m_stix;
QList<STIX::FlareData> m_stixData;
QScatterSeries *m_stixSeries;
AvailableChannelOrFeatureHandler m_availableFeatureHandler;
explicit SIDGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent = nullptr);
virtual ~SIDGUI();
void blockApplySettings(bool block);
void applySetting(const QString& settingsKey);
void applySettings(const QStringList& settingsKeys, bool force = false);
void applyAllSettings();
void displaySettings();
bool handleMessage(const Message& message);
void makeUIConnections();
void writeCSV(const QString& filename);
void readCSV(const QString& filename, bool autoload);
void setAutosaveTimer();
ChannelMeasurement& addMeasurements(const QString& id);
ChannelMeasurement& getMeasurements(const QString& id);
void addMeasurement(const QString& id, QDateTime dateTime, double measurement);
void plotChart();
void plotXRayChart();
void createGRBSeries(QChart *chart, QDateTimeAxis *xAxis, QLogValueAxis *yAxis);
void createXRaySeries(QChart *chart, QDateTimeAxis *xAxis, QCategoryAxis *yAxis);
void createProtonSeries(QChart *chart, QDateTimeAxis *xAxis, QLogValueAxis *yAxis);
void createSTIXSeries(QChart *chart, QDateTimeAxis *xAxis, QCategoryAxis *yAxis);
void createFlareAxis(QCategoryAxis *yAxis);
void setXAxisRange();
void setY1AxisRange();
void setAutoscaleX();
void setAutoscaleY();
void autoscaleX();
void autoscaleY();
void setButtonBackground(QToolButton *button, bool checked);
void updateMeasurementRange(double measurement);
void updateTimeRange(QDateTime dateTime);
void applySDO();
void applyDateTime();
bool eventFilter(QObject *obj, QEvent *event) override;
void sendToSkyMap(const AvailableChannelOrFeature& skymap, float ra, float dec);
void showGRBContextMenu(QContextMenuEvent *contextEvent, QChartView *chartView, int closestPoint);
void showStixContextMenu(QContextMenuEvent *contextEvent, QChartView *chartView, int closestPoint);
void showContextMenu(QContextMenuEvent *contextEvent);
bool findClosestPoint(QContextMenuEvent *contextEvent, QChart *chart, QScatterSeries *series, int& closestPoint);
void clearMinMax();
bool plotAnyXRay() const;
void clearAllData();
void connectDataUpdates();
void disconnectDataUpdates();
void getData();
void openSkyMap();
static qreal pixelDistance(QChart *chart, QAbstractSeries *series, QPointF a, QPointF b);
private slots:
void onMenuDialogCalled(const QPoint &p);
void onWidgetRolled(QWidget* widget, bool rollDown);
void handleInputMessages();
void on_startStop_toggled(bool checked);
void on_samples_valueChanged(int value);
void on_separateCharts_toggled(bool checked);
void on_displayLegend_toggled(bool checked);
void on_plotXRayLongPrimary_toggled(bool checked);
void on_plotXRayLongSecondary_toggled(bool checked);
void on_plotXRayShortPrimary_toggled(bool checked);
void on_plotXRayShortSecondary_toggled(bool checked);
void on_plotGRB_toggled(bool checked);
void on_plotSTIX_toggled(bool checked);
void on_plotProton_toggled(bool checked);
void on_deleteAll_clicked();
void on_autoscaleX_clicked();
void on_autoscaleY_clicked();
void on_today_clicked();
void on_prevDay_clicked();
void on_nextDay_clicked();
void on_startDateTime_dateTimeChanged(QDateTime value);
void on_endDateTime_dateTimeChanged(QDateTime value);
void on_y1Min_valueChanged(double value);
void on_y1Max_valueChanged(double value);
void on_saveData_clicked();
void on_loadData_clicked();
void on_saveChartImage_clicked();
void autoscaleXRightClicked();
void autoscaleYRightClicked();
void todayRightClicked();
void updateStatus();
void autosave();
void on_settings_clicked();
void xRayDataUpdated(const QList<GOESXRay::XRayData>& data, bool primary);
void protonDataUpdated(const QList<GOESXRay::ProtonData>& data, bool primary);
void grbDataUpdated(const QList<GRB::Data>& data);
void stixDataUpdated(const QList<STIX::FlareData>& data);
void legendMarkerClicked();
void seriesClicked(const QPointF &point);
void chartSplitterMoved(int pos, int index);
void sdoSplitterMoved(int pos, int index);
void on_sdoEnabled_toggled(bool checked);
void on_sdoVideoEnabled_toggled(bool checked);
void on_sdoData_currentIndexChanged(int index);
void on_sdoNow_toggled(bool checked);
void on_sdoDateTime_dateTimeChanged(QDateTime value);
void sdoImageUpdated(const QImage& image);
void sdoVideoError(QMediaPlayer::Error error);
void sdoVideoStatusChanged(QMediaPlayer::MediaStatus status);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
void sdoBufferStatusChanged(int percentFilled);
#else
void sdoBufferProgressChanged(float filled);
#endif
void on_showSats_clicked();
void onSatTrackerAdded(int featureSetIndex, Feature *feature);
void on_map_currentTextChanged(const QString& text);
void featuresChanged(const QStringList& renameFrom, const QStringList& renameTo);
};
#endif // INCLUDE_FEATURE_SIDGUI_H_

View File

@ -0,0 +1,767 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SIDGUI</class>
<widget class="RollupContents" name="SIDGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1044</width>
<height>580</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>320</width>
<height>100</height>
</size>
</property>
<property name="font">
<font>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>SID</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<rect>
<x>10</x>
<y>10</y>
<width>964</width>
<height>80</height>
</rect>
</property>
<property name="windowTitle">
<string>Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>3</number>
</property>
<property name="leftMargin">
<number>2</number>
</property>
<property name="topMargin">
<number>2</number>
</property>
<property name="rightMargin">
<number>2</number>
</property>
<property name="bottomMargin">
<number>2</number>
</property>
<item>
<layout class="QHBoxLayout" name="chartButtonsLayout">
<item>
<widget class="ButtonSwitch" name="startStop">
<property name="toolTip">
<string>Start/stop measurements</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/play.png</normaloff>
<normalon>:/stop.png</normalon>:/play.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="loadData">
<property name="toolTip">
<string>Load data from a .csv file</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/load.png</normaloff>:/load.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="saveData">
<property name="toolTip">
<string>Save data to a .csv file</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/save.png</normaloff>:/save.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="saveChartImage">
<property name="toolTip">
<string>Save chart to an image file</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/picture.png</normaloff>:/picture.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="deleteAll">
<property name="toolTip">
<string>Delete all data</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/bin.png</normaloff>:/bin.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="averageLabel">
<property name="text">
<string>Avg</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="samples">
<property name="toolTip">
<string>Number of samples in average</string>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotXRayLongPrimary">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Display primary long wavelength X-Ray data on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/xlp.svg</normaloff>:/sid/icons/xlp.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotXRayLongSecondary">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Display secondary long wavelength X-Ray data on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/xls.svg</normaloff>:/sid/icons/xls.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotXRayShortPrimary">
<property name="toolTip">
<string>Display primary short wavelength X-Ray data on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/xsp.svg</normaloff>:/sid/icons/xsp.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotXRayShortSecondary">
<property name="toolTip">
<string>Display secondary short wavelength X-Ray data on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/xss.svg</normaloff>:/sid/icons/xss.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotProton">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Display proton flux data on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/proton.svg</normaloff>:/sid/icons/proton.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotGRB">
<property name="toolTip">
<string>Display GRBs on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/gamma.svg</normaloff>:/sid/icons/gamma.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="plotSTIX">
<property name="toolTip">
<string>Display solar flares from STIX on chart</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/solar-orbiter.svg</normaloff>:/sid/icons/solar-orbiter.svg</iconset>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_6">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="separateCharts">
<property name="toolTip">
<string>Display as a single chart or multiple charts</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/chartcombined.png</normaloff>
<normalon>:/sid/icons/chartseparate.png</normalon>:/sid/icons/chartcombined.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="displayLegend">
<property name="toolTip">
<string>Display legend</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/legend.png</normaloff>:/sid/icons/legend.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="settings">
<property name="toolTip">
<string>Open settings dialog</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/listing.png</normaloff>:/listing.png</iconset>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="sdoLabel">
<property name="text">
<string>SDO/SOHO</string>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="sdoEnabled">
<property name="toolTip">
<string>Display SDO/SOHO imagery</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="icons.qrc">
<normaloff>:/sid/icons/sun.png</normaloff>:/sid/icons/sun.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="sdoVideoEnabled">
<property name="toolTip">
<string>Select image or video</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/picture.png</normaloff>
<normalon>:/film.png</normalon>:/picture.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="sdoData">
<property name="toolTip">
<string>Image/wavelength selection</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="showSats">
<property name="toolTip">
<string>Show GOES 16, 18 and SDO in Satellite Tracker</string>
</property>
<property name="text">
<string/>
</property>
<property name="icon">
<iconset resource="../../../sdrgui/resources/res.qrc">
<normaloff>:/gps.png</normaloff>:/gps.png</iconset>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="chartAxisLayout">
<item>
<widget class="QToolButton" name="autoscaleX">
<property name="toolTip">
<string>Autoscale X-axis. Right click to continually autoscale</string>
</property>
<property name="text">
<string>X</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="autoscaleY">
<property name="toolTip">
<string>Autoscale Y-axis. Right click to continually autoscale</string>
</property>
<property name="text">
<string>Y</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="today">
<property name="toolTip">
<string>Set X-axis range to today. Right click to set to today's daylight hours.</string>
</property>
<property name="text">
<string>T</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="prevDay">
<property name="toolTip">
<string>Set X-axis range to -1 day</string>
</property>
<property name="text">
<string>-1</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="nextDay">
<property name="toolTip">
<string>Set X-axis range to +1 day</string>
</property>
<property name="text">
<string>+1</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="startDateTimeLabel">
<property name="text">
<string>Start</string>
</property>
</widget>
</item>
<item>
<widget class="WrappingDateTimeEdit" name="startDateTime">
<property name="toolTip">
<string>X axis start time</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="endDateTimeLabel">
<property name="text">
<string>End</string>
</property>
</widget>
</item>
<item>
<widget class="WrappingDateTimeEdit" name="endDateTime">
<property name="toolTip">
<string>X axis end time</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="minLabel">
<property name="text">
<string>Min</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="y1Min">
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>-150.000000000000000</double>
</property>
<property name="value">
<double>-100.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="minUnits">
<property name="text">
<string>dB</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="maxLabel">
<property name="text">
<string>Max</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="y1Max">
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>-150.000000000000000</double>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="maxUnits">
<property name="text">
<string>dB</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="sdoNow">
<property name="toolTip">
<string>When checked SDO data is the latest available. When unchecked, date and time may be set manually</string>
</property>
<property name="text">
<string>Now</string>
</property>
</widget>
</item>
<item>
<widget class="WrappingDateTimeEdit" name="sdoDateTime">
<property name="toolTip">
<string>Date and time for SDO data</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="mapLabel">
<property name="text">
<string>Map</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="map">
<property name="toolTip">
<string>3D Map feature to send date and time to</string>
</property>
<item>
<property name="text">
<string>None</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="charts" native="true">
<property name="geometry">
<rect>
<x>10</x>
<y>100</y>
<width>661</width>
<height>384</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSplitter" name="sdoSplitter">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QSplitter" name="chartSplitter">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QChartView" name="chart">
<property name="minimumSize">
<size>
<width>100</width>
<height>100</height>
</size>
</property>
<property name="windowTitle">
<string>Power vs Time</string>
</property>
</widget>
<widget class="QChartView" name="xRayChart"/>
</widget>
<widget class="QWidget" name="sdoContainer" native="true">
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QProgressBar" name="sdoProgressBar">
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item>
<widget class="ScaledImage" name="sdoImage">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QVideoWidget" name="sdoVideo" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>
<customwidgets>
<customwidget>
<class>RollupContents</class>
<extends>QWidget</extends>
<header>gui/rollupcontents.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>QChartView</class>
<extends>QGraphicsView</extends>
<header>QtCharts</header>
</customwidget>
<customwidget>
<class>WrappingDateTimeEdit</class>
<extends>QDateTimeEdit</extends>
<header>gui/wrappingdatetimeedit.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ScaledImage</class>
<extends>QLabel</extends>
<header>gui/scaledimage.h</header>
</customwidget>
<customwidget>
<class>QVideoWidget</class>
<extends>QWidget</extends>
<header>qvideowidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../sdrgui/resources/res.qrc"/>
<include location="icons.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,79 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QtPlugin>
#include "plugin/pluginapi.h"
#ifndef SERVER_MODE
#include "sidgui.h"
#endif
#include "sid.h"
#include "sidplugin.h"
#include "sidwebapiadapter.h"
const PluginDescriptor SIDPlugin::m_pluginDescriptor = {
SIDMain::m_featureId,
QStringLiteral("SID"),
QStringLiteral("7.20.0"),
QStringLiteral("(c) Jon Beniston, M7RCE"),
QStringLiteral("https://github.com/f4exb/sdrangel"),
true,
QStringLiteral("https://github.com/f4exb/sdrangel")
};
SIDPlugin::SIDPlugin(QObject* parent) :
QObject(parent),
m_pluginAPI(nullptr)
{
}
const PluginDescriptor& SIDPlugin::getPluginDescriptor() const
{
return m_pluginDescriptor;
}
void SIDPlugin::initPlugin(PluginAPI* pluginAPI)
{
m_pluginAPI = pluginAPI;
m_pluginAPI->registerFeature(SIDMain::m_featureIdURI, SIDMain::m_featureId, this);
}
#ifdef SERVER_MODE
FeatureGUI* SIDPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
{
(void) featureUISet;
(void) feature;
return nullptr;
}
#else
FeatureGUI* SIDPlugin::createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const
{
return SIDGUI::create(m_pluginAPI, featureUISet, feature);
}
#endif
Feature* SIDPlugin::createFeature(WebAPIAdapterInterface* webAPIAdapterInterface) const
{
return new SIDMain(webAPIAdapterInterface);
}
FeatureWebAPIAdapter* SIDPlugin::createFeatureWebAPIAdapter() const
{
return new SIDWebAPIAdapter();
}

View File

@ -0,0 +1,49 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SIDPLUGIN_H
#define INCLUDE_FEATURE_SIDPLUGIN_H
#include <QObject>
#include "plugin/plugininterface.h"
class FeatureGUI;
class WebAPIAdapterInterface;
class SIDPlugin : public QObject, PluginInterface {
Q_OBJECT
Q_INTERFACES(PluginInterface)
Q_PLUGIN_METADATA(IID "sdrangel.feature.sid")
public:
explicit SIDPlugin(QObject* parent = nullptr);
const PluginDescriptor& getPluginDescriptor() const;
void initPlugin(PluginAPI* pluginAPI);
virtual FeatureGUI* createFeatureGUI(FeatureUISet *featureUISet, Feature *feature) const;
virtual Feature* createFeature(WebAPIAdapterInterface *webAPIAdapterInterface) const;
virtual FeatureWebAPIAdapter* createFeatureWebAPIAdapter() const;
private:
static const PluginDescriptor m_pluginDescriptor;
PluginAPI* m_pluginAPI;
};
#endif // INCLUDE_FEATURE_SIDPLUGIN_H

View File

@ -0,0 +1,650 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023-2024 Jon Beniston, M7RCE //
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QColor>
#include <QDataStream>
#include "util/simpleserializer.h"
#include "settings/serializable.h"
#include "sidsettings.h"
// https://medialab.github.io/iwanthue/
// Restricted dark colours and chroma at either end
const QList<QRgb> SIDSettings::m_defaultColors = {
0xdd4187,
0x7ce048,
0xc944db,
0xd5d851,
0x826add,
0x5da242,
0xc97bc1,
0x85e49b,
0xdf5035,
0x57d6d9,
0xd28e2e,
0x7091d3,
0xa3a052,
0xd36d76,
0x4aa47d,
0xc9895a,
};
const QList<QRgb> SIDSettings::m_defaultXRayShortColors = {
0x8a3ffc,
0x8a3ffc
};
const QList<QRgb> SIDSettings::m_defaultXRayLongColors = {
0x4589ff,
0x0f62fe
};
const QList<QRgb> SIDSettings::m_defaultProtonColors = {
0x9ef0f0,
0x3ddbd9,
0x08bdba,
0x009d9a
};
const QRgb SIDSettings::m_defaultGRBColor = 0xffffff;
const QRgb SIDSettings::m_defaultSTIXColor = 0xcccc00;
SIDSettings::SIDSettings() :
m_rollupState(nullptr),
m_workspaceIndex(0)
{
resetToDefaults();
}
void SIDSettings::resetToDefaults()
{
m_channelSettings = {};
m_period = 10.0f;
m_autosave = true;
m_autoload = true;
m_filename = "sid_autosave.csv";
m_autosavePeriod = 10;
m_samples = 1;
m_autoscaleX = true;
m_autoscaleY = true;
m_separateCharts = true;
m_displayLegend = true;
m_legendAlignment = Qt::AlignTop;
m_displayAxisTitles = true;
m_displaySecondaryAxis = true;
m_plotXRayLongPrimary = true;
m_plotXRayLongSecondary = false;
m_plotXRayShortPrimary = true;
m_plotXRayShortSecondary = false;
m_plotGRB = true;
m_plotSTIX = true;
m_plotProton = true;
m_y1Min = -100.0f;
m_y1Max = 0.0f;
m_startDateTime = QDateTime();
m_endDateTime = QDateTime();
m_xrayShortColors = m_defaultXRayShortColors;
m_xrayLongColors = m_defaultXRayLongColors;
m_protonColors = m_defaultProtonColors;
m_grbColor = m_defaultGRBColor;
m_stixColor =m_defaultSTIXColor;
m_sdoEnabled = true;
m_sdoVideoEnabled = false;
m_sdoData = "";
m_sdoNow = true;
m_sdoDateTime = QDateTime();
m_map = "";
m_title = "SID";
m_rgbColor = QColor(102, 0, 102).rgb();
m_useReverseAPI = false;
m_reverseAPIAddress = "127.0.0.1";
m_reverseAPIPort = 8888;
m_reverseAPIFeatureSetIndex = 0;
m_reverseAPIFeatureIndex = 0;
}
QByteArray SIDSettings::serialize() const
{
SimpleSerializer s(1);
s.writeList(1, m_channelSettings);
s.writeFloat(2, m_period);
s.writeBool(10, m_autosave);
s.writeBool(11, m_autoload);
s.writeString(12, m_filename);
s.writeS32(13, m_autosavePeriod);
s.writeS32(20, m_samples);
s.writeBool(21, m_autoscaleX);
s.writeBool(22, m_autoscaleY);
s.writeBool(23, m_separateCharts);
s.writeBool(24, m_displayLegend);
s.writeS32(25, (int) m_legendAlignment);
s.writeBool(26, m_displayAxisTitles);
s.writeBool(27, m_displaySecondaryAxis);
s.writeBool(28, m_plotXRayLongPrimary);
s.writeBool(29, m_plotXRayLongSecondary);
s.writeBool(30, m_plotXRayShortPrimary);
s.writeBool(31, m_plotXRayShortSecondary);
s.writeBool(32, m_plotGRB);
s.writeBool(33, m_plotSTIX);
s.writeBool(34, m_plotProton);
s.writeFloat(36, m_y1Min);
s.writeFloat(37, m_y1Max);
if (m_startDateTime.isValid()) {
s.writeS64(38, m_startDateTime.toMSecsSinceEpoch());
}
if (m_endDateTime.isValid()) {
s.writeS64(39, m_endDateTime.toMSecsSinceEpoch());
}
s.writeList(40, m_xrayShortColors);
s.writeList(41, m_xrayLongColors);
s.writeList(42, m_protonColors);
s.writeU32(43, m_grbColor);
s.writeU32(44, m_stixColor);
s.writeBool(50, m_sdoEnabled);
s.writeBool(51, m_sdoVideoEnabled);
s.writeString(52, m_sdoData);
s.writeBool(53, m_sdoNow);
if (m_sdoDateTime.isValid()) {
s.writeS64(54, m_sdoDateTime.toMSecsSinceEpoch());
}
s.writeString(55, m_map);
s.writeList(60, m_sdoSplitterSizes);
s.writeList(61, m_chartSplitterSizes);
s.writeString(70, m_title);
s.writeU32(71, m_rgbColor);
s.writeBool(72, m_useReverseAPI);
s.writeString(73, m_reverseAPIAddress);
s.writeU32(74, m_reverseAPIPort);
s.writeU32(75, m_reverseAPIFeatureSetIndex);
s.writeU32(76, m_reverseAPIFeatureIndex);
if (m_rollupState) {
s.writeBlob(77, m_rollupState->serialize());
}
s.writeS32(78, m_workspaceIndex);
s.writeBlob(79, m_geometryBytes);
return s.final();
}
bool SIDSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if (!d.isValid())
{
resetToDefaults();
return false;
}
if (d.getVersion() == 1)
{
QByteArray bytetmp;
uint32_t utmp;
qint64 tmp64;
QString strtmp;
QByteArray blob;
d.readList(1, &m_channelSettings);
d.readFloat(2, &m_period, 10.0f);
d.readBool(10, &m_autosave, true);
d.readBool(11, &m_autoload, true);
d.readString(12, &m_filename, "sid_autosave.csv");
d.readS32(13, &m_autosavePeriod, 10);
d.readS32(20, &m_samples, 1);
d.readBool(21, &m_autoscaleX, true);
d.readBool(22, &m_autoscaleY, true);
d.readBool(23, &m_separateCharts, true);
d.readBool(24, &m_displayLegend, true);
d.readS32(25, (int *) &m_legendAlignment, Qt::AlignTop);
d.readBool(26, &m_displayAxisTitles, true);
d.readBool(27, &m_displaySecondaryAxis, true);
d.readBool(28, &m_plotXRayLongPrimary, true);
d.readBool(29, &m_plotXRayLongSecondary, false);
d.readBool(30, &m_plotXRayShortPrimary, true);
d.readBool(31, &m_plotXRayShortSecondary, false);
d.readBool(32, &m_plotGRB, true);
d.readBool(33, &m_plotSTIX, true);
d.readBool(34, &m_plotProton, false);
d.readFloat(36, &m_y1Min, -100.0f);
d.readFloat(37, &m_y1Max, 0.0f);
if (d.readS64(38, &tmp64)) {
m_startDateTime = QDateTime::fromMSecsSinceEpoch(tmp64);
} else {
m_startDateTime = QDateTime();
}
if (d.readS64(39, &tmp64)) {
m_endDateTime = QDateTime::fromMSecsSinceEpoch(tmp64);
} else {
m_endDateTime = QDateTime();
}
d.readList(40, &m_xrayShortColors);
if (m_xrayShortColors.size() != 2) {
m_xrayShortColors = m_defaultXRayShortColors;
}
d.readList(41, &m_xrayLongColors);
if (m_xrayLongColors.size() != 2) {
m_xrayLongColors = m_defaultXRayLongColors;
}
d.readList(42, &m_protonColors);
if (m_protonColors.size() != 4) {
m_protonColors = m_defaultProtonColors;
}
d.readU32(43, &m_grbColor, m_defaultGRBColor);
d.readU32(44, &m_stixColor, m_defaultSTIXColor);
d.readBool(50, &m_sdoEnabled, true);
d.readBool(51, &m_sdoVideoEnabled, false);
d.readString(52, &m_sdoData, "");
d.readBool(53, &m_sdoNow);
if (d.readS64(54, &tmp64)) {
m_sdoDateTime = QDateTime::fromMSecsSinceEpoch(tmp64);
} else {
m_sdoDateTime = QDateTime();
}
d.readString(55, &m_map, "");
d.readList(60, &m_sdoSplitterSizes);
d.readList(61, &m_chartSplitterSizes);
d.readString(70, &m_title, "SID");
d.readU32(71, &m_rgbColor, QColor(102, 0, 102).rgb());
d.readBool(72, &m_useReverseAPI, false);
d.readString(73, &m_reverseAPIAddress, "127.0.0.1");
d.readU32(74, &utmp, 0);
if ((utmp > 1023) && (utmp < 65535)) {
m_reverseAPIPort = utmp;
} else {
m_reverseAPIPort = 8888;
}
d.readU32(75, &utmp, 0);
m_reverseAPIFeatureSetIndex = utmp > 99 ? 99 : utmp;
d.readU32(76, &utmp, 0);
m_reverseAPIFeatureIndex = utmp > 99 ? 99 : utmp;
if (m_rollupState)
{
d.readBlob(77, &bytetmp);
m_rollupState->deserialize(bytetmp);
}
d.readS32(78, &m_workspaceIndex, 0);
d.readBlob(79, &m_geometryBytes);
return true;
}
else
{
resetToDefaults();
return false;
}
}
void SIDSettings::applySettings(const QStringList& settingsKeys, const SIDSettings& settings)
{
if (settingsKeys.contains("channelSettings")) {
m_channelSettings = settings.m_channelSettings;
}
if (settingsKeys.contains("period")) {
m_period = settings.m_period;
}
if (settingsKeys.contains("autosave")) {
m_autosave = settings.m_autosave;
}
if (settingsKeys.contains("autoload")) {
m_autoload = settings.m_autoload;
}
if (settingsKeys.contains("autosavePeriod")) {
m_autosavePeriod = settings.m_autosavePeriod;
}
if (settingsKeys.contains("filename")) {
m_filename = settings.m_filename;
}
if (settingsKeys.contains("samples")) {
m_samples = settings.m_samples;
}
if (settingsKeys.contains("autoscaleX")) {
m_autoscaleX = settings.m_autoscaleX;
}
if (settingsKeys.contains("autoscaleY")) {
m_autoscaleY = settings.m_autoscaleY;
}
if (settingsKeys.contains("separateCharts")) {
m_separateCharts = settings.m_separateCharts;
}
if (settingsKeys.contains("displayLegend")) {
m_displayLegend = settings.m_displayLegend;
}
if (settingsKeys.contains("legendAlignment")) {
m_legendAlignment = settings.m_legendAlignment;
}
if (settingsKeys.contains("displayAxisTitles")) {
m_displayAxisTitles = settings.m_displayAxisTitles;
}
if (settingsKeys.contains("displayAxisLabels")) {
m_displaySecondaryAxis = settings.m_displaySecondaryAxis;
}
if (settingsKeys.contains("plotXRayLongPrimary")) {
m_plotXRayLongPrimary = settings.m_plotXRayLongPrimary;
}
if (settingsKeys.contains("plotXRayLongSecondary")) {
m_plotXRayLongSecondary = settings.m_plotXRayLongSecondary;
}
if (settingsKeys.contains("plotXRayShortPrimary")) {
m_plotXRayShortPrimary = settings.m_plotXRayShortPrimary;
}
if (settingsKeys.contains("plotXRayShorSecondary")) {
m_plotXRayShortSecondary = settings.m_plotXRayShortSecondary;
}
if (settingsKeys.contains("plotGRB")) {
m_plotGRB = settings.m_plotGRB;
}
if (settingsKeys.contains("plotSTIX")) {
m_plotSTIX = settings.m_plotSTIX;
}
if (settingsKeys.contains("plotProton")) {
m_plotProton = settings.m_plotProton;
}
if (settingsKeys.contains("startDateTime")) {
m_startDateTime = settings.m_startDateTime;
}
if (settingsKeys.contains("endDateTime")) {
m_endDateTime = settings.m_endDateTime;
}
if (settingsKeys.contains("y1Min")) {
m_y1Min = settings.m_y1Min;
}
if (settingsKeys.contains("y1Max")) {
m_y1Max = settings.m_y1Max;
}
if (settingsKeys.contains("xrayShortColors")) {
m_xrayShortColors = settings.m_xrayShortColors;
}
if (settingsKeys.contains("xrayLongColors")) {
m_xrayLongColors = settings.m_xrayLongColors;
}
if (settingsKeys.contains("protonColors")) {
m_protonColors = settings.m_protonColors;
}
if (settingsKeys.contains("grbColor")) {
m_grbColor = settings.m_grbColor;
}
if (settingsKeys.contains("stixColor")) {
m_stixColor = settings.m_stixColor;
}
if (settingsKeys.contains("sdoEnabled")) {
m_sdoEnabled = settings.m_sdoEnabled;
}
if (settingsKeys.contains("sdoVideoEnabled")) {
m_sdoVideoEnabled = settings.m_sdoVideoEnabled;
}
if (settingsKeys.contains("sdoData")) {
m_sdoData = settings.m_sdoData;
}
if (settingsKeys.contains("sdoNow")) {
m_sdoNow = settings.m_sdoNow;
}
if (settingsKeys.contains("sdoDateTime")) {
m_sdoDateTime = settings.m_sdoDateTime;
}
if (settingsKeys.contains("map")) {
m_map = settings.m_map;
}
if (settingsKeys.contains("sdoSplitterSizes")) {
m_sdoSplitterSizes = settings.m_sdoSplitterSizes;
}
if (settingsKeys.contains("chartSplitterSizes")) {
m_chartSplitterSizes = settings.m_chartSplitterSizes;
}
if (settingsKeys.contains("title")) {
m_title = settings.m_title;
}
if (settingsKeys.contains("rgbColor")) {
m_rgbColor = settings.m_rgbColor;
}
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("reverseAPIFeatureSetIndex")) {
m_reverseAPIFeatureSetIndex = settings.m_reverseAPIFeatureSetIndex;
}
if (settingsKeys.contains("reverseAPIFeatureIndex")) {
m_reverseAPIFeatureIndex = settings.m_reverseAPIFeatureIndex;
}
if (settingsKeys.contains("workspaceIndex")) {
m_workspaceIndex = settings.m_workspaceIndex;
}
}
QString SIDSettings::getDebugString(const QStringList& settingsKeys, bool force) const
{
std::ostringstream ostr;
if (settingsKeys.contains("channelSettings"))
{
QStringList s;
for (auto cs : m_channelSettings) {
s.append(cs.m_id);
}
ostr << " m_channelSettings: " << s.join(",").toStdString();
}
if (settingsKeys.contains("period") || force) {
ostr << " m_period: " << m_period;
}
if (settingsKeys.contains("autosave") || force) {
ostr << " m_autosave: " << m_autosave;
}
if (settingsKeys.contains("autoload") || force) {
ostr << " m_autoload: " << m_autoload;
}
if (settingsKeys.contains("filename") || force) {
ostr << " m_filename: " << m_filename.toStdString();
}
if (settingsKeys.contains("samples") || force) {
ostr << " m_samples: " << m_samples;
}
if (settingsKeys.contains("autoscaleX") || force) {
ostr << " m_autoscaleX: " << m_autoscaleX;
}
if (settingsKeys.contains("autoscaleY") || force) {
ostr << " m_autoscaleY: " << m_autoscaleY;
}
if (settingsKeys.contains("separateCharts") || force) {
ostr << " m_separateCharts: " << m_separateCharts;
}
if (settingsKeys.contains("displayLegend") || force) {
ostr << " m_displayLegend: " << m_displayLegend;
}
if (settingsKeys.contains("legendAlignment") || force) {
ostr << " m_legendAlignment: " << m_legendAlignment;
}
if (settingsKeys.contains("displayAxisTitles") || force) {
ostr << " m_displayAxisTitles: " << m_displayAxisTitles;
}
if (settingsKeys.contains("displayAxisLabels") || force) {
ostr << " m_displaySecondaryAxis: " << m_displaySecondaryAxis;
}
if (settingsKeys.contains("plotXRayLongPrimary") || force) {
ostr << " m_plotXRayLongPrimary: " << m_plotXRayLongPrimary;
}
if (settingsKeys.contains("plotXRayLongSecondary") || force) {
ostr << " m_plotXRayLongSecondary: " << m_plotXRayLongSecondary;
}
if (settingsKeys.contains("plotXRayShortPrimary") || force) {
ostr << " m_plotXRayShortPrimary: " << m_plotXRayShortPrimary;
}
if (settingsKeys.contains("plotXRayShortSecondary") || force) {
ostr << " m_plotXRayShortSecondary: " << m_plotXRayShortSecondary;
}
if (settingsKeys.contains("plotGRB") || force) {
ostr << " m_plotGRB: " << m_plotGRB;
}
if (settingsKeys.contains("plotSTIX") || force) {
ostr << " m_plotSTIX: " << m_plotSTIX;
}
if (settingsKeys.contains("plotProton") || force) {
ostr << " m_plotProton: " << m_plotProton;
}
if (settingsKeys.contains("startDateTime") || force) {
ostr << " m_startDateTime: " << m_startDateTime.toString().toStdString();
}
if (settingsKeys.contains("endDateTime") || force) {
ostr << " m_endDateTime: " << m_endDateTime.toString().toStdString();
}
if (settingsKeys.contains("y1Min") || force) {
ostr << " m_y1Min: " << m_y1Min;
}
if (settingsKeys.contains("y1Max") || force) {
ostr << " m_y1Max: " << m_y1Max;
}
if (settingsKeys.contains("sdoEnabled") || force) {
ostr << " m_sdoEnabled: " << m_sdoEnabled;
}
if (settingsKeys.contains("sdoVideoEnabled") || force) {
ostr << " m_sdoVideoEnabled: " << m_sdoVideoEnabled;
}
if (settingsKeys.contains("sdoData") || force) {
ostr << " m_sdoData: " << m_sdoData.toStdString();
}
if (settingsKeys.contains("sdoNow") || force) {
ostr << " m_sdoNow: " << m_sdoNow;
}
if (settingsKeys.contains("sdoDateTime") || force) {
ostr << " m_sdoDateTime: " << m_sdoDateTime.toString().toStdString();
}
if (settingsKeys.contains("map") || force) {
ostr << " m_map: " << m_map.toStdString();
}
if (settingsKeys.contains("title") || force) {
ostr << " m_title: " << m_title.toStdString();
}
if (settingsKeys.contains("rgbColor") || force) {
ostr << " m_rgbColor: " << m_rgbColor;
}
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("reverseAPIFeatureSetIndex") || force) {
ostr << " m_reverseAPIFeatureSetIndex: " << m_reverseAPIFeatureSetIndex;
}
if (settingsKeys.contains("reverseAPIFeatureIndex") || force) {
ostr << " m_reverseAPIFeatureIndex: " << m_reverseAPIFeatureIndex;
}
if (settingsKeys.contains("workspaceIndex") || force) {
ostr << " m_workspaceIndex: " << m_workspaceIndex;
}
return QString(ostr.str().c_str());
}
SIDSettings::ChannelSettings *SIDSettings::getChannelSettings(const QString& id)
{
for (int i = 0; i < m_channelSettings.size(); i++)
{
if (m_channelSettings[i].m_id == id) {
return &m_channelSettings[i];
}
}
return nullptr;
}
QByteArray SIDSettings::ChannelSettings::serialize() const
{
SimpleSerializer s(1);
s.writeString(1, m_id);
s.writeBool(2, m_enabled);
s.writeString(3, m_label);
s.writeU32(4, m_color.rgb());
return s.final();
}
bool SIDSettings::ChannelSettings::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if (!d.isValid()) {
return false;
}
if (d.getVersion() == 1)
{
QByteArray blob;
quint32 utmp;
d.readString(1, &m_id);
d.readBool(2, &m_enabled, true);
d.readString(3, &m_label);
d.readU32(4, &utmp);
m_color = utmp;
return true;
}
else
{
return false;
}
}
QDataStream& operator<<(QDataStream& out, const SIDSettings::ChannelSettings& settings)
{
out << settings.serialize();
return out;
}
QDataStream& operator>>(QDataStream& in, SIDSettings::ChannelSettings& settings)
{
QByteArray data;
in >> data;
settings.deserialize(data);
return in;
}

View File

@ -0,0 +1,115 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023-2024 Jon Beniston, M7RCE //
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SIDSETTINGS_H_
#define INCLUDE_FEATURE_SIDSETTINGS_H_
#include <QByteArray>
#include <QString>
#include <QDateTime>
#include <QColor>
#include "util/message.h"
class Serializable;
struct SIDSettings
{
struct ChannelSettings
{
QString m_id;
bool m_enabled;
QColor m_color;
QString m_label;
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
};
QList<ChannelSettings> m_channelSettings; // Channels to record power from
float m_period; // Mesaurement period, in seconds
bool m_autosave;
bool m_autoload;
QString m_filename; // Filename for autosave
int m_autosavePeriod; // In minutes
int m_samples; // Number of samples in average
bool m_autoscaleX;
bool m_autoscaleY;
bool m_separateCharts;
bool m_displayLegend;
Qt::Alignment m_legendAlignment;
bool m_displayAxisTitles;
bool m_displaySecondaryAxis;
bool m_plotXRayLongPrimary;
bool m_plotXRayLongSecondary;
bool m_plotXRayShortPrimary;
bool m_plotXRayShortSecondary;
bool m_plotGRB;
bool m_plotSTIX;
bool m_plotProton;
QDateTime m_startDateTime;
QDateTime m_endDateTime;
float m_y1Min;
float m_y1Max;
QList<QRgb> m_xrayShortColors;
QList<QRgb> m_xrayLongColors;
QList<QRgb> m_protonColors;
QRgb m_grbColor;
QRgb m_stixColor;
bool m_sdoEnabled;
bool m_sdoVideoEnabled;
QString m_sdoData;
bool m_sdoNow;
QDateTime m_sdoDateTime;
QString m_map; // 3D map Id to send date/time to
QList<int> m_sdoSplitterSizes;
QList<int> m_chartSplitterSizes;
QString m_title;
quint32 m_rgbColor;
bool m_useReverseAPI;
QString m_reverseAPIAddress;
uint16_t m_reverseAPIPort;
uint16_t m_reverseAPIFeatureSetIndex;
uint16_t m_reverseAPIFeatureIndex;
Serializable *m_rollupState;
int m_workspaceIndex;
QByteArray m_geometryBytes;
SIDSettings();
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
void setRollupState(Serializable *rollupState) { m_rollupState = rollupState; }
void applySettings(const QStringList& settingsKeys, const SIDSettings& settings);
QString getDebugString(const QStringList& settingsKeys, bool force=false) const;
ChannelSettings *getChannelSettings(const QString& id);
static const QList<QRgb> m_defaultColors;
static const QList<QRgb> m_defaultXRayShortColors;
static const QList<QRgb> m_defaultXRayLongColors;
static const QList<QRgb> m_defaultProtonColors;
static const QRgb m_defaultGRBColor;
static const QRgb m_defaultSTIXColor;
};
#endif // INCLUDE_FEATURE_SIDSETTINGS_H_

View File

@ -0,0 +1,214 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023-2024 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "util/units.h"
#include "device/deviceset.h"
#include "device/deviceapi.h"
#include "channel/channelwebapiutils.h"
#include "gui/colordialog.h"
#include "gui/tablecolorchooser.h"
#include "maincore.h"
#include "sidsettingsdialog.h"
SIDSettingsDialog::SIDSettingsDialog(SIDSettings *settings, QWidget* parent) :
QDialog(parent),
ui(new Ui::SIDSettingsDialog),
m_settings(settings),
m_fileDialog(nullptr, "Select file to write autosave CSV data to", "", "*.csv")
{
ui->setupUi(this);
ui->period->setValue(m_settings->m_period);
ui->autosave->setChecked(m_settings->m_autosave);
ui->autoload->setChecked(m_settings->m_autoload);
ui->filename->setText(m_settings->m_filename);
ui->autosavePeriod->setValue(m_settings->m_autosavePeriod);
switch (m_settings->m_legendAlignment) {
case Qt::AlignTop:
ui->legendAlignment->setCurrentIndex(0);
break;
case Qt::AlignRight:
ui->legendAlignment->setCurrentIndex(1);
break;
case Qt::AlignBottom:
ui->legendAlignment->setCurrentIndex(2);
break;
case Qt::AlignLeft:
ui->legendAlignment->setCurrentIndex(3);
break;
}
ui->displayAxisTitles->setChecked(m_settings->m_displayAxisTitles);
ui->displaySecondaryAxis->setChecked(m_settings->m_displaySecondaryAxis);
QStringList ids;
QStringList titles;
getChannels(ids, titles);
// Create settings for channels we don't currently have settings for
for (int i = 0; i < ids.size(); i++)
{
SIDSettings::ChannelSettings *channelSettings = m_settings->getChannelSettings(ids[i]);
if (!channelSettings)
{
SIDSettings::ChannelSettings newSettings;
newSettings.m_id = ids[i];
newSettings.m_enabled = true;
newSettings.m_label = titles[i];
newSettings.m_color = SIDSettings::m_defaultColors[i % SIDSettings::m_defaultColors.size()];
m_settings->m_channelSettings.append(newSettings);
}
}
// Add settings to table
for (int i = 0; i < m_settings->m_channelSettings.size(); i++)
{
SIDSettings::ChannelSettings *channelSettings = &m_settings->m_channelSettings[i];
int row = ui->channels->rowCount();
ui->channels->setRowCount(row+1);
ui->channels->setItem(row, CHANNELS_COL_ID, new QTableWidgetItem(channelSettings->m_id));
QTableWidgetItem *enableItem = new QTableWidgetItem();
enableItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
enableItem->setCheckState(channelSettings->m_enabled ? Qt::Checked : Qt::Unchecked);
ui->channels->setItem(row, CHANNELS_COL_ENABLED, enableItem);
ui->channels->setItem(row, CHANNELS_COL_LABEL, new QTableWidgetItem(channelSettings->m_label));
TableColorChooser *colorGUI = new TableColorChooser(ui->channels, row, CHANNELS_COL_COLOR, false, channelSettings->m_color.rgba());
m_channelColorGUIs.append(colorGUI);
}
ui->channels->resizeColumnsToContents();
addColor("Primary Long X-Ray", m_settings->m_xrayLongColors[0]);
addColor("Secondary Long X-Ray", m_settings->m_xrayLongColors[1]);
addColor("Primary Short X-Ray ", m_settings->m_xrayShortColors[0]);
addColor("Secondary Short X-Ray", m_settings->m_xrayShortColors[1]);
addColor("GRB", m_settings->m_grbColor);
addColor("STIX", m_settings->m_stixColor);
addColor("10 MeV Proton", m_settings->m_protonColors[0]);
addColor("100 MeV Proton", m_settings->m_protonColors[2]);
ui->colors->resizeColumnsToContents();
}
void SIDSettingsDialog::addColor(const QString& name, QRgb rgb)
{
int row = ui->colors->rowCount();
ui->colors->setRowCount(row+1);
ui->colors->setItem(row, COLORS_COL_NAME, new QTableWidgetItem(name));
TableColorChooser *colorGUI = new TableColorChooser(ui->colors, row, COLORS_COL_COLOR, false, rgb);
m_colorGUIs.append(colorGUI);
}
SIDSettingsDialog::~SIDSettingsDialog()
{
delete ui;
qDeleteAll(m_channelColorGUIs);
qDeleteAll(m_colorGUIs);
}
// Get channels that have channelPowerDB value in their report
void SIDSettingsDialog::getChannels(QStringList& ids, QStringList& titles)
{
MainCore *mainCore = MainCore::instance();
std::vector<DeviceSet*> deviceSets = mainCore->getDeviceSets();
for (unsigned int deviceSetIndex = 0; deviceSetIndex < deviceSets.size(); deviceSetIndex++)
{
DeviceSet *deviceSet = deviceSets[deviceSetIndex];
for (unsigned int channelIndex = 0; channelIndex < deviceSet->getNumberOfChannels(); channelIndex++)
{
QString title;
ChannelWebAPIUtils::getChannelSetting(deviceSetIndex, channelIndex, "title", title);
double power;
if (ChannelWebAPIUtils::getChannelReportValue(deviceSetIndex, channelIndex, "channelPowerDB", power))
{
ChannelAPI *channel = mainCore->getChannel(deviceSetIndex, channelIndex);
QString id = mainCore->getChannelId(channel);
ids.append(id);
titles.append(title);
}
}
}
}
void SIDSettingsDialog::accept()
{
m_settings->m_period = ui->period->value();
m_settings->m_autosave = ui->autosave->isChecked();
m_settings->m_autoload = ui->autoload->isChecked();
m_settings->m_filename = ui->filename->text();
m_settings->m_autosavePeriod = ui->autosavePeriod->value();
switch (ui->legendAlignment->currentIndex() ) {
case 0:
m_settings->m_legendAlignment = Qt::AlignTop;
break;
case 1:
m_settings->m_legendAlignment = Qt::AlignRight;
break;
case 2:
m_settings->m_legendAlignment = Qt::AlignBottom;
break;
case 3:
m_settings->m_legendAlignment = Qt::AlignLeft;
break;
}
m_settings->m_displayAxisTitles = ui->displayAxisTitles->isChecked();
m_settings->m_displaySecondaryAxis = ui->displaySecondaryAxis->isChecked();
for (int i = 0; i < m_settings->m_channelSettings.size(); i++)
{
SIDSettings::ChannelSettings *channelSettings = &m_settings->m_channelSettings[i];
channelSettings->m_id = ui->channels->item(i, CHANNELS_COL_ID)->text();
channelSettings->m_enabled = ui->channels->item(i, CHANNELS_COL_ENABLED)->checkState() == Qt::Checked;
channelSettings->m_label = ui->channels->item(i, CHANNELS_COL_LABEL)->text();
channelSettings->m_color = m_channelColorGUIs[i]->m_color;
}
m_settings->m_xrayLongColors[0] = m_colorGUIs[0]->m_color;
m_settings->m_xrayLongColors[1] = m_colorGUIs[1]->m_color;
m_settings->m_xrayShortColors[0] = m_colorGUIs[2]->m_color;
m_settings->m_xrayShortColors[1] = m_colorGUIs[3]->m_color;
m_settings->m_grbColor = m_colorGUIs[4]->m_color;
m_settings->m_stixColor = m_colorGUIs[5]->m_color;
m_settings->m_protonColors[0] = m_colorGUIs[6]->m_color;
m_settings->m_protonColors[2] = m_colorGUIs[7]->m_color;
QDialog::accept();
}
void SIDSettingsDialog::on_browse_clicked()
{
m_fileDialog.setAcceptMode(QFileDialog::AcceptSave);
if (m_fileDialog.exec())
{
QStringList fileNames = m_fileDialog.selectedFiles();
if (fileNames.size() > 0) {
ui->filename->setText(fileNames[0]);
}
}
}

View File

@ -0,0 +1,63 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SIDSETTINGSDIALOG_H
#define INCLUDE_SIDSETTINGSDIALOG_H
#include <QFileDialog>
#include "ui_sidsettingsdialog.h"
#include "sidsettings.h"
class TableColorChooser;
class SIDSettingsDialog : public QDialog {
Q_OBJECT
public:
explicit SIDSettingsDialog(SIDSettings *settings, QWidget* parent = 0);
~SIDSettingsDialog();
private:
void getChannels(QStringList& ids, QStringList& titles);
void addColor(const QString& name, QRgb rgb);
private slots:
void accept();
void on_browse_clicked();
private:
Ui::SIDSettingsDialog* ui;
SIDSettings *m_settings;
QList<TableColorChooser *> m_channelColorGUIs;
QList<TableColorChooser *> m_colorGUIs;
QFileDialog m_fileDialog;
enum ChannelsRows {
CHANNELS_COL_ID,
CHANNELS_COL_ENABLED,
CHANNELS_COL_LABEL,
CHANNELS_COL_COLOR
};
enum ColorsRows {
COLORS_COL_NAME,
COLORS_COL_COLOR
};
};
#endif // INCLUDE_SIDSETTINGSDIALOG_H

View File

@ -0,0 +1,338 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SIDSettingsDialog</class>
<widget class="QDialog" name="SIDSettingsDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>441</width>
<height>800</height>
</rect>
</property>
<property name="font">
<font>
<family>Liberation Sans</family>
<pointsize>9</pointsize>
</font>
</property>
<property name="windowTitle">
<string>APRS Settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Data</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="channelsLabel">
<property name="text">
<string>Channels to record power from:</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QTableWidget" name="channels">
<property name="selectionMode">
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<attribute name="verticalHeaderStretchLastSection">
<bool>true</bool>
</attribute>
<column>
<property name="text">
<string>ID</string>
</property>
</column>
<column>
<property name="text">
<string>Enabled</string>
</property>
</column>
<column>
<property name="text">
<string>Label</string>
</property>
</column>
<column>
<property name="text">
<string>Colour</string>
</property>
</column>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="periodLabel">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Measurement period (s)</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="period">
<property name="toolTip">
<string>Specifies the time period in seconds between each power measurement</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.001000000000000</double>
</property>
<property name="maximum">
<double>100000.000000000000000</double>
</property>
<property name="value">
<double>10.000000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="autosaveGroup">
<property name="title">
<string>Autosave</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="4" column="0">
<widget class="QLabel" name="autosavePeriodLabel">
<property name="text">
<string>Autosave Period (min)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QCheckBox" name="autosave">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="autoloadLabel">
<property name="text">
<string>Autoload</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="filenameLabel">
<property name="text">
<string>Autosave filename</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="filename"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="autosaveLabel">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Autosave</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="autosavePeriod">
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.100000000000000</double>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="value">
<double>10.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="autoload">
<property name="toolTip">
<string>Automatically load last autosave data</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QToolButton" name="browse">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="chartsGroup">
<property name="title">
<string>Charts</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="legendAlignmentLabel">
<property name="minimumSize">
<size>
<width>150</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>Legend Position</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="legendAlignment">
<property name="toolTip">
<string>Position of legend</string>
</property>
<item>
<property name="text">
<string>Top</string>
</property>
</item>
<item>
<property name="text">
<string>Right</string>
</property>
</item>
<item>
<property name="text">
<string>Bottom</string>
</property>
</item>
<item>
<property name="text">
<string>Left</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="displayAxisTitlesLabel">
<property name="text">
<string>Display axis titles</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QCheckBox" name="displayAxisTitles">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="displaySecondaryAxisLabel">
<property name="text">
<string>Display secondary axis</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="displaySecondaryAxis">
<property name="toolTip">
<string>Whether to display secondary axis scale (E.g. for GRB / Proton flux on right of chart)</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="4" column="0" colspan="2">
<widget class="QTableWidget" name="colors">
<column>
<property name="text">
<string>Series</string>
</property>
</column>
<column>
<property name="text">
<string>Colour</string>
</property>
</column>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Colours:</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>SIDSettingsDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>257</x>
<y>31</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>SIDSettingsDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>325</x>
<y>31</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -0,0 +1,52 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include "SWGFeatureSettings.h"
#include "sid.h"
#include "sidwebapiadapter.h"
SIDWebAPIAdapter::SIDWebAPIAdapter()
{}
SIDWebAPIAdapter::~SIDWebAPIAdapter()
{}
int SIDWebAPIAdapter::webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) errorMessage;
response.setSidSettings(new SWGSDRangel::SWGSIDSettings());
response.getSidSettings()->init();
SIDMain::webapiFormatFeatureSettings(response, m_settings);
return 200;
}
int SIDWebAPIAdapter::webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage)
{
(void) force; // no action
(void) errorMessage;
SIDMain::webapiUpdateFeatureSettings(m_settings, featureSettingsKeys, response);
return 200;
}

View File

@ -0,0 +1,50 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_SID_WEBAPIADAPTER_H
#define INCLUDE_SID_WEBAPIADAPTER_H
#include "feature/featurewebapiadapter.h"
#include "sidsettings.h"
/**
* Standalone API adapter only for the settings
*/
class SIDWebAPIAdapter : public FeatureWebAPIAdapter {
public:
SIDWebAPIAdapter();
virtual ~SIDWebAPIAdapter();
virtual QByteArray serialize() const { return m_settings.serialize(); }
virtual bool deserialize(const QByteArray& data) { return m_settings.deserialize(data); }
virtual int webapiSettingsGet(
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
virtual int webapiSettingsPutPatch(
bool force,
const QStringList& featureSettingsKeys,
SWGSDRangel::SWGFeatureSettings& response,
QString& errorMessage);
private:
SIDSettings m_settings;
};
#endif // INCLUDE_SID_WEBAPIADAPTER_H

View File

@ -0,0 +1,153 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#include <QDebug>
#include "webapi/webapiadapterinterface.h"
#include "webapi/webapiutils.h"
#include "channel/channelwebapiutils.h"
#include "device/deviceset.h"
#include "device/deviceapi.h"
#include "maincore.h"
#include "sid.h"
#include "sidworker.h"
SIDWorker::SIDWorker(SIDMain *sid, WebAPIAdapterInterface *webAPIAdapterInterface) :
m_sid(sid),
m_webAPIAdapterInterface(webAPIAdapterInterface),
m_msgQueueToFeature(nullptr),
m_msgQueueToGUI(nullptr),
m_pollTimer(this)
{
}
SIDWorker::~SIDWorker()
{
stopWork();
m_inputMessageQueue.clear();
}
void SIDWorker::startWork()
{
qDebug("SIDWorker::startWork");
QMutexLocker mutexLocker(&m_mutex);
connect(&m_pollTimer, &QTimer::timeout, this, &SIDWorker::update);
m_pollTimer.start(1000);
connect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
// Handle any messages already on the queue
handleInputMessages();
}
void SIDWorker::stopWork()
{
qDebug("SIDWorker::stopWork");
QMutexLocker mutexLocker(&m_mutex);
disconnect(&m_inputMessageQueue, SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
m_pollTimer.stop();
disconnect(&m_pollTimer, &QTimer::timeout, this, &SIDWorker::update);
}
void SIDWorker::handleInputMessages()
{
Message* message;
while ((message = m_inputMessageQueue.pop()) != nullptr)
{
if (handleMessage(*message)) {
delete message;
}
}
}
bool SIDWorker::handleMessage(const Message& cmd)
{
if (SIDMain::MsgConfigureSID::match(cmd))
{
QMutexLocker mutexLocker(&m_mutex);
SIDMain::MsgConfigureSID& cfg = (SIDMain::MsgConfigureSID&) cmd;
applySettings(cfg.getSettings(), cfg.getSettingsKeys(), cfg.getForce());
return true;
}
else
{
return false;
}
}
void SIDWorker::applySettings(const SIDSettings& settings, const QList<QString>& settingsKeys, bool force)
{
qDebug() << "SIDWorker::applySettings:" << settings.getDebugString(settingsKeys, force) << force;
if (settingsKeys.contains("period") || force)
{
m_pollTimer.stop();
m_pollTimer.start(settings.m_period * 1000);
}
if (force) {
m_settings = settings;
} else {
m_settings.applySettings(settingsKeys, settings);
}
}
void SIDWorker::update()
{
// Get powers from each channel
QDateTime dateTime = QDateTime::currentDateTime();
for (const auto& channelSettings : m_settings.m_channelSettings)
{
if (channelSettings.m_enabled)
{
unsigned int deviceSetIndex, channelIndex;
if (MainCore::getDeviceAndChannelIndexFromId(channelSettings.m_id, deviceSetIndex, channelIndex))
{
// Check device is running
std::vector<DeviceSet*> deviceSets = MainCore::instance()->getDeviceSets();
if (deviceSetIndex < deviceSets.size())
{
DeviceSet *deviceSet = deviceSets[deviceSetIndex];
if (deviceSet && (deviceSet->m_deviceAPI->state() == DeviceAPI::StRunning))
{
double power;
if (ChannelWebAPIUtils::getChannelReportValue(deviceSetIndex, channelIndex, "channelPowerDB", power))
{
if (getMessageQueueToGUI())
{
SIDMain::MsgMeasurement *msgToGUI = SIDMain::MsgMeasurement::create(dateTime, channelSettings.m_id, power);
getMessageQueueToGUI()->push(msgToGUI);
}
}
else
{
qDebug() << "SIDWorker::update: Failed to get power for channel " << channelSettings.m_id;
}
}
}
}
else
{
qDebug() << "SIDWorker::update: Malformed channel id: " << channelSettings.m_id;
}
}
}
}

View File

@ -0,0 +1,67 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2023 Jon Beniston, M7RCE //
// Copyright (C) 2020 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 <http://www.gnu.org/licenses/>. //
///////////////////////////////////////////////////////////////////////////////////
#ifndef INCLUDE_FEATURE_SIDWORKER_H_
#define INCLUDE_FEATURE_SIDWORKER_H_
#include <QObject>
#include <QTimer>
#include "util/message.h"
#include "util/messagequeue.h"
#include "sid.h"
#include "sidsettings.h"
class WebAPIAdapterInterface;
class SIDMain;
class SIDWorker : public QObject
{
Q_OBJECT
public:
SIDWorker(SIDMain *m_sid, WebAPIAdapterInterface *webAPIAdapterInterface);
~SIDWorker();
void startWork();
void stopWork();
MessageQueue *getInputMessageQueue() { return &m_inputMessageQueue; }
void setMessageQueueToFeature(MessageQueue *messageQueue) { m_msgQueueToFeature = messageQueue; }
void setMessageQueueToGUI(MessageQueue *messageQueue) { m_msgQueueToGUI = messageQueue; }
private:
SIDMain *m_sid;
WebAPIAdapterInterface *m_webAPIAdapterInterface;
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
MessageQueue *m_msgQueueToFeature; //!< Queue to report channel change to main feature object
MessageQueue *m_msgQueueToGUI;
SIDSettings m_settings;
QRecursiveMutex m_mutex;
QTimer m_pollTimer;
bool handleMessage(const Message& cmd);
void applySettings(const SIDSettings& settings, const QList<QString>& settingsKeys, bool force = false);
MessageQueue *getMessageQueueToGUI() { return m_msgQueueToGUI; }
private slots:
void handleInputMessages();
void update();
};
#endif // INCLUDE_FEATURE_SIDWORKER_H_

View File

@ -1440,13 +1440,13 @@ bool ChannelWebAPIUtils::patchFeatureSetting(unsigned int featureSetIndex, unsig
if (httpRC/100 == 2)
{
qDebug("ChannelWebAPIUtils::patchFeatureSetting: set feature setting %s to %s OK", qPrintable(setting), value.toVariantList());
qDebug("ChannelWebAPIUtils::patchFeatureSetting: set feature setting %s OK", qPrintable(setting));
return true;
}
else
{
qWarning("ChannelWebAPIUtils::patchFeatureSetting: set feature setting %s to %s error %d: %s",
qPrintable(setting), value.toVariantList(), httpRC, qPrintable(*errorResponse2.getMessage()));
qWarning("ChannelWebAPIUtils::patchFeatureSetting: set feature setting %s error %d: %s",
qPrintable(setting), httpRC, qPrintable(*errorResponse2.getMessage()));
return false;
}
}

View File

@ -5280,6 +5280,11 @@ bool WebAPIRequestMapper::getFeatureSettings(
featureSettings->getSatelliteTrackerSettings()->init();
featureSettings->getSatelliteTrackerSettings()->fromJsonObject(settingsJsonObject);
}
else if (featureSettingsKey == "SIDSettings")
{
featureSettings->setSidSettings(new SWGSDRangel::SWGSIDSettings());
featureSettings->getSidSettings()->fromJsonObject(settingsJsonObject);
}
else if (featureSettingsKey == "SimplePTTSettings")
{
featureSettings->setSimplePttSettings(new SWGSDRangel::SWGSimplePTTSettings());
@ -5648,6 +5653,7 @@ void WebAPIRequestMapper::resetFeatureSettings(SWGSDRangel::SWGFeatureSettings&
featureSettings.setMapSettings(nullptr);
featureSettings.setPerTesterSettings(nullptr);
featureSettings.setSatelliteTrackerSettings(nullptr);
featureSettings.setSidSettings(nullptr);
featureSettings.setSimplePttSettings(nullptr);
featureSettings.setSkyMapSettings(nullptr);
featureSettings.setStarTrackerSettings(nullptr);

View File

@ -323,6 +323,7 @@ const QMap<QString, QString> WebAPIUtils::m_featureTypeToSettingsKey = {
{"Radiosonde", "RadiosondeSettings"},
{"RigCtlServer", "RigCtlServerSettings"},
{"SatelliteTracker", "SatelliteTrackerSettings"},
{"SID", "SIDSettings"},
{"SimplePTT", "SimplePTTSettings"},
{"SkyMap", "SkyMapSettings"},
{"StarTracker", "StarTrackerSettings"},