mirror of
https://github.com/f4exb/sdrangel.git
synced 2024-11-25 17:28:50 -05:00
Morse Decoder: basic functionality
This commit is contained in:
parent
4387f54f0d
commit
39bc799509
@ -109,7 +109,6 @@ public:
|
||||
|
||||
private:
|
||||
DataFifo *m_dataFifo;
|
||||
int m_channelSampleRate;
|
||||
int m_sinkSampleRate;
|
||||
MessageQueue m_inputMessageQueue; //!< Queue for asynchronous inbound communication
|
||||
MessageQueue *m_msgQueueToFeature; //!< Queue to report channel change to main feature object
|
||||
|
@ -18,6 +18,7 @@ set(morsedecoder_HEADERS
|
||||
|
||||
include_directories(
|
||||
${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client
|
||||
${GGMORSE_INCLUDE_DIR}
|
||||
)
|
||||
|
||||
if(NOT SERVER_MODE)
|
||||
@ -51,6 +52,7 @@ target_link_libraries(${TARGET_NAME}
|
||||
${TARGET_LIB}
|
||||
sdrbase
|
||||
${TARGET_LIB_GUI}
|
||||
${GGMORSE_LIBRARIES}
|
||||
)
|
||||
|
||||
install(TARGETS ${TARGET_NAME} DESTINATION ${INSTALL_FOLDER})
|
||||
|
@ -38,6 +38,7 @@ MESSAGE_CLASS_DEFINITION(MorseDecoder::MsgStartStop, Message)
|
||||
MESSAGE_CLASS_DEFINITION(MorseDecoder::MsgReportChannels, Message)
|
||||
MESSAGE_CLASS_DEFINITION(MorseDecoder::MsgSelectChannel, Message)
|
||||
MESSAGE_CLASS_DEFINITION(MorseDecoder::MsgReportSampleRate, Message)
|
||||
MESSAGE_CLASS_DEFINITION(MorseDecoder::MsgReportText, Message)
|
||||
|
||||
const char* const MorseDecoder::m_featureIdURI = "sdrangel.feature.morsedecoder";
|
||||
const char* const MorseDecoder::m_featureId = "MorseDecoder";
|
||||
@ -238,6 +239,26 @@ bool MorseDecoder::handleMessage(const Message& cmd)
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MsgReportText::match(cmd))
|
||||
{
|
||||
MsgReportText& report = (MsgReportText&) cmd;
|
||||
|
||||
// Write to log file
|
||||
if (m_logFile.isOpen())
|
||||
{
|
||||
// Format text
|
||||
m_logStream << MorseDecoderSettings::formatText(report.getText());
|
||||
}
|
||||
|
||||
if (getMessageQueueToGUI())
|
||||
{
|
||||
MsgReportText *msg = new MsgReportText(report);
|
||||
getMessageQueueToGUI()->push(msg);
|
||||
}
|
||||
|
||||
// TODO: send via UDP
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
@ -278,6 +299,29 @@ void MorseDecoder::applySettings(const MorseDecoderSettings& settings, const QLi
|
||||
m_worker->getInputMessageQueue()->push(msg);
|
||||
}
|
||||
|
||||
if (settingsKeys.contains("logEnabled")
|
||||
|| settingsKeys.contains("logFilename")
|
||||
|| force)
|
||||
{
|
||||
if (m_logFile.isOpen())
|
||||
{
|
||||
m_logStream.flush();
|
||||
m_logFile.close();
|
||||
}
|
||||
if (settings.m_logEnabled && !settings.m_logFilename.isEmpty())
|
||||
{
|
||||
m_logFile.setFileName(settings.m_logFilename);
|
||||
if (m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
|
||||
{
|
||||
qDebug() << "RttyDemod::applySettings - Logging to: " << settings.m_logFilename;
|
||||
m_logStream.setDevice(&m_logFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
qDebug() << "RttyDemod::applySettings - Unable to open log file: " << settings.m_logFilename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (settings.m_useReverseAPI)
|
||||
{
|
||||
|
@ -21,6 +21,8 @@
|
||||
#include <QHash>
|
||||
#include <QNetworkRequest>
|
||||
#include <QRecursiveMutex>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "feature/feature.h"
|
||||
#include "util/message.h"
|
||||
@ -148,6 +150,29 @@ public:
|
||||
{}
|
||||
};
|
||||
|
||||
class MsgReportText : public Message {
|
||||
MESSAGE_CLASS_DECLARATION
|
||||
|
||||
public:
|
||||
QString getText() const { return m_text; }
|
||||
|
||||
static MsgReportText* create(const QString& text) {
|
||||
return new MsgReportText(text);
|
||||
}
|
||||
|
||||
float m_estimatedPitchHz;
|
||||
float m_estimatedSpeedWPM;
|
||||
float m_signalThreshold;
|
||||
float m_costFunction;
|
||||
|
||||
private:
|
||||
QString m_text;
|
||||
|
||||
MsgReportText(const QString& text) :
|
||||
m_text(text)
|
||||
{}
|
||||
};
|
||||
|
||||
MorseDecoder(WebAPIAdapterInterface *webAPIAdapterInterface);
|
||||
virtual ~MorseDecoder();
|
||||
virtual void destroy() { delete this; }
|
||||
@ -202,6 +227,8 @@ private:
|
||||
ChannelAPI *m_selectedChannel;
|
||||
ObjectPipe *m_dataPipe;
|
||||
int m_sampleRate;
|
||||
QFile m_logFile;
|
||||
QTextStream m_logStream;
|
||||
|
||||
QNetworkAccessManager *m_networkManager;
|
||||
QNetworkRequest m_networkRequest;
|
||||
|
@ -17,6 +17,7 @@
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QFileDialog>
|
||||
#include <QScrollBar>
|
||||
|
||||
#include "feature/featureuiset.h"
|
||||
#include "dsp/spectrumvis.h"
|
||||
@ -109,6 +110,12 @@ bool MorseDecoderGUI::handleMessage(const Message& message)
|
||||
|
||||
return true;
|
||||
}
|
||||
else if (MorseDecoder::MsgReportText::match(message))
|
||||
{
|
||||
MorseDecoder::MsgReportText& report = (MorseDecoder::MsgReportText&) message;
|
||||
textReceived(report.getText());
|
||||
updateMorseStats(report.m_estimatedPitchHz, report.m_estimatedSpeedWPM, report.m_costFunction);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -125,6 +132,35 @@ void MorseDecoderGUI::handleInputMessages()
|
||||
}
|
||||
}
|
||||
|
||||
void MorseDecoderGUI::textReceived(const QString& text)
|
||||
{
|
||||
// Is the scroll bar at the bottom?
|
||||
int scrollPos = ui->text->verticalScrollBar()->value();
|
||||
bool atBottom = scrollPos >= ui->text->verticalScrollBar()->maximum();
|
||||
|
||||
// Move cursor to end where we want to append new text
|
||||
// (user may have moved it by clicking / highlighting text)
|
||||
ui->text->moveCursor(QTextCursor::End);
|
||||
|
||||
// Restore scroll position
|
||||
ui->text->verticalScrollBar()->setValue(scrollPos);
|
||||
|
||||
// Format and insert text
|
||||
ui->text->insertPlainText(MorseDecoderSettings::formatText(text));
|
||||
|
||||
// Scroll to bottom, if we we're previously at the bottom
|
||||
if (atBottom) {
|
||||
ui->text->verticalScrollBar()->setValue(ui->text->verticalScrollBar()->maximum());
|
||||
}
|
||||
}
|
||||
|
||||
void MorseDecoderGUI::updateMorseStats(float estPitch, float estWPM, float cost)
|
||||
{
|
||||
ui->pitchText->setText(QString("%1 Hz").arg(estPitch, 0, 'f', 1));
|
||||
ui->speedText->setText(QString("%1 WPM").arg(estWPM, 0, 'f', 0));
|
||||
ui->cText->setText(QString("%1").arg(cost, 0, 'f', 3));
|
||||
}
|
||||
|
||||
void MorseDecoderGUI::onWidgetRolled(QWidget* widget, bool rollDown)
|
||||
{
|
||||
(void) widget;
|
||||
@ -157,6 +193,7 @@ MorseDecoderGUI::MorseDecoderGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISe
|
||||
m_morseDecoder = reinterpret_cast<MorseDecoder*>(feature);
|
||||
m_morseDecoder->setMessageQueueToGUI(&m_inputMessageQueue);
|
||||
|
||||
|
||||
connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onMenuDialogCalled(const QPoint &)));
|
||||
connect(getInputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleInputMessages()));
|
||||
|
||||
@ -199,6 +236,9 @@ void MorseDecoderGUI::displaySettings()
|
||||
setTitle(m_settings.m_title);
|
||||
blockApplySettings(true);
|
||||
getRollupContents()->restoreState(m_rollupState);
|
||||
ui->statLock->setChecked(!m_settings.m_auto);
|
||||
ui->logFilename->setToolTip(QString(".txt log filename: %1").arg(m_settings.m_logFilename));
|
||||
ui->logEnable->setChecked(m_settings.m_logEnabled);
|
||||
blockApplySettings(false);
|
||||
}
|
||||
|
||||
@ -304,6 +344,44 @@ void MorseDecoderGUI::on_channelApply_clicked()
|
||||
}
|
||||
}
|
||||
|
||||
void MorseDecoderGUI::on_statLock_toggled(bool checked)
|
||||
{
|
||||
m_settings.m_auto = !checked;
|
||||
m_settingsKeys.append("auto");
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void MorseDecoderGUI::on_logEnable_clicked(bool checked)
|
||||
{
|
||||
m_settings.m_logEnabled = checked;
|
||||
m_settingsKeys.append("logEnabled");
|
||||
applySettings();
|
||||
}
|
||||
|
||||
void MorseDecoderGUI::on_logFilename_clicked()
|
||||
{
|
||||
// Get filename to save to
|
||||
QFileDialog fileDialog(nullptr, "Select file to log received text to", "", "*.txt");
|
||||
fileDialog.setAcceptMode(QFileDialog::AcceptSave);
|
||||
|
||||
if (fileDialog.exec())
|
||||
{
|
||||
QStringList fileNames = fileDialog.selectedFiles();
|
||||
if (fileNames.size() > 0)
|
||||
{
|
||||
m_settings.m_logFilename = fileNames[0];
|
||||
ui->logFilename->setToolTip(QString(".txt log filename: %1").arg(m_settings.m_logFilename));
|
||||
m_settingsKeys.append("logFilename");
|
||||
applySettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MorseDecoderGUI::on_clearTable_clicked()
|
||||
{
|
||||
ui->text->clear();
|
||||
}
|
||||
|
||||
void MorseDecoderGUI::tick()
|
||||
{
|
||||
}
|
||||
@ -353,4 +431,8 @@ void MorseDecoderGUI::makeUIConnections()
|
||||
QObject::connect(ui->startStop, &ButtonSwitch::toggled, this, &MorseDecoderGUI::on_startStop_toggled);
|
||||
QObject::connect(ui->channels, qOverload<int>(&QComboBox::currentIndexChanged), this, &MorseDecoderGUI::on_channels_currentIndexChanged);
|
||||
QObject::connect(ui->channelApply, &QPushButton::clicked, this, &MorseDecoderGUI::on_channelApply_clicked);
|
||||
QObject::connect(ui->statLock, &QToolButton::toggled, this, &MorseDecoderGUI::on_statLock_toggled);
|
||||
QObject::connect(ui->logEnable, &ButtonSwitch::clicked, this, &MorseDecoderGUI::on_logEnable_clicked);
|
||||
QObject::connect(ui->logFilename, &QToolButton::clicked, this, &MorseDecoderGUI::on_logFilename_clicked);
|
||||
QObject::connect(ui->clearTable, &QPushButton::clicked, this, &MorseDecoderGUI::on_clearTable_clicked);
|
||||
}
|
||||
|
@ -78,6 +78,8 @@ private:
|
||||
void displaySampleRate(int sampleRate);
|
||||
void updateChannelList();
|
||||
bool handleMessage(const Message& message);
|
||||
void textReceived(const QString& text);
|
||||
void updateMorseStats(float estPitch, float estWPM, float cost);
|
||||
void makeUIConnections();
|
||||
|
||||
private slots:
|
||||
@ -87,6 +89,10 @@ private slots:
|
||||
void on_startStop_toggled(bool checked);
|
||||
void on_channels_currentIndexChanged(int index);
|
||||
void on_channelApply_clicked();
|
||||
void on_statLock_toggled(bool checked);
|
||||
void on_clearTable_clicked();
|
||||
void on_logEnable_clicked(bool checked=false);
|
||||
void on_logFilename_clicked();
|
||||
void updateStatus();
|
||||
void tick();
|
||||
|
||||
|
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>407</width>
|
||||
<height>407</height>
|
||||
<width>422</width>
|
||||
<height>810</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
@ -18,7 +18,7 @@
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>407</width>
|
||||
<width>422</width>
|
||||
<height>407</height>
|
||||
</size>
|
||||
</property>
|
||||
@ -36,13 +36,13 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>10</y>
|
||||
<width>401</width>
|
||||
<width>420</width>
|
||||
<height>121</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>401</width>
|
||||
<width>420</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
@ -307,13 +307,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Line" name="line_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="statLock">
|
||||
<property name="toolTip">
|
||||
@ -332,6 +325,42 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="cLabel">
|
||||
<property name="text">
|
||||
<string>Cost</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="cText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>3000.000</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</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="ButtonSwitch" name="logEnable">
|
||||
<property name="maximumSize">
|
||||
@ -383,42 +412,6 @@
|
||||
</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="cLabel">
|
||||
<property name="text">
|
||||
<string>C</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="cText">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>3000.000</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
@ -428,19 +421,22 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>130</y>
|
||||
<width>401</width>
|
||||
<width>420</width>
|
||||
<height>271</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Morse Text</string>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>420</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QTextEdit" name="text"/>
|
||||
|
@ -51,6 +51,7 @@ void MorseDecoderSettings::resetToDefaults()
|
||||
m_udpPort = 9999;
|
||||
m_logFilename = "cw_log.txt";
|
||||
m_logEnabled = false;
|
||||
m_auto = true;
|
||||
}
|
||||
|
||||
QByteArray MorseDecoderSettings::serialize() const
|
||||
@ -77,6 +78,7 @@ QByteArray MorseDecoderSettings::serialize() const
|
||||
s.writeU32(24, m_udpPort);
|
||||
s.writeString(25, m_logFilename);
|
||||
s.writeBool(26, m_logEnabled);
|
||||
s.writeBool(27, m_auto);
|
||||
|
||||
return s.final();
|
||||
}
|
||||
@ -135,6 +137,7 @@ bool MorseDecoderSettings::deserialize(const QByteArray& data)
|
||||
|
||||
d.readString(25, &m_logFilename, "cw_log.txt");
|
||||
d.readBool(26, &m_logEnabled, false);
|
||||
d.readBool(27, &m_auto, true);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -186,6 +189,15 @@ void MorseDecoderSettings::applySettings(const QStringList& settingsKeys, const
|
||||
if (settingsKeys.contains("logFilename")) {
|
||||
m_logFilename = settings.m_logFilename;
|
||||
}
|
||||
if (settingsKeys.contains("auto")) {
|
||||
m_auto = settings.m_auto;
|
||||
}
|
||||
if (settingsKeys.contains("logEnabled")) {
|
||||
m_logEnabled = settings.m_logEnabled;
|
||||
}
|
||||
if (settingsKeys.contains("logFilename")) {
|
||||
m_logFilename = settings.m_logFilename;
|
||||
}
|
||||
}
|
||||
|
||||
QString MorseDecoderSettings::getDebugString(const QStringList& settingsKeys, bool force) const
|
||||
@ -231,6 +243,33 @@ QString MorseDecoderSettings::getDebugString(const QStringList& settingsKeys, bo
|
||||
if (settingsKeys.contains("logFilename") || force) {
|
||||
ostr << " m_logFilename: " << m_logFilename.toStdString();
|
||||
}
|
||||
if (settingsKeys.contains("auto") || force) {
|
||||
ostr << " m_auto: " << m_auto;
|
||||
}
|
||||
if (settingsKeys.contains("logEnabled") || force) {
|
||||
ostr << " m_logEnabled: " << m_logEnabled;
|
||||
}
|
||||
if (settingsKeys.contains("logFilename") || force) {
|
||||
ostr << " m_logFilename: " << m_logFilename.toStdString();
|
||||
}
|
||||
|
||||
return QString(ostr.str().c_str());
|
||||
}
|
||||
|
||||
QString MorseDecoderSettings::formatText(const QString& text)
|
||||
{
|
||||
// Format text
|
||||
QString showText = text.simplified();
|
||||
|
||||
if (text.size() > 3)
|
||||
{
|
||||
if (text.right(1)[0].isSpace()) {
|
||||
showText.append(text.right(1));
|
||||
}
|
||||
if (text.left(1)[0].isSpace()) {
|
||||
showText = text.left(1) + showText;
|
||||
}
|
||||
}
|
||||
|
||||
return showText;
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ struct MorseDecoderSettings
|
||||
uint16_t m_udpPort;
|
||||
QString m_logFilename;
|
||||
bool m_logEnabled;
|
||||
bool m_auto; //!< Auto pitch and speed
|
||||
|
||||
MorseDecoderSettings();
|
||||
void resetToDefaults();
|
||||
@ -50,6 +51,8 @@ struct MorseDecoderSettings
|
||||
void applySettings(const QStringList& settingsKeys, const MorseDecoderSettings& settings);
|
||||
QString getDebugString(const QStringList& settingsKeys, bool force=false) const;
|
||||
|
||||
static QString formatText(const QString& text);
|
||||
|
||||
static const QStringList m_channelURIs;
|
||||
};
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "dsp/scopevis.h"
|
||||
#include "dsp/datafifo.h"
|
||||
|
||||
#include "morsedecoder.h"
|
||||
#include "morsedecoderworker.h"
|
||||
|
||||
MESSAGE_CLASS_DEFINITION(MorseDecoderWorker::MsgConfigureMorseDecoderWorker, Message)
|
||||
@ -28,15 +29,30 @@ MESSAGE_CLASS_DEFINITION(MorseDecoderWorker::MsgConnectFifo, Message)
|
||||
MorseDecoderWorker::MorseDecoderWorker() :
|
||||
m_dataFifo(nullptr),
|
||||
m_msgQueueToFeature(nullptr),
|
||||
m_sampleBufferSize(0),
|
||||
m_nbBytes(0)
|
||||
m_auto(false),
|
||||
m_pitchHz(-1),
|
||||
m_speedWPM(-1)
|
||||
{
|
||||
qDebug("MorseDecoderWorker::MorseDecoderWorker");
|
||||
m_ggMorseParameters = new GGMorse::Parameters{
|
||||
48000.0f, // sampleRateInp: capture sample rate
|
||||
48000.0f, // sampleRateOut: playback sample rate
|
||||
GGMorse::kDefaultSamplesPerFrame, // samplesPerFrame: number of samples per audio frame (128)
|
||||
GGMORSE_SAMPLE_FORMAT_I16, // sampleFormatInp: format of the captured audio samples
|
||||
GGMORSE_SAMPLE_FORMAT_I16 // sampleFormatOut: format of the playback audio samples
|
||||
};
|
||||
m_ggMorse = new GGMorse(*m_ggMorseParameters);
|
||||
auto parametersDecode = m_ggMorse->getDefaultParametersDecode(); // auto pitch [200, 1200 Hz], auto speed, apply low pass and high pass
|
||||
parametersDecode.applyFilterHighPass = false;
|
||||
m_ggMorse->setParametersDecode(parametersDecode);
|
||||
applySampleRate(48000);
|
||||
}
|
||||
|
||||
MorseDecoderWorker::~MorseDecoderWorker()
|
||||
{
|
||||
m_inputMessageQueue.clear();
|
||||
delete m_ggMorse;
|
||||
delete m_ggMorseParameters;
|
||||
}
|
||||
|
||||
void MorseDecoderWorker::reset()
|
||||
@ -63,31 +79,113 @@ void MorseDecoderWorker::feedPart(
|
||||
DataFifo::DataType dataType
|
||||
)
|
||||
{
|
||||
int nbBytes;
|
||||
int countBytes = end - begin;
|
||||
int bytesLeft = m_bytesBufferSize - m_bytesBufferCount;
|
||||
|
||||
switch(dataType)
|
||||
if (dataType == DataFifo::DataTypeCI16) // (re, im) -> one sample conversion
|
||||
{
|
||||
case DataFifo::DataTypeCI16:
|
||||
nbBytes = 4;
|
||||
break;
|
||||
case DataFifo::DataTypeI16:
|
||||
default:
|
||||
nbBytes = 2;
|
||||
countBytes /= 2;
|
||||
|
||||
if (countBytes != m_convBuffer.size()) {
|
||||
m_convBuffer.resize(countBytes);
|
||||
}
|
||||
|
||||
int16_t *s = (int16_t*) begin;
|
||||
int16_t *b = (int16_t*) m_convBuffer.begin();
|
||||
|
||||
for (int is = 0; is < countBytes; is++)
|
||||
{
|
||||
int32_t re = s[2*is];
|
||||
int32_t im = s[2*is+1];
|
||||
b[is] = (int16_t) ((re+im) / 2);
|
||||
}
|
||||
|
||||
if (countBytes >= bytesLeft)
|
||||
{
|
||||
std::copy(m_convBuffer.begin(), m_convBuffer.begin() + bytesLeft, m_bytesBuffer.begin() + m_bytesBufferCount); // fill buffer
|
||||
int unprocessedBytes = processBuffer(m_convBuffer);
|
||||
std::copy(m_convBuffer.begin() + bytesLeft - unprocessedBytes, m_convBuffer.end(), m_bytesBuffer.begin());
|
||||
m_bytesBufferCount = bytesLeft + unprocessedBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::copy(m_convBuffer.begin(), m_convBuffer.end(), m_bytesBuffer.begin() + m_bytesBufferCount);
|
||||
m_bytesBufferCount += countBytes;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (countBytes >= bytesLeft)
|
||||
{
|
||||
std::copy(begin, begin + bytesLeft, m_bytesBuffer.begin() + m_bytesBufferCount); // fill buffer
|
||||
int unprocessedBytes = processBuffer(m_bytesBuffer);
|
||||
std::copy(begin + bytesLeft - unprocessedBytes, end, m_bytesBuffer.begin());
|
||||
m_bytesBufferCount = bytesLeft + unprocessedBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::copy(begin, end, m_bytesBuffer.begin() + m_bytesBufferCount);
|
||||
m_bytesBufferCount += countBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int MorseDecoderWorker::processBuffer(QByteArray& bytesBuffer)
|
||||
{
|
||||
uint32_t samplesHave = bytesBuffer.size() / 2;
|
||||
uint32_t samplesTotal = bytesBuffer.size() / 2;
|
||||
int bytesLeft = 0;
|
||||
|
||||
GGMorse::CBWaveformInp cbWaveformInp = [&](void * data, uint32_t nMaxBytes)
|
||||
{
|
||||
if (samplesHave*2 < nMaxBytes)
|
||||
{
|
||||
if (samplesHave != 0)
|
||||
{
|
||||
bytesLeft = samplesHave*2;
|
||||
qDebug("MorseDecoderWorker::processBuffer::cbWaveformInp: nMaxBytes: %u / %u samples left buffer size: %u",
|
||||
nMaxBytes, samplesHave, bytesBuffer.size());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
samplesHave -= nMaxBytes/2;
|
||||
// qDebug("MorseDecoderWorker::processBuffer::cbWaveformInp: samplesTotal: %u samplesHave: %u nMaxBytes: %u",
|
||||
// samplesTotal, samplesHave, nMaxBytes);
|
||||
memcpy(data, bytesBuffer.data() + (samplesTotal - samplesHave)*2, nMaxBytes);
|
||||
return (int) nMaxBytes;
|
||||
};
|
||||
|
||||
bool result = m_ggMorse->decode(cbWaveformInp);
|
||||
|
||||
if (result)
|
||||
{
|
||||
GGMorse::TxRx dst;
|
||||
m_ggMorse->takeRxData(dst);
|
||||
QString text;
|
||||
std::for_each(
|
||||
dst.begin(),
|
||||
dst.end(),
|
||||
[&](const uint8_t c) { text.append(c); }
|
||||
);
|
||||
|
||||
const GGMorse::Statistics& stats = m_ggMorse->getStatistics();
|
||||
m_pitchHz = stats.estimatedPitch_Hz;
|
||||
m_speedWPM = stats.estimatedSpeed_wpm;
|
||||
|
||||
if (m_msgQueueToFeature)
|
||||
{
|
||||
MorseDecoder::MsgReportText *msg = MorseDecoder::MsgReportText::create(text);
|
||||
msg->m_costFunction = stats.costFunction;
|
||||
msg->m_estimatedPitchHz = m_settings.m_auto ? stats.estimatedPitch_Hz : m_pitchHz;
|
||||
msg->m_estimatedSpeedWPM = m_settings.m_auto ? stats.estimatedSpeed_wpm : m_speedWPM;
|
||||
msg->m_signalThreshold = stats.signalThreshold;
|
||||
m_msgQueueToFeature->push(msg);
|
||||
}
|
||||
}
|
||||
|
||||
m_nbBytes = nbBytes;
|
||||
int countSamples = (end - begin) / nbBytes;
|
||||
|
||||
if (countSamples > m_sampleBufferSize)
|
||||
{
|
||||
m_sampleBuffer.resize(countSamples);
|
||||
m_sampleBufferSize = countSamples;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// for (int i = 0; i < countSamples; i++) {
|
||||
// processSample(dataType, begin, countSamples, i);
|
||||
// }
|
||||
return bytesLeft;
|
||||
}
|
||||
|
||||
void MorseDecoderWorker::handleInputMessages()
|
||||
@ -120,7 +218,7 @@ bool MorseDecoderWorker::handleMessage(const Message& cmd)
|
||||
MsgConnectFifo& msg = (MsgConnectFifo&) cmd;
|
||||
m_dataFifo = msg.getFifo();
|
||||
bool doConnect = msg.getConnect();
|
||||
qDebug("DemodAnalyzerWorker::handleMessage: MsgConnectFifo: %s", (doConnect ? "connect" : "disconnect"));
|
||||
qDebug("MorseDecoderWorker::handleMessage: MsgConnectFifo: %s", (doConnect ? "connect" : "disconnect"));
|
||||
|
||||
if (doConnect) {
|
||||
QObject::connect(
|
||||
@ -153,6 +251,25 @@ void MorseDecoderWorker::applySettings(const MorseDecoderSettings& settings, con
|
||||
{
|
||||
qDebug() << "MorseDecoderWorker::applySettings:" << settings.getDebugString(settingsKeys, force) << force;
|
||||
|
||||
if (settingsKeys.contains("auto") || force)
|
||||
{
|
||||
auto parametersDecode = m_ggMorse->getDefaultParametersDecode();
|
||||
parametersDecode.applyFilterHighPass = false;
|
||||
|
||||
if (settings.m_auto)
|
||||
{
|
||||
parametersDecode.frequency_hz = -1.0f; // auto
|
||||
parametersDecode.speed_wpm = -1.0f; // auto
|
||||
}
|
||||
else
|
||||
{
|
||||
parametersDecode.frequency_hz = m_pitchHz;
|
||||
parametersDecode.speed_wpm = m_speedWPM;
|
||||
}
|
||||
|
||||
m_ggMorse->setParametersDecode(parametersDecode);
|
||||
}
|
||||
|
||||
if (force) {
|
||||
m_settings = settings;
|
||||
} else {
|
||||
@ -163,7 +280,16 @@ void MorseDecoderWorker::applySettings(const MorseDecoderSettings& settings, con
|
||||
|
||||
void MorseDecoderWorker::applySampleRate(int sampleRate)
|
||||
{
|
||||
QMutexLocker mutexLocker(&m_mutex);
|
||||
m_sinkSampleRate = sampleRate;
|
||||
m_ggMorseParameters->sampleRateInp = sampleRate;
|
||||
int ggMorseBlockSize = (sampleRate / GGMorse::kBaseSampleRate)*GGMorse::kDefaultSamplesPerFrame;
|
||||
// m_bytesBufferSize = (GGMorse::kBaseSampleRate/GGMorse::kDefaultSamplesPerFrame)*ggMorseBlockSize*10; // ~10s
|
||||
m_bytesBufferSize = sampleRate*10 + ggMorseBlockSize;
|
||||
m_bytesBuffer.resize(m_bytesBufferSize);
|
||||
m_bytesBufferCount = 0;
|
||||
qDebug("MorseDecoderWorker::applySampleRate: m_sinkSampleRate: %d ggMorseBlockSize: %d m_bytesBufferSize: %d",
|
||||
m_sinkSampleRate, ggMorseBlockSize, m_bytesBufferSize);
|
||||
}
|
||||
|
||||
void MorseDecoderWorker::handleData()
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ggmorse/ggmorse.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QRecursiveMutex>
|
||||
#include <QByteArray>
|
||||
@ -31,7 +33,6 @@
|
||||
|
||||
#include "morsedecodersettings.h"
|
||||
|
||||
|
||||
class MorseDecoderWorker : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
@ -100,10 +101,16 @@ private:
|
||||
MessageQueue *m_msgQueueToFeature; //!< Queue to report channel change to main feature object
|
||||
MorseDecoderSettings m_settings;
|
||||
double m_magsq;
|
||||
SampleVector m_sampleBuffer;
|
||||
int m_sampleBufferSize;
|
||||
int m_nbBytes;
|
||||
QByteArray m_bytesBuffer;
|
||||
int m_bytesBufferSize;
|
||||
int m_bytesBufferCount;
|
||||
QByteArray m_convBuffer;
|
||||
QRecursiveMutex m_mutex;
|
||||
GGMorse::Parameters *m_ggMorseParameters;
|
||||
GGMorse *m_ggMorse;
|
||||
bool m_auto;
|
||||
float m_pitchHz;
|
||||
float m_speedWPM;
|
||||
|
||||
void feedPart(
|
||||
const QByteArray::const_iterator& begin,
|
||||
@ -112,6 +119,31 @@ private:
|
||||
);
|
||||
|
||||
bool handleMessage(const Message& cmd);
|
||||
int processBuffer(QByteArray& bytesBuffer); //!< return the number of bytes left
|
||||
|
||||
// inline void processSample(
|
||||
// DataFifo::DataType dataType,
|
||||
// const QByteArray::const_iterator& begin,
|
||||
// int nbBytesPerSample,
|
||||
// int i
|
||||
// )
|
||||
// {
|
||||
// int16_t *s = (int16_t*) begin + (i*nbBytesPerSample);
|
||||
|
||||
// switch(dataType)
|
||||
// {
|
||||
// case DataFifo::DataTypeI16: {
|
||||
// m_sampleBuffer[i] = *s;
|
||||
// }
|
||||
// break;
|
||||
// case DataFifo::DataTypeCI16: {
|
||||
// int32_t re = s[2*i];
|
||||
// int32_t im = s[2*i+1];
|
||||
// m_sampleBuffer[i] = (int16_t) ((re+im) / 2);
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
private slots:
|
||||
void handleInputMessages();
|
||||
|
Loading…
Reference in New Issue
Block a user