diff --git a/.gitignore b/.gitignore index 55516db49..9aa9d4f54 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ obj-x86_64-linux-gnu/* /rescuesdriq/vendor/ /rescuesdriq/Godeps/ +/.vs diff --git a/CHANGELOG b/CHANGELOG index e780728b9..8f5453b52 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,17 @@ +sdrangel (7.15.4-1) unstable; urgency=medium + + * Fix Mac compilation. PR #1786 + * Add support for plugin presets. PR #1789 + * Map feature updates. PR #1778 + * Fix RTLSDR E4000 gain and bandwidth settings. Add tuner type to GUI. PR #1790 + * Add support for RTLSDRBlog V4 with HF upsampler. PR #1790 + * Update RTLSDR driver to include RTLSDRBlog V4 support. PR #1790 + * Add rotator az/el and offset to table. PR #1791 + * Optmize redrawing of charts in Star Tracker. PR #1791 + * Initialise PhaseDiscriminators state, to avoid outputting huge values. Fix #1794. PR #1794 + + -- Edouard Griffiths, F4EXB Sat, 02 Sep 2023 19:12:12 +0200 + sdrangel (7.15.3-1) unstable; urgency=medium * Rotator Controller: Add additional gamepad calibration and functionality. PR #1761 diff --git a/CMakeLists.txt b/CMakeLists.txt index d02fea41f..a2d05068d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # configure version set(sdrangel_VERSION_MAJOR "7") set(sdrangel_VERSION_MINOR "15") -set(sdrangel_VERSION_PATCH "3") +set(sdrangel_VERSION_PATCH "4") set(sdrangel_VERSION_SUFFIX "") # SDRAngel cmake options diff --git a/CMakePresets.json b/CMakePresets.json index e6eefbfae..47d1bc4c4 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,56 +1,94 @@ { "version": 3, "configurePresets": [ - { - "name": "default", - "binaryDir": "build-default", - "cacheVariables": { - "DEBUG_OUTPUT": "ON", - "AIRSPYHF_DIR": "/opt/install/libairspyhf", - "AIRSPY_DIR": "/opt/install/libairspy", - "APT_DIR": "/opt/install/aptdec", - "BLADERF_DIR": "/opt/install/libbladeRF", - "CM256CC_DIR": "/opt/install/cm256cc", - "CODEC2_DIR": "/opt/install/codec2", - "DAB_DIR": "/opt/install/libdab", - "DSDCC_DIR": "/opt/install/dsdcc", - "HACKRF_DIR": "/opt/install/libhackrf", - "HAMLIB_DIR": "/opt/build/hamlib-prefix", - "IIO_DIR": "/opt/install/libiio", - "LIBSIGMF_DIR": "/opt/install/libsigmf", - "LIMESUITE_DIR": "/opt/install/LimeSuite", - "MBE_DIR": "/opt/install/mbelib", - "MIRISDR_DIR": "/opt/install/libmirisdr", - "PERSEUS_DIR": "/opt/install/libperseus", - "RTLSDR_DIR": "/opt/install/librtlsdr", - "SERIALDV_DIR": "/opt/install/serialdv", - "SGP4_DIR": "/opt/install/sgp4", - "SOAPYSDR_DIR": "/opt/install/SoapySDR", - "UHD_DIR": "/opt/install/uhd", - "XTRX_DIR": "/opt/install/xtrx-images", - "CMAKE_INSTALL_PREFIX": "/opt/install/sdrangel" - }, - "warnings": { - "dev": false - } + { + "name": "default", + "binaryDir": "build-default", + "cacheVariables": { + "DEBUG_OUTPUT": "ON", + "AIRSPYHF_DIR": "/opt/install/libairspyhf", + "AIRSPY_DIR": "/opt/install/libairspy", + "APT_DIR": "/opt/install/aptdec", + "BLADERF_DIR": "/opt/install/libbladeRF", + "CM256CC_DIR": "/opt/install/cm256cc", + "CODEC2_DIR": "/opt/install/codec2", + "DAB_DIR": "/opt/install/libdab", + "DSDCC_DIR": "/opt/install/dsdcc", + "HACKRF_DIR": "/opt/install/libhackrf", + "HAMLIB_DIR": "/opt/build/hamlib-prefix", + "IIO_DIR": "/opt/install/libiio", + "LIBSIGMF_DIR": "/opt/install/libsigmf", + "LIMESUITE_DIR": "/opt/install/LimeSuite", + "MBE_DIR": "/opt/install/mbelib", + "MIRISDR_DIR": "/opt/install/libmirisdr", + "PERSEUS_DIR": "/opt/install/libperseus", + "RTLSDR_DIR": "/opt/install/librtlsdr", + "SERIALDV_DIR": "/opt/install/serialdv", + "SGP4_DIR": "/opt/install/sgp4", + "SOAPYSDR_DIR": "/opt/install/SoapySDR", + "UHD_DIR": "/opt/install/uhd", + "XTRX_DIR": "/opt/install/xtrx-images", + "CMAKE_INSTALL_PREFIX": "/opt/install/sdrangel" }, - { - "name": "default-qt6", - "inherits": "default", - "binaryDir": "build-qt6", - "cacheVariables": { - "ENABLE_QT6": "ON" - } + "warnings": { + "dev": false + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "hostOS": [ "Linux" ] + } } + }, + { + // Don't inherit from "default", as we don't want UHD_DIR etc set + "name": "default-windows", + "binaryDir": "c:/users/jon/source/repos/sdrangel/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "DEBUG_OUTPUT": "ON", + "RX_SAMPLE_24BIT": "ON", + "DARCH_OPT": "SSE4_1", + "HIDE_CONSOLE": "OFF", + "ENABLE_AIRSPY": "ON", + "ENABLE_BLADERF": "OFF", + "ENABLE_HACKRF": "OFF", + "ENABLE_IIO": "OFF", + "ENABLE_MIRISDR": "OFF", + "ENABLE_PERSEUS": "OFF", + "ENABLE_XTRX": "OFF", + "BUILD_SERVER": "OFF", + "CMAKE_PREFIX_PATH": "C:/Qt/5.15.2/msvc2019_64;C:/Applications/boost_1_81_0" + }, + "warnings": { + "dev": false + }, + "vendor": { + "microsoft.com/VisualStudioSettings/CMake/1.0": { + "hostOS": [ "Windows" ] + } + } + }, + { + "name": "default-qt6", + "inherits": "default", + "binaryDir": "build-qt6", + "cacheVariables": { + "ENABLE_QT6": "ON" + } + } ], - "buildPresets": [ - { - "name": "default", - "configurePreset": "default" - }, - { - "name": "default-qt6", - "configurePreset": "default-qt6" - } - ] + "buildPresets": [ + { + "name": "default", + "configurePreset": "default" + }, + { + "name": "default-windows", + "configurePreset": "default-windows" + }, + { + "name": "default-qt6", + "configurePreset": "default-qt6" + } + ] } diff --git a/debian/changelog b/debian/changelog index 9d88b9936..f48256f79 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,17 @@ +sdrangel (7.15.4-1) unstable; urgency=medium + + * Fix Mac compilation. PR #1786 + * Add support for plugin presets. PR #1789 + * Map feature updates. PR #1778 + * Fix RTLSDR E4000 gain and bandwidth settings. Add tuner type to GUI. PR #1790 + * Add support for RTLSDRBlog V4 with HF upsampler. PR #1790 + * Update RTLSDR driver to include RTLSDRBlog V4 support. PR #1790 + * Add rotator az/el and offset to table. PR #1791 + * Optmize redrawing of charts in Star Tracker. PR #1791 + * Initialise PhaseDiscriminators state, to avoid outputting huge values. Fix #1794. PR #1794 + + -- Edouard Griffiths, F4EXB Sat, 02 Sep 2023 19:12:12 +0200 + sdrangel (7.15.3-1) unstable; urgency=medium * Rotator Controller: Add additional gamepad calibration and functionality. PR #1761 diff --git a/plugins/channelrx/demoddsc/dscdemodsink.cpp b/plugins/channelrx/demoddsc/dscdemodsink.cpp index 695dcab4f..0eae78418 100644 --- a/plugins/channelrx/demoddsc/dscdemodsink.cpp +++ b/plugins/channelrx/demoddsc/dscdemodsink.cpp @@ -227,6 +227,8 @@ void DSCDemodSink::receiveBit(bool bit) m_dscDecoder.init(m_phasingPatterns[i].m_offset); m_gotSOP = true; m_bitCount = 0; + m_rssiMagSqSum = 0.0; + m_rssiMagSqCount = 0; break; } } diff --git a/plugins/channelrx/demodnavtex/navtexdemodsink.cpp b/plugins/channelrx/demodnavtex/navtexdemodsink.cpp index eac89e944..47b3e42d5 100644 --- a/plugins/channelrx/demodnavtex/navtexdemodsink.cpp +++ b/plugins/channelrx/demodnavtex/navtexdemodsink.cpp @@ -329,6 +329,8 @@ void NavtexDemodSink::receiveBit(bool bit) m_gotSOP = true; m_bitCount = 0; m_sitorBDecoder.init(); + m_rssiMagSqSum = 0.0; + m_rssiMagSqCount = 0; } else { diff --git a/plugins/channelrx/demodrtty/readme.md b/plugins/channelrx/demodrtty/readme.md index 8a56457f8..27232fadc 100644 --- a/plugins/channelrx/demodrtty/readme.md +++ b/plugins/channelrx/demodrtty/readme.md @@ -79,7 +79,12 @@ Specifies whether bits are transmitted least-significant-bit first (LSB) or most

14: Mark/Space Frequency

-When unchecked, the mark frequency is the higher frequency, when checked the space frequency is higher. +When unchecked, the mark frequency is the higher RF frequency, when checked the space frequency is higher. + +This should be unchecked when transmitter is using LSB AFSK and checked for USB AFSK and DWD +[1](https://www.dwd.de/EN/specialusers/shipping/broadcast_en/brodcast_rtty_1_052014.pdf?__blob=publicationFile&v=1) +[2](https://www.dwd.de/EN/specialusers/shipping/broadcast_en/broadcast_rtty_2_052014.pdf?__blob=publicationFile&v=1) +shipping weather broadcasts.

15: Suppress CR LF

@@ -101,3 +106,4 @@ Click to specify the name of the .txt file which received characters are logged The received text area shows characters as they are received. +Holding the cursor over an acronym may show a tooltip with the decoded acronym. diff --git a/plugins/channelrx/demodrtty/rttydemodgui.cpp b/plugins/channelrx/demodrtty/rttydemodgui.cpp index 57fda37c2..5945a1304 100644 --- a/plugins/channelrx/demodrtty/rttydemodgui.cpp +++ b/plugins/channelrx/demodrtty/rttydemodgui.cpp @@ -34,6 +34,7 @@ #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "util/db.h" +#include "util/rtty.h" #include "gui/basicchannelsettingsdialog.h" #include "gui/devicestreamselectiondialog.h" #include "dsp/dspengine.h" @@ -482,6 +483,8 @@ RttyDemodGUI::RttyDemodGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, Baseb connect(&m_channelMarker, SIGNAL(highlightedByCursor()), this, SLOT(channelMarkerHighlightedByCursor())); connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages())); + ui->text->addAcronyms(Rtty::m_acronyms); + ui->scopeContainer->setVisible(false); // Hide developer only settings diff --git a/plugins/channelrx/demodrtty/rttydemodgui.ui b/plugins/channelrx/demodrtty/rttydemodgui.ui index a70e366f5..8ac4000d9 100644 --- a/plugins/channelrx/demodrtty/rttydemodgui.ui +++ b/plugins/channelrx/demodrtty/rttydemodgui.ui @@ -916,7 +916,7 @@ - + Received text @@ -1293,6 +1293,12 @@
gui/glscopegui.h
1 + + AcronymView + QTextEdit +
gui/acronymview.h
+ 1 +
deltaFrequency diff --git a/plugins/channelrx/demodrtty/rttydemodsink.cpp b/plugins/channelrx/demodrtty/rttydemodsink.cpp index 2f987994d..8e8d70fc5 100644 --- a/plugins/channelrx/demodrtty/rttydemodsink.cpp +++ b/plugins/channelrx/demodrtty/rttydemodsink.cpp @@ -154,8 +154,8 @@ void RttyDemodSink::processOneSample(Complex &ci) m_expIdx = (m_expIdx + 1) % m_expLength; //Complex exp = m_exp[m_sampleIdx]; //qDebug() << "IQ " << real(ci) << imag(ci); - Complex corr1 = ci * exp; - Complex corr2 = ci * std::conj(exp); + Complex corr1 = ci * std::conj(exp); // Conj is high/mark freq (as for matched filter, we need to time reverse and take conjugate) + Complex corr2 = ci * exp; // Low/space freq // Filter Real abs1, abs2; @@ -238,6 +238,8 @@ void RttyDemodSink::processOneSample(Complex &ci) m_clockCount = 0; m_clock = false; m_cycleCount = 0; + m_rssiMagSqSum = 0.0; + m_rssiMagSqCount = 0; } } else diff --git a/plugins/channeltx/modpacket/packetmodsource.cpp b/plugins/channeltx/modpacket/packetmodsource.cpp index b08d57b12..7cb5465ec 100644 --- a/plugins/channeltx/modpacket/packetmodsource.cpp +++ b/plugins/channeltx/modpacket/packetmodsource.cpp @@ -349,7 +349,7 @@ void PacketModSource::applySettings(const PacketModSettings& settings, bool forc << " symbolSpan: " << settings.m_symbolSpan << " channelSampleRate:" << m_channelSampleRate << " baud:" << settings.m_baud; - m_pulseShape.create(settings.m_beta, m_settings.m_symbolSpan, m_channelSampleRate/settings.m_baud); + m_pulseShape.create(settings.m_beta, settings.m_symbolSpan, m_channelSampleRate/settings.m_baud); } if ((settings.m_polynomial != m_settings.m_polynomial) || force) m_scrambler.setPolynomial(settings.m_polynomial); diff --git a/plugins/channeltx/modrtty/readme.md b/plugins/channeltx/modrtty/readme.md index bdde25242..cf267e4bd 100644 --- a/plugins/channeltx/modrtty/readme.md +++ b/plugins/channeltx/modrtty/readme.md @@ -2,7 +2,10 @@

Introduction

-This plugin can be used to transmit RTTY encoded text. +This plugin can be used to modulate RTTY (Radioteletype) encoded text. +RTTY uses BFSK (Binary Frequency Shift Keying), where transmission of data alternates between two frequencies, +the mark frequency and the space frequency. The RTTY Modulator should be centered in between these frequencies. +The baud rate, frequency shift (difference between mark and space frequencies), filter bandwidth and baudot character set are configurable.

Interface

@@ -64,7 +67,14 @@ UDP port number to receive text to be transmitted on.

13: Baudot Character Set

-Specifies the Baudot character set used to encode the text to transmit. +Specifies the Baudot character set used to encode the text to transmit. The following character sets are supported: + +* ITA 2 +* UK +* European +* US +* Russian +* Murray

14: Bit Ordering

@@ -90,11 +100,26 @@ Press to clear the transmitted text. Enter text to transmit. Pressing return will transmit the text and clear this field. Press the arrow to display and select a list of pre-defined text or previously transmitted text to enter in to the field. +The list of pre-defined text can be customised via the Transmit Settings dialog (20). +

20: TX

-Press to transmits the current text. The text field will not be cleared. +Press to transmit the current text. The text field will not be cleared. -Right click to open a dialog to adjust additional transmitter settings. +Right click to open a dialog to adjust additional Transmit Settings, including the list of pre-defined text. + +Predefined text supports the following variable substitutions: + +* ${callsign} - Gets replaced with the station name from Preferences > My Position +* ${location} = Gets replaced with the Maidenhead locator for the position specified under Preferences > My Position + +The substitutions are applied when the Transmit Settings dialog is closed. + +

21: Transmitted Text

+ +The trasnmitted text area shows characters as they are transmitted. + +Holding the cursor over an acronym may show a tooltip with the decoded acronym.

API

diff --git a/plugins/channeltx/modrtty/rttymodgui.cpp b/plugins/channeltx/modrtty/rttymodgui.cpp index dc5550b7a..f1fc898ea 100644 --- a/plugins/channeltx/modrtty/rttymodgui.cpp +++ b/plugins/channeltx/modrtty/rttymodgui.cpp @@ -31,6 +31,7 @@ #include "plugin/pluginapi.h" #include "util/simpleserializer.h" #include "util/db.h" +#include "util/rtty.h" #include "util/maidenhead.h" #include "gui/glspectrum.h" #include "gui/crightclickenabler.h" @@ -476,6 +477,8 @@ RttyModGUI::RttyModGUI(PluginAPI* pluginAPI, DeviceUISet *deviceUISet, BasebandS m_settings.setChannelMarker(&m_channelMarker); m_settings.setRollupState(&m_rollupState); + ui->transmittedText->addAcronyms(Rtty::m_acronyms); + ui->spectrumContainer->setVisible(false); displaySettings(); diff --git a/plugins/channeltx/modrtty/rttymodgui.ui b/plugins/channeltx/modrtty/rttymodgui.ui index d418d5b66..b7fee75f8 100644 --- a/plugins/channeltx/modrtty/rttymodgui.ui +++ b/plugins/channeltx/modrtty/rttymodgui.ui @@ -751,7 +751,7 @@
- + Qt::Horizontal @@ -854,11 +854,7 @@ 3 - - - true - - +
@@ -960,6 +956,12 @@
gui/levelmeter.h
1 + + AcronymView + QTextEdit +
gui/acronymview.h
+ 1 +
deltaFrequency diff --git a/plugins/channeltx/modrtty/rttymodsettings.cpp b/plugins/channeltx/modrtty/rttymodsettings.cpp index 5a81beb29..d6682c26a 100644 --- a/plugins/channeltx/modrtty/rttymodsettings.cpp +++ b/plugins/channeltx/modrtty/rttymodsettings.cpp @@ -57,7 +57,7 @@ void RttyModSettings::resetToDefaults() "UR 599 QTH IS ${location}", "TU DE ${callsign} CQ" }); - m_rgbColor = QColor(0, 105, 2).rgb(); + m_rgbColor = QColor(180, 205, 130).rgb(); m_title = "RTTY Modulator"; m_streamIndex = 0; m_useReverseAPI = false; @@ -66,7 +66,7 @@ void RttyModSettings::resetToDefaults() m_reverseAPIDeviceIndex = 0; m_reverseAPIChannelIndex = 0; m_pulseShaping = false; - m_beta = 0.5f; + m_beta = 1.0f; m_symbolSpan = 6; m_udpEnabled = false; m_udpAddress = "127.0.0.1"; @@ -200,7 +200,7 @@ bool RttyModSettings::deserialize(const QByteArray& data) d.readU32(39, &utmp, 0); m_reverseAPIChannelIndex = utmp > 99 ? 99 : utmp; d.readBool(46, &m_pulseShaping, false); - d.readReal(47, &m_beta, 0.5f); + d.readReal(47, &m_beta, 1.0f); d.readS32(48, &m_symbolSpan, 6); d.readBool(51, &m_udpEnabled); d.readString(52, &m_udpAddress, "127.0.0.1"); diff --git a/plugins/channeltx/modrtty/rttymodsource.cpp b/plugins/channeltx/modrtty/rttymodsource.cpp index 8080ddb90..a42ce7a87 100644 --- a/plugins/channeltx/modrtty/rttymodsource.cpp +++ b/plugins/channeltx/modrtty/rttymodsource.cpp @@ -43,7 +43,7 @@ RttyModSource::RttyModSource() : { m_bits.append(0); m_lowpass.create(301, m_channelSampleRate, 400.0 / 2.0); - m_pulseShape.create(0.5, 6, m_channelSampleRate/45.45); + m_pulseShape.create(0.5, 6, m_channelSampleRate / 45.45, true); m_demodBuffer.resize(1<<12); m_demodBufferFill = 0; @@ -121,7 +121,7 @@ void RttyModSource::sampleToSpectrum(Complex sample) void RttyModSource::modulateSample() { - Real audioMod; + Real mod; if (m_sampleIdx == 0) { @@ -154,18 +154,18 @@ void RttyModSource::modulateSample() if (m_settings.m_pulseShaping) { if (m_sampleIdx == 1) { - audioMod = m_pulseShape.filter(m_bit ? 1.0f : -1.0f); + mod = m_pulseShape.filter(m_bit ? 1.0f : -1.0f); } else { - audioMod = m_pulseShape.filter(0.0f); + mod = m_pulseShape.filter(0.0f); } } else { - audioMod = m_bit ? 1.0f : -1.0f; + mod = m_bit ? 1.0f : -1.0f; } // FM - m_fmPhase += m_phaseSensitivity * audioMod * (m_settings.m_spaceHigh ? -1.0f : 1.0f); + m_fmPhase += m_phaseSensitivity * mod * (m_settings.m_spaceHigh ? -1.0f : 1.0f); // Keep phase in range -pi,pi if (m_fmPhase > M_PI) { m_fmPhase -= 2.0f * M_PI; @@ -194,7 +194,8 @@ void RttyModSource::modulateSample() Real s = std::real(m_modSample); calculateLevel(s); - m_demodBuffer[m_demodBufferFill] = audioMod * std::numeric_limits::max(); + // Send to demod analyser + m_demodBuffer[m_demodBufferFill] = mod * std::numeric_limits::max(); ++m_demodBufferFill; if (m_demodBufferFill >= m_demodBuffer.size()) @@ -258,7 +259,7 @@ void RttyModSource::applySettings(const RttyModSettings& settings, bool force) << " symbolSpan: " << settings.m_symbolSpan << " channelSampleRate:" << m_channelSampleRate << " baud:" << settings.m_baud; - m_pulseShape.create(settings.m_beta, m_settings.m_symbolSpan, m_channelSampleRate/settings.m_baud); + m_pulseShape.create(settings.m_beta, settings.m_symbolSpan, m_channelSampleRate/settings.m_baud, true); } if ((settings.m_characterSet != m_settings.m_characterSet) || force) { @@ -302,7 +303,7 @@ void RttyModSource::applyChannelSettings(int channelSampleRate, int channelFrequ << " symbolSpan: " << m_settings.m_symbolSpan << " channelSampleRate:" << m_channelSampleRate << " baud:" << m_settings.m_baud; - m_pulseShape.create(m_settings.m_beta, m_settings.m_symbolSpan, channelSampleRate/m_settings.m_baud); + m_pulseShape.create(m_settings.m_beta, m_settings.m_symbolSpan, channelSampleRate/m_settings.m_baud, true); } if ((m_channelSampleRate != channelSampleRate) || force) diff --git a/plugins/feature/demodanalyzer/demodanalyzersettings.cpp b/plugins/feature/demodanalyzer/demodanalyzersettings.cpp index 249128732..f7a21d503 100644 --- a/plugins/feature/demodanalyzer/demodanalyzersettings.cpp +++ b/plugins/feature/demodanalyzer/demodanalyzersettings.cpp @@ -38,6 +38,7 @@ const QStringList DemodAnalyzerSettings::m_channelTypes = { QStringLiteral("PacketDemod"), QStringLiteral("PacketMod"), QStringLiteral("RadiosondeDemod"), + QStringLiteral("RTTYMod"), QStringLiteral("SSBDemod"), QStringLiteral("SSBMod"), QStringLiteral("WFMDemod"), @@ -60,6 +61,7 @@ const QStringList DemodAnalyzerSettings::m_channelURIs = { QStringLiteral("sdrangel.channel.packetdemod"), QStringLiteral("sdrangel.channeltx.modpacket"), QStringLiteral("sdrangel.channel.radiosondedemod"), + QStringLiteral("sdrangel.channeltx.modrtty"), QStringLiteral("sdrangel.channel.ssbdemod"), QStringLiteral("sdrangel.channeltx.modssb"), QStringLiteral("sdrangel.channel.wfmdemod"), diff --git a/plugins/feature/gs232controller/gs232controllerplugin.cpp b/plugins/feature/gs232controller/gs232controllerplugin.cpp index 92d45d84f..1df12cbbf 100644 --- a/plugins/feature/gs232controller/gs232controllerplugin.cpp +++ b/plugins/feature/gs232controller/gs232controllerplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor GS232ControllerPlugin::m_pluginDescriptor = { GS232Controller::m_featureId, QStringLiteral("Rotator Controller"), - QStringLiteral("7.15.3"), + QStringLiteral("7.15.4"), QStringLiteral("(c) Jon Beniston, M7RCE"), QStringLiteral("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/feature/map/mapplugin.cpp b/plugins/feature/map/mapplugin.cpp index cc22a636a..cfed1f3bf 100644 --- a/plugins/feature/map/mapplugin.cpp +++ b/plugins/feature/map/mapplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor MapPlugin::m_pluginDescriptor = { Map::m_featureId, QStringLiteral("Map"), - QStringLiteral("7.15.3"), + QStringLiteral("7.15.4"), QStringLiteral("(c) Jon Beniston, M7RCE"), QStringLiteral("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/feature/startracker/startrackerplugin.cpp b/plugins/feature/startracker/startrackerplugin.cpp index 8597d1c53..0cdbcbe6c 100644 --- a/plugins/feature/startracker/startrackerplugin.cpp +++ b/plugins/feature/startracker/startrackerplugin.cpp @@ -30,7 +30,7 @@ const PluginDescriptor StarTrackerPlugin::m_pluginDescriptor = { StarTracker::m_featureId, QStringLiteral("Star Tracker"), - QStringLiteral("7.14.1"), + QStringLiteral("7.15.4"), QStringLiteral("(c) Jon Beniston, M7RCE"), QStringLiteral("https://github.com/f4exb/sdrangel"), true, diff --git a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp index 44321b0fc..5a933d77c 100644 --- a/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp +++ b/plugins/samplesource/rtlsdr/rtlsdrplugin.cpp @@ -18,7 +18,7 @@ const PluginDescriptor RTLSDRPlugin::m_pluginDescriptor = { QStringLiteral("RTLSDR"), QStringLiteral("RTL-SDR Input"), - QStringLiteral("7.8.3"), + QStringLiteral("7.15.4"), QStringLiteral("(c) Edouard Griffiths, F4EXB"), QStringLiteral("https://github.com/f4exb/sdrangel"), true, diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 1b7369a08..5b640f7bb 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -259,6 +259,7 @@ set(sdrbase_SOURCES util/simpleserializer.cpp util/serialutil.cpp #util/spinlock.cpp + util/rtty.cpp util/uid.cpp util/units.cpp util/timeutil.cpp @@ -491,6 +492,7 @@ set(sdrbase_HEADERS util/profiler.h util/radiosonde.h util/rtpsink.h + util/rtty.h util/syncmessenger.h util/samplesourceserializer.h util/simpleserializer.h diff --git a/sdrbase/dsp/raisedcosine.h b/sdrbase/dsp/raisedcosine.h index 630ef2ebb..7975901a4 100644 --- a/sdrbase/dsp/raisedcosine.h +++ b/sdrbase/dsp/raisedcosine.h @@ -89,18 +89,38 @@ public: else { // Calculate maximum output of filter, assuming upsampled bipolar input E.g. [1 0 0 -1 0 0..] - // This doesn't necessarily include the centre tap, so we try each offset - double maxGain = 0.0; - for (i = 0; i < samplesPerSymbol; i++) + // This doesn't necessarily include the centre tap, as ISI there should be zero, + // it's often at the midpoint between two symbols. However, depending on beta, + // the input that produces the worst case can vary, so we currently try them all + double maxGain = 0; + for (int input = 0; input < (1 << symbolSpan); input++) { - double g = 0.0; - for (j = 0; j < (int)m_taps.size() - 1; j += samplesPerSymbol) - g += std::fabs(2.0 * m_taps[j]); - if ((i & 1) == 0) - g += std::fabs(m_taps[j]); - if (g > maxGain) - maxGain = g; + double maxV = 0; + for(int i = 0; i < nTaps; i++) { + m_samples[i] = 0; + } + for (int i = 0; i < symbolSpan; i++) + { + Type sym = (input >> i) & 1 ? 1 : -1; + for (int j = 0; j < samplesPerSymbol; j++) + { + Type out; + if (j == 1) { + out = filter(sym); + } else { + out = filter(0); + } + double outAbs = abs(out); + if (outAbs > maxV) { + maxV = outAbs; + } + } + } + if (maxV > maxGain) { + maxGain = maxV; + } } + // Scale up so maximum out is 1 for(i = 0; i < (int)m_taps.size(); i++) m_taps[i] /= maxGain; diff --git a/sdrbase/util/baudot.cpp b/sdrbase/util/baudot.cpp index dfac753d0..00cfd09cc 100644 --- a/sdrbase/util/baudot.cpp +++ b/sdrbase/util/baudot.cpp @@ -81,8 +81,8 @@ const QStringList Baudot::m_usFigure = { const QStringList Baudot::m_russianLetter = { "\0", "Е", "\n", "А", " ", "С", "И", "У", - "\r", "Д", "П", "Й", "Н", "Ф", "Ц", "К", - "Т", "З", "Л", "В", "Х", "Ы", "P", "Я", + "\r", "Д", "Р", "Й", "Ч", "Ф", "Ц", "К", + "Т", "З", "Л", "В", "Х", "Ы", "П", "Я", "О", "Б", "Г", "<", "М", "Ь", "Ж", ">" }; @@ -206,36 +206,37 @@ void BaudotEncoder::setCharacterSet(Baudot::CharacterSet characterSet) switch (m_characterSet) { case Baudot::ITA2: - m_letters = Baudot::m_ita2Letter; - m_figures = Baudot::m_ita2Figure; + m_chars[LETTERS] = Baudot::m_ita2Letter; + m_chars[FIGURES] = Baudot::m_ita2Figure; break; case Baudot::UK: - m_letters = Baudot::m_ukLetter; - m_figures = Baudot::m_ukFigure; + m_chars[LETTERS] = Baudot::m_ukLetter; + m_chars[FIGURES] = Baudot::m_ukFigure; break; case Baudot::EUROPEAN: - m_letters = Baudot::m_europeanLetter; - m_figures = Baudot::m_europeanFigure; + m_chars[LETTERS] = Baudot::m_europeanLetter; + m_chars[FIGURES] = Baudot::m_europeanFigure; break; case Baudot::US: - m_letters = Baudot::m_usLetter; - m_figures = Baudot::m_usFigure; + m_chars[LETTERS] = Baudot::m_usLetter; + m_chars[FIGURES] = Baudot::m_usFigure; break; case Baudot::RUSSIAN: - m_letters = Baudot::m_russianLetter; - m_figures = Baudot::m_russianFigure; + m_chars[LETTERS] = Baudot::m_ita2Letter; + m_chars[FIGURES] = Baudot::m_russianFigure; break; case Baudot::MURRAY: - m_letters = Baudot::m_murrayLetter; - m_figures = Baudot::m_murrayFigure; + m_chars[LETTERS] = Baudot::m_murrayLetter; + m_chars[FIGURES] = Baudot::m_murrayFigure; break; default: qDebug() << "BaudotEncoder::BaudotEncoder: Unsupported character set " << m_characterSet; - m_letters = Baudot::m_ita2Letter; - m_figures = Baudot::m_ita2Figure; + m_chars[LETTERS] = Baudot::m_ita2Letter; + m_chars[FIGURES] = Baudot::m_ita2Figure; m_characterSet = Baudot::ITA2; break; } + m_chars[(int)CYRILLIC] = Baudot::m_russianLetter; } void BaudotEncoder::setUnshiftOnSpace(bool unshiftOnSpace) @@ -262,14 +263,11 @@ void BaudotEncoder::setStopBits(int stopBits) void BaudotEncoder::init() { - m_figure = false; + m_page = LETTERS; } bool BaudotEncoder::encode(QChar c, unsigned &bits, unsigned int &bitCount) { - unsigned int code; - const unsigned int codeLen = 5; - bits = 0; bitCount = 0; @@ -277,50 +275,63 @@ bool BaudotEncoder::encode(QChar c, unsigned &bits, unsigned int &bitCount) c = c.toUpper(); QString s(c); - // We could create reverse look-up tables to speed this up, but it's only 200 baud... - if (m_letters.contains(s)) + if (s == '>') { - if (m_figure) - { - // Switch to letters - addStartBits(bits, bitCount); - code = reverseBits(m_letters.indexOf(">"), codeLen); - addBits(bits, bitCount, code, codeLen); - addStopBits(bits, bitCount); - m_figure = false; - } - addStartBits(bits, bitCount); - code = reverseBits(m_letters.indexOf(s), codeLen); - addBits(bits, bitCount, code, codeLen); - addStopBits(bits, bitCount); + addCode(bits, bitCount, m_chars[m_page].indexOf(s)); + m_page = LETTERS; return true; } - else if (m_figures.contains(s)) + else if (s == '<') { - if (!m_figure) - { - // Switch to figures - addStartBits(bits, bitCount); - code = reverseBits(m_letters.indexOf("<"), codeLen); - addBits(bits, bitCount, code, codeLen); - addStopBits(bits, bitCount); - m_figure = true; - } - addStartBits(bits, bitCount); - code = reverseBits(m_figures.indexOf(s), codeLen); - addBits(bits, bitCount, code, codeLen); - addStopBits(bits, bitCount); - if ((s == " ") && m_unshiftOnSpace) { - m_figure = false; - } + addCode(bits, bitCount, m_chars[m_page].indexOf(s)); + m_page = FIGURES; + return true; + } + else if ((m_characterSet == Baudot::RUSSIAN) && (s == '\0')) + { + addCode(bits, bitCount, m_chars[m_page].indexOf(s)); + m_page = CYRILLIC; + return true; + } + + // We could create reverse look-up tables to speed this up, but it's only 200 baud... + + // Is character in current page? If so, use that, as it avoids switching + if (m_chars[m_page].contains(s)) + { + addCode(bits, bitCount, m_chars[m_page].indexOf(s)); + return true; } else { - qDebug() << "BaudotEncoder::encode: Can't encode" << c; - return false; + // Look for character in other pages + const QString switchPage[] = { ">", "<", "\0" }; + + for (int page = m_page == LETTERS ? 1 : 0; page < (m_characterSet == Baudot::RUSSIAN) ? 3 : 2; page++) + { + if (m_chars[page].contains(s)) + { + // Switch to page + addCode(bits, bitCount, m_chars[m_page].indexOf(switchPage[page])); + m_page = (BaudotEncoder::Page)page; + + addCode(bits, bitCount, m_chars[m_page].indexOf(s)); + return true; + } + } } - return true; + return false; +} + +void BaudotEncoder::addCode(unsigned& bits, unsigned int& bitCount, unsigned int code) const +{ + const unsigned int codeLen = 5; + + addStartBits(bits, bitCount); + code = reverseBits(code, codeLen); + addBits(bits, bitCount, code, codeLen); + addStopBits(bits, bitCount); } void BaudotEncoder::addStartBits(unsigned& bits, unsigned int& bitCount) const diff --git a/sdrbase/util/baudot.h b/sdrbase/util/baudot.h index ade131abe..c276c791a 100644 --- a/sdrbase/util/baudot.h +++ b/sdrbase/util/baudot.h @@ -88,6 +88,7 @@ public: private: + void addCode(unsigned& bits, unsigned int& bitCount, unsigned int code) const; void addStartBits(unsigned int& bits, unsigned int& bitCount) const; void addStopBits(unsigned int& bits, unsigned int& bitCount) const; void addBits(unsigned int& bits, unsigned int& bitCount, int data, int count) const; @@ -96,9 +97,12 @@ private: Baudot::CharacterSet m_characterSet; bool m_unshiftOnSpace; - QStringList m_letters; - QStringList m_figures; - bool m_figure; + QStringList m_chars[3]; + enum Page { + LETTERS, + FIGURES, + CYRILLIC + } m_page; bool m_msbFirst; int m_startBits; int m_stopBits; diff --git a/sdrbase/util/rtty.cpp b/sdrbase/util/rtty.cpp new file mode 100644 index 000000000..1cacf96f0 --- /dev/null +++ b/sdrbase/util/rtty.cpp @@ -0,0 +1,290 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "util/rtty.h" + +// From http://www.ct2fzi.net/abreviations/abreviations.html +const QHash Rtty::m_acronyms = { + {"AA", "After All"}, + {"AB", "All Before"}, + {"ABT", "About"}, + {"ACK", "Acknowledgement"}, + {"ADEE", "Addressee"}, + {"ADR", "Address"}, + {"AF", "Audio Frequency"}, + {"AGN", "Again"}, + {"AM", "Amplitude Modulation"}, + {"AMU", "Antenna Matching Unit"}, + {"ANS", "Answer"}, + {"ANT", "Antenna"}, + {"ARQ", "Automatic Repeat Request"}, + {"ATU", "Antenna Tuning Unit"}, + {"B4", "Before"}, + {"BCN", "Beacon"}, + {"BCNU", "Be Seeing You"}, + {"BD", "Bad"}, + {"BK", "Break"}, + {"BN", "Been"}, + {"BTH", "Both"}, + {"BTR", "Better"}, + {"BTW", "By The Way"}, + {"BTU", "Back To You"}, + {"C", "Correct"}, + {"CBA", "Callbook Address"}, + {"CFM", "Confirm"}, + {"CK", "Check"}, + {"CKT", "Circuit"}, + {"CL", "Closing Down"}, + {"CLBK", "Callbook"}, + {"CLD", "Called"}, + {"CLG", "Calling"}, + {"CMG", "Coming"}, + {"CNT", "Can't"}, + {"COMP", "Computer"}, + {"CONDX", "Conditions"}, + {"COZ", "Because"}, + {"CPI", "Copy"}, + {"CQ", "General Call"}, + {"CRD", "Card"}, + {"CS", "Callsign"}, + {"CTCSS", "Continuous Tone Coded Squelch System"}, + {"CU", "See You"}, + {"CUAGN", "See You Again"}, + {"CUD", "Could"}, + {"CUL", "See You Later"}, + {"CUM", "Come"}, + {"CUZ", "Because"}, + {"CW", "Continuous Wave / Morse"}, + {"DA", "Day"}, + {"DE", "From"}, + {"DF", "Direction Finding"}, + {"DIFF", "Difference"}, + {"DLD", "Delivered"}, + {"DLVD", "Delivered"}, + {"DN", "Down"}, + {"DR", "Dear"}, + {"DSB", "Double Side Band"}, + {"DSP", "Digital Signal Processing"}, + {"DSW", "Goodbye (Russian)"}, + {"DWN", "Down"}, + {"DX", "Distance"}, + {"EL", "Element"}, + {"EME", "Earth-Moon-Earth"}, + {"ENUF", "Enough"}, + {"ES", "And"}, + {"EU", "Europe"}, + {"EVE", "Evening"}, + {"FB", "Fine Business"}, + {"FER", "For"}, + {"FIO", "For Information Only"}, + {"FM", "Frequency Modulation"}, + {"FQ", "Frequency"}, + {"FREQ", "Frequency"}, + {"FSD", "Full Scale Deflection"}, + {"FSK", "Frequency Shift Keying"}, + {"FWD", "Forward"}, + {"FWIW", "For What It's Worth"}, + {"FYI", "For Your Information"}, + {"GA", "Good Afternoon"}, + {"GB", "Good Bye"}, + {"GD", "Good Day"}, + {"GE", "Good Evening"}, + {"GESS", "Guess"}, + {"GG", "Going"}, + {"GLD", "Glad"}, + {"GM", "Good Morning"}, + {"GMT", "Greenwich Mean Time"}, + {"GN", "Good Night"}, + {"GND", "Ground"}, + {"GP", "Ground Plane"}, + {"GPS", "Global Positioning System"}, + {"GS", "Green Stamp"}, + {"GUD", "Good"}, + {"GV", "Give"}, + {"GVG", "Giving"}, + {"HAGD", "Have A Good Day"}, + {"HAGWE", "Have A Good Weekend"}, + {"HF", "High Frequency"}, + {"HI", "High"}, + {"HPE", "Hope"}, + {"HQ", "Headquarters"}, + {"HR", "Here / Hour"}, + {"HRD", "Heard"}, + {"HV", "Have"}, + {"HVG", "Having"}, + {"HVY", "Heavy"}, + {"HW", "How"}, + {"IMHO", "In My Humble Opinion"}, + {"IMI", "Say again"}, + {"K", "Over"}, + {"KN", "Over"}, + {"LF", "Low Frequency"}, + {"LNG", "Long"}, + {"LP", "Long Path"}, + {"LSB", "Lower Sideband"}, + {"LSN", "Listen"}, + {"LTR", "Later"}, + {"LV", "Leave"}, + {"LVG", "Leaving"}, + {"LW", "Long Wire"}, + {"MGR", "Manager"}, + {"MI", "My"}, + {"MNI", "Many"}, + {"MOM", "Moment"}, + {"MS", "Meteor Scatter"}, + {"MSG", "Message"}, + {"N", "No"}, + {"NCS", "Net Control Station"}, + {"ND", "Nothing Doing"}, + {"NM", "No More"}, + {"NR", "Near / Number"}, + {"NW", "Now"}, + {"OB", "Old Boy"}, + {"OC", "Old Chap"}, + {"OM", "Old Man"}, + {"OP", "Operator"}, + {"OPR", "Operator"}, + {"OT", "Old Timer"}, + {"OW", "Old Woman"}, + {"PA", "Power Amplifier"}, + {"PBL", "Preamble"}, + {"PKG", "Package"}, + {"POV", "Point Of View"}, + {"PSE", "Please"}, + {"PSK", "Phase Shift Keying"}, + {"PT", "Point"}, + {"PTT", "Push To Talk"}, + {"PWR", "Power"}, + {"PX", "Prefix"}, + {"QRA", "Address"}, + {"QRG", "Frequency"}, + {"QRK", "Readability"}, + {"QRL", "Busy"}, + {"QRM", "Interference"}, + {"QRN", "Noise"}, + {"QRO", "High Power"}, + {"QRP", "Low Power"}, + {"QRQ", "Send Faster"}, + {"QRS", "Send Slower"}, + {"QRSS", "Send Very Slowly"}, + {"QRT", "Stop Sending"}, + {"QRU", "Nothing Further To Say"}, + {"QRV", "Ready"}, + {"QRX", "Wait"}, + {"QRZ", "Who Is Calling Me"}, + {"QSA", "Signal Strength"}, + {"QSB", "Fading"}, + {"QSK", "Break-in"}, + {"QSL", "All Received OK"}, + {"QSLL", "I Will Send A QSL Card"}, + {"QSO", "Contact"}, + {"QSP", "Relay A Message"}, + {"QSX", "Listening On Frequency"}, + {"QSY", "Change Frequency"}, + {"QTH", "Location"}, + {"R", "Received OK"}, + {"RC", "Ragchew"}, + {"RCD", "Recieved"}, + {"RCVR", "Receiver"}, + {"RE", "Regarding"}, + {"REF", "Reference"}, + {"RF", "Radio Frequency"}, + {"RFI", "Radio Frequency Interference"}, + {"RPT", "Repeat / Report"}, + {"RST", "Signal Report"}, + {"RTTY", "Radio Teletype"}, + {"RX", "Receive"}, + {"SA", "Say"}, + {"SDR", "Software Defined Radio"}, + {"SEZ", "Says"}, + {"SGD", "Signed"}, + {"SHUD", "Should"}, + {"SIG", "Signal"}, + {"SK", "End Of Work"}, + {"SKED", "Schedule"}, + {"SN", "Soon"}, + {"SP", "Short Path"}, + {"SRI", "Sorry"}, + {"SSB", "Single Sideband"}, + {"STN", "Station"}, + {"SUM", "Some"}, + {"SVC", "Service"}, + {"SWR", "Standing Wave Ratio"}, + {"TFC", "Traffic"}, + {"TIA", "Thanks In Advance"}, + {"TKS", "Thanks"}, + {"TMW", "Tomorrow"}, + {"TNC", "Terminal Node Controller"}, + {"TNX", "Thanks"}, + {"TR", "Transmit"}, + {"T/R", "Transmit/Receive"}, + {"TRBL", "Trouble"}, + {"TRF", "Tuned Radio Frequency"}, + {"TRIX", "Tricks"}, + {"TRX", "Transceiver"}, + {"TT", "That"}, + {"TTS", "That Is"}, + {"TU", "Thank You"}, + {"TVI", "Television Interference"}, + {"TX", "Transmit"}, + {"TXT", "Text"}, + {"U", "You"}, + {"UHF", "Ultra High Frequency"}, + {"UNLIS", "Unlicensed"}, + {"UR", "Your"}, + {"URS", "Yours"}, + {"UTC", "Coordinated Universal Time"}, + {"V", "Volts"}, + {"VHF", "Very High Frequency"}, + {"VE", "Understood"}, + {"VERT", "Vertical"}, + {"VFB", "Very Fine Business"}, + {"VFO", "Variable Frequency Oscillator"}, + {"VLF", "Very Low Frequency"}, + {"VOX", "Voice Operated"}, + {"VSB", "Vestigial Sideband"}, + {"VSWR", "Voltage Standing Wave Ratio"}, + {"VY", "Very"}, + {"W", "Watts"}, + {"WA", "Word After"}, + {"WAT", "What"}, + {"WATSA", "What Say"}, + {"WB", "Word Before"}, + {"WD", "Word"}, + {"WDS", "Words"}, + {"WID", "With"}, + {"WKD", "Worked"}, + {"WKG", "Working"}, + {"WL", "Will"}, + {"WPM", "Words Per Minute"}, + {"WRD", "Word"}, + {"WRK", "Work"}, + {"WUD", "Would"}, + {"WX", "Weather"}, + {"XCVR", "Transceiver"}, + {"XMTR", "Transmitter"}, + {"XTAL", "Crystal"}, + {"YF", "Wife"}, + {"YL", "Young Lady"}, + {"YR", "Year"}, + {"Z", "Zulu Time"}, + {"30", "I Have Nothing More to Send"}, + {"33", "Fondest Regards"}, + {"55", "Best Success"}, + {"73", "Best Wishes"}, + {"88", "Love And Kisses"}, +}; diff --git a/sdrbase/util/rtty.h b/sdrbase/util/rtty.h new file mode 100644 index 000000000..d0a40f9cc --- /dev/null +++ b/sdrbase/util/rtty.h @@ -0,0 +1,31 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_UTIL_RTTY_H +#define INCLUDE_UTIL_RTTY_H + +#include + +#include "export.h" + +class SDRBASE_API Rtty +{ +public: + static const QHash m_acronyms; +}; + +#endif /* INCLUDE_UTIL_RTTY_H */ diff --git a/sdrgui/CMakeLists.txt b/sdrgui/CMakeLists.txt index d7ab6122f..fd3c5caa0 100644 --- a/sdrgui/CMakeLists.txt +++ b/sdrgui/CMakeLists.txt @@ -7,6 +7,7 @@ set(CMAKE_AUTOUIC OFF) set(sdrgui_SOURCES mainwindow.cpp gui/aboutdialog.cpp + gui/acronymview.cpp gui/addpresetdialog.cpp gui/audiodialog.cpp gui/audioselectdialog.cpp @@ -127,6 +128,7 @@ set(sdrgui_HEADERS gui/aboutdialog.h gui/accessiblevaluedial.h gui/accessiblevaluedialz.h + gui/acronymview.h gui/addpresetdialog.h gui/audiodialog.h gui/audioselectdialog.h diff --git a/sdrgui/gui/acronymview.cpp b/sdrgui/gui/acronymview.cpp new file mode 100644 index 000000000..295b6f706 --- /dev/null +++ b/sdrgui/gui/acronymview.cpp @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include "acronymview.h" + +AcronymView::AcronymView(QWidget* parent) : + QPlainTextEdit(parent) +{ + setMouseTracking(true); + setReadOnly(true); +} + +bool AcronymView::event(QEvent* event) +{ + if (event->type() == QEvent::ToolTip) + { + QHelpEvent* helpEvent = static_cast(event); + QTextCursor cursor = cursorForPosition(helpEvent->pos()); + cursor.select(QTextCursor::WordUnderCursor); + QString text = cursor.selectedText(); + // Remove trailing digits from METAR + while (text.size() > 0 && text.right(1)[0].isDigit()) { + text = text.left(text.size() - 1); + } + if (!text.isEmpty() && m_acronym.contains(text)) + { + QToolTip::showText(helpEvent->globalPos(), QString("%1 - %2").arg(text).arg(m_acronym.value(text))); + } + else + { + if (!text.isEmpty()) { + qDebug() << "AcronymView::event: No tooltip for " << text; + } + QToolTip::hideText(); + } + return true; + } + return QPlainTextEdit::event(event); +} + +void AcronymView::addAcronym(const QString& acronym, const QString& explanation) +{ + m_acronym.insert(acronym, explanation); +} + +void AcronymView::addAcronyms(const QHash& acronyms) +{ + m_acronym.insert(acronyms); +} diff --git a/sdrgui/gui/acronymview.h b/sdrgui/gui/acronymview.h new file mode 100644 index 000000000..1e2b5c57b --- /dev/null +++ b/sdrgui/gui/acronymview.h @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////////////// +// 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 . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef INCLUDE_GUI_ACRONYMVIEW_H +#define INCLUDE_GUI_ACRONYMVIEW_H + +#include +#include + +#include "export.h" + +// Displays text like a QPlainTextEdit, but adds tooltips for acronyms in the text +class SDRGUI_API AcronymView : public QPlainTextEdit { + Q_OBJECT + + QHash m_acronym; + +public: + + AcronymView(QWidget* parent = nullptr); + bool event(QEvent* event); + void addAcronym(const QString& acronym, const QString& explanation); + void addAcronyms(const QHash& acronyms); + +}; + +#endif // INCLUDE_GUI_ACRONYMVIEW_H