mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-15 21:01:45 -05:00
Merge branch 'rttymod' of https://github.com/srcejon/sdrangel into rttymod
This commit is contained in:
commit
2f5f2672d0
1
.gitignore
vendored
1
.gitignore
vendored
@ -43,3 +43,4 @@ obj-x86_64-linux-gnu/*
|
||||
|
||||
/rescuesdriq/vendor/
|
||||
/rescuesdriq/Godeps/
|
||||
/.vs
|
||||
|
14
CHANGELOG
14
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 <f4exb06@gmail.com> 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
|
||||
|
@ -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
|
||||
|
@ -32,6 +32,40 @@
|
||||
},
|
||||
"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" ]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -48,6 +82,10 @@
|
||||
"name": "default",
|
||||
"configurePreset": "default"
|
||||
},
|
||||
{
|
||||
"name": "default-windows",
|
||||
"configurePreset": "default-windows"
|
||||
},
|
||||
{
|
||||
"name": "default-qt6",
|
||||
"configurePreset": "default-qt6"
|
||||
|
14
debian/changelog
vendored
14
debian/changelog
vendored
@ -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 <f4exb06@gmail.com> 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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -79,7 +79,12 @@ Specifies whether bits are transmitted least-significant-bit first (LSB) or most
|
||||
|
||||
<h3>14: Mark/Space Frequency</h3>
|
||||
|
||||
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.
|
||||
|
||||
<h3>15: Suppress CR LF</h3>
|
||||
|
||||
@ -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.
|
||||
|
@ -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
|
||||
|
@ -916,7 +916,7 @@
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="text">
|
||||
<widget class="AcronymView" name="text">
|
||||
<property name="toolTip">
|
||||
<string>Received text</string>
|
||||
</property>
|
||||
@ -1293,6 +1293,12 @@
|
||||
<header>gui/glscopegui.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>AcronymView</class>
|
||||
<extends>QTextEdit</extends>
|
||||
<header>gui/acronymview.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>deltaFrequency</tabstop>
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -2,7 +2,10 @@
|
||||
|
||||
<h2>Introduction</h2>
|
||||
|
||||
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.
|
||||
|
||||
<h2>Interface</h2>
|
||||
|
||||
@ -64,7 +67,14 @@ UDP port number to receive text to be transmitted on.
|
||||
|
||||
<h3>13: Baudot Character Set</h3>
|
||||
|
||||
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
|
||||
|
||||
<h3>14: Bit Ordering</h3>
|
||||
|
||||
@ -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).
|
||||
|
||||
<h3>20: TX</h3>
|
||||
|
||||
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.
|
||||
|
||||
<h3>21: Transmitted Text</h3>
|
||||
|
||||
The trasnmitted text area shows characters as they are transmitted.
|
||||
|
||||
Holding the cursor over an acronym may show a tooltip with the decoded acronym.
|
||||
|
||||
<h2>API</h2>
|
||||
|
||||
|
@ -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();
|
||||
|
@ -751,7 +751,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<spacer name="horizontalSpacerSettings">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
@ -854,11 +854,7 @@
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPlainTextEdit" name="transmittedText">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="AcronymView" name="transmittedText"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@ -960,6 +956,12 @@
|
||||
<header>gui/levelmeter.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>AcronymView</class>
|
||||
<extends>QTextEdit</extends>
|
||||
<header>gui/acronymview.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>deltaFrequency</tabstop>
|
||||
|
@ -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");
|
||||
|
@ -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<int16_t>::max();
|
||||
// Send to demod analyser
|
||||
m_demodBuffer[m_demodBufferFill] = mod * std::numeric_limits<int16_t>::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)
|
||||
|
@ -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"),
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
addCode(bits, bitCount, m_chars[m_page].indexOf(s));
|
||||
m_page = FIGURES;
|
||||
return true;
|
||||
}
|
||||
else if ((m_characterSet == Baudot::RUSSIAN) && (s == '\0'))
|
||||
{
|
||||
// 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 = 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;
|
||||
// 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 false;
|
||||
}
|
||||
|
||||
return true;
|
||||
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
|
||||
|
@ -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;
|
||||
|
290
sdrbase/util/rtty.cpp
Normal file
290
sdrbase/util/rtty.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "util/rtty.h"
|
||||
|
||||
// From http://www.ct2fzi.net/abreviations/abreviations.html
|
||||
const QHash<QString, QString> 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"},
|
||||
};
|
31
sdrbase/util/rtty.h
Normal file
31
sdrbase/util/rtty.h
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_UTIL_RTTY_H
|
||||
#define INCLUDE_UTIL_RTTY_H
|
||||
|
||||
#include <QHash>
|
||||
|
||||
#include "export.h"
|
||||
|
||||
class SDRBASE_API Rtty
|
||||
{
|
||||
public:
|
||||
static const QHash<QString, QString> m_acronyms;
|
||||
};
|
||||
|
||||
#endif /* INCLUDE_UTIL_RTTY_H */
|
@ -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
|
||||
|
68
sdrgui/gui/acronymview.cpp
Normal file
68
sdrgui/gui/acronymview.cpp
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <QEvent>
|
||||
#include <QHelpEvent>
|
||||
#include <QToolTip>
|
||||
#include <QDebug>
|
||||
|
||||
#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<QHelpEvent*>(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<QString, QString>& acronyms)
|
||||
{
|
||||
m_acronym.insert(acronyms);
|
||||
}
|
41
sdrgui/gui/acronymview.h
Normal file
41
sdrgui/gui/acronymview.h
Normal file
@ -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 <http://www.gnu.org/licenses/>. //
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifndef INCLUDE_GUI_ACRONYMVIEW_H
|
||||
#define INCLUDE_GUI_ACRONYMVIEW_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QPlainTextEdit>
|
||||
|
||||
#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<QString, QString> m_acronym;
|
||||
|
||||
public:
|
||||
|
||||
AcronymView(QWidget* parent = nullptr);
|
||||
bool event(QEvent* event);
|
||||
void addAcronym(const QString& acronym, const QString& explanation);
|
||||
void addAcronyms(const QHash<QString, QString>& acronyms);
|
||||
|
||||
};
|
||||
|
||||
#endif // INCLUDE_GUI_ACRONYMVIEW_H
|
Loading…
Reference in New Issue
Block a user