CW Keyer with GUI. Sort of works

This commit is contained in:
f4exb 2016-12-11 11:35:25 +01:00
parent 2defcc7cec
commit d3d6ebec89
10 changed files with 1067 additions and 628 deletions

View File

@ -138,6 +138,7 @@ set(sdrbase_SOURCES
sdrbase/gui/buttonswitch.cpp
sdrbase/gui/channelwindow.cpp
sdrbase/gui/colormapper.cpp
sdrbase/gui/cwkeyergui.cpp
sdrbase/gui/glscope.cpp
sdrbase/gui/glscopegui.cpp
sdrbase/gui/glshadersimple.cpp
@ -245,6 +246,7 @@ set(sdrbase_HEADERS
sdrbase/gui/buttonswitch.h
sdrbase/gui/channelwindow.h
sdrbase/gui/colormapper.h
sdrbase/gui/cwkeyergui.h
sdrbase/gui/glscope.h
sdrbase/gui/glscopegui.h
sdrbase/gui/glshadersimple.h
@ -303,6 +305,7 @@ set(sdrbase_FORMS
sdrbase/gui/aboutdialog.ui
sdrbase/gui/addpresetdialog.ui
sdrbase/gui/basicchannelsettingswidget.ui
sdrbase/gui/cwkeyergui.ui
sdrbase/gui/glscopegui.ui
sdrbase/gui/glspectrumgui.ui
sdrbase/gui/pluginsdialog.ui

View File

@ -70,9 +70,8 @@ AMMod::AMMod() :
// test CW keyer
// TODO: link to CW keyer GUI
m_cwKeyer.setSampleRate(m_config.m_audioSampleRate);
m_cwKeyer.setWPM(5);
m_cwKeyer.setText("PARIS PARIS PARIS PARIS PARIS");
m_cwKeyer.setMode(CWKeyer::CWText);
m_cwKeyer.setWPM(13);
m_cwKeyer.setMode(CWKeyer::CWNone);
}
AMMod::~AMMod()
@ -192,9 +191,6 @@ void AMMod::pullAF(Real& sample)
{
sample = 0.0f;
m_toneNco.setPhase(0);
// if (m_cwKeyer.eom()) {
// m_cwKeyer.resetText();
// }
}
break;
case AMModInputNone:

View File

@ -190,6 +190,8 @@ public:
Real getMagSq() const { return m_magsq; }
CWKeyer *getCWKeyer() { return &m_cwKeyer; }
signals:
/**
* Level changed

View File

@ -90,6 +90,7 @@ QByteArray AMModGUI::serialize() const
s.writeS32(4, ui->modPercent->value());
s.writeU32(5, m_channelMarker.getColor().rgb());
s.writeS32(6, ui->volume->value());
s.writeBlob(7, ui->cwKeyerGUI->serialize());
return s.final();
}
@ -130,6 +131,9 @@ bool AMModGUI::deserialize(const QByteArray& data)
d.readS32(6, &tmp, 10);
ui->volume->setValue(tmp);
d.readBlob(7, &bytetmp);
ui->cwKeyerGUI->deserialize(bytetmp);
blockApplySettings(false);
m_channelMarker.blockSignals(false);
@ -266,6 +270,13 @@ void AMModGUI::on_morseKeyer_toggled(bool checked)
ui->play->setEnabled(!checked); // release other source inputs
ui->tone->setEnabled(!checked); // release other source inputs
ui->mic->setEnabled(!checked);
if (checked) {
ui->cwKeyerGUI->grabKeyboard();
} else {
ui->cwKeyerGUI->releaseKeyboard();
}
m_modAFInput = checked ? AMMod::AMModInputCWTone : AMMod::AMModInputNone;
AMMod::MsgConfigureAFInput* message = AMMod::MsgConfigureAFInput::create(m_modAFInput);
m_amMod->getInputMessageQueue()->push(message);
@ -374,8 +385,11 @@ AMModGUI::AMModGUI(PluginAPI* pluginAPI, DeviceSinkAPI *deviceAPI, QWidget* pare
ui->play->setEnabled(false);
ui->play->setChecked(false);
ui->tone->setChecked(false);
ui->morseKeyer->setChecked(false);
ui->mic->setChecked(false);
ui->cwKeyerGUI->setBuddies(m_amMod->getInputMessageQueue(), m_amMod->getCWKeyer());
applySettings();
connect(m_amMod->getOutputMessageQueue(), SIGNAL(messageEnqueued()), this, SLOT(handleSourceMessages()));

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>342</width>
<height>249</height>
<height>363</height>
</rect>
</property>
<property name="sizePolicy">
@ -40,7 +40,7 @@
<x>10</x>
<y>10</y>
<width>320</width>
<height>211</height>
<height>341</height>
</rect>
</property>
<property name="minimumSize">
@ -474,6 +474,13 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="cwKeyerLayout">
<item>
<widget class="CWKeyerGUI" name="cwKeyerGUI" native="true"/>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
@ -671,6 +678,12 @@
<header>gui/levelmeter.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>CWKeyerGUI</class>
<extends>QWidget</extends>
<header>gui/cwkeyergui.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="../../../sdrbase/resources/res.qrc"/>

View File

@ -156,6 +156,7 @@ const char CWKeyer::m_asciiToMorse[128][7] = {
};
CWKeyer::CWKeyer() :
m_mutex(QMutex::Recursive),
m_sampleRate(48000),
m_textPointer(0),
m_elementPointer(0),
@ -166,6 +167,7 @@ CWKeyer::CWKeyer() :
m_dot(false),
m_dash(false),
m_elementOn(false),
m_loop(false),
m_asciiChar('\0'),
m_mode(CWKey),
m_keyIambicState(KeySilent),
@ -178,43 +180,91 @@ CWKeyer::~CWKeyer()
{
}
void CWKeyer::setSampleRate(int sampleRate)
{
m_mutex.lock();
m_sampleRate = sampleRate;
m_mutex.unlock();
setWPM(m_wpm);
}
void CWKeyer::setWPM(int wpm)
{
if ((wpm > 0) && (wpm < 21))
if ((wpm > 0) && (wpm < 27))
{
QMutexLocker mutexLocker(&m_mutex);
m_dotLength = (int) (0.24f * m_sampleRate * (5.0f / wpm));
m_wpm = wpm;
m_dotLength = (int) (0.24f * m_sampleRate * (wpm / 5.0f));
}
}
void CWKeyer::setText(const QString& text)
{
QMutexLocker mutexLocker(&m_mutex);
m_text = text;
m_textState = TextStart;
}
void CWKeyer::setMode(CWMode mode)
{
if (mode != m_mode)
{
QMutexLocker mutexLocker(&m_mutex);
if (mode == CWText)
{
m_textState = TextStart;
}
m_mode = mode;
}
}
void CWKeyer::setKey(bool key)
{
if (m_mode == CWKey)
{
qDebug() << "CWKeyer::setKey: " << key;
m_key = key;
}
}
void CWKeyer::setDot(bool dotOn)
{
if (dotOn)
if (m_mode == CWIambic)
{
m_dash = false;
m_dot = true;
}
else
{
m_dot = false;
if (dotOn)
{
m_dash = false;
m_dot = true;
}
else
{
m_dot = false;
}
}
}
void CWKeyer::setDash(bool dashOn)
{
if (dashOn)
if (m_mode == CWIambic)
{
m_dot = false;
m_dash = true;
}
else
{
m_dash = false;
if (dashOn)
{
m_dot = false;
m_dash = true;
}
else
{
m_dash = false;
}
}
}
int CWKeyer::getSample()
{
QMutexLocker mutexLocker(&m_mutex);
if (m_mode == CWKey)
{
return m_key ? 1 : 0;
@ -324,6 +374,9 @@ void CWKeyer::nextStateText()
m_elementPointer = 0;
m_textPointer = 0;
m_textState = TextStartChar;
m_key = false;
m_dot = false;
m_dash = false;
break;
case TextStartChar:
m_samplePointer = 0;
@ -410,8 +463,19 @@ void CWKeyer::nextStateText()
}
break;
case TextEnd:
if (m_loop)
{
m_textState = TextStart;
}
m_key = false;
m_dot = false;
m_dash = false;
break;
case TextStop:
default:
m_key = false;
m_dot = false;
m_dash = false;
break;
}
}

View File

@ -19,6 +19,8 @@
#define SDRBASE_DSP_CWKEYER_H_
#include <QObject>
#include <QMutex>
#include "util/export.h"
class SDRANGEL_API CWKeyer : public QObject {
@ -27,6 +29,7 @@ class SDRANGEL_API CWKeyer : public QObject {
public:
typedef enum
{
CWNone,
CWText,
CWKey,
CWIambic
@ -47,26 +50,34 @@ public:
TextElement,
TextCharSpace,
TextWordSpace,
TextEnd
TextEnd,
TextStop
} CWTextState;
CWKeyer();
~CWKeyer();
void setSampleRate(int sampleRate) { m_sampleRate = sampleRate; }
void setWPM(int wpm);
void setText(const QString& text) { m_text = text; }
void setMode(CWMode mode) { m_mode = mode; }
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
void setKey(bool key) { m_key = key; };
void setSampleRate(int sampleRate);
void setWPM(int wpm);
void setText(const QString& text);
void setMode(CWMode mode);
void setLoop(bool loop) { m_loop = loop; }
void setKey(bool key);
void setDot(bool dotOn);
void setDash(bool dashOn);
int getSample();
bool eom();
void resetText() { m_textState = TextStart; }
void stopText() { m_textState = TextStop; }
private:
QMutex m_mutex;
int m_sampleRate;
int m_wpm;
int m_dotLength; //!< dot length in samples
@ -80,6 +91,7 @@ private:
bool m_dot;
bool m_dash;
bool m_elementOn;
bool m_loop;
char m_asciiChar;
CWMode m_mode;
CWKeyIambicState m_keyIambicState;

269
sdrbase/gui/cwkeyergui.cpp Normal file
View File

@ -0,0 +1,269 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths //
// //
// 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 //
// //
// 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 <QKeyEvent>
#include <QDebug>
#include "gui/cwkeyergui.h"
#include "ui_cwkeyergui.h"
#include "dsp/cwkeyer.h"
#include "util/simpleserializer.h"
CWKeyerGUI::CWKeyerGUI(QWidget* parent) :
QWidget(parent),
ui(new Ui::CWKeyerGUI),
m_messageQueue(0),
m_cwKeyer(0),
m_key(0x30),
m_keyDot(0x31),
m_keyDash(0x32)
{
ui->setupUi(this);
}
CWKeyerGUI::~CWKeyerGUI()
{
this->releaseKeyboard(); // just in case
delete ui;
}
void CWKeyerGUI::setBuddies(MessageQueue* messageQueue, CWKeyer* cwKeyer)
{
m_messageQueue = messageQueue;
m_cwKeyer = cwKeyer;
applySettings();
}
void CWKeyerGUI::resetToDefaults()
{
m_key = false;
m_keyDot = false;
m_keyDash = false;
}
QByteArray CWKeyerGUI::serialize() const
{
SimpleSerializer s(1);
s.writeString(1, ui->cwTextEdit->text());
s.writeS32(2, ui->cwSpeed->value());
s.writeS32(3, ui->morseKeyAssign->currentIndex());
s.writeS32(4, ui->iambicKeyDotAssign->currentIndex());
s.writeS32(5, ui->iambicKeyDashAssign->currentIndex());
return s.final();
}
bool CWKeyerGUI::deserialize(const QByteArray& data)
{
SimpleDeserializer d(data);
if(!d.isValid())
{
resetToDefaults();
return false;
}
if(d.getVersion() == 1)
{
QString aString;
int aValue;
d.readString(1, &aString, "");
ui->cwTextEdit->setText(aString);
d.readS32(2, &aValue, 13);
ui->cwSpeed->setValue(aValue);
d.readS32(3, &aValue, 0);
ui->morseKeyAssign->setCurrentIndex(aValue);
d.readS32(4, &aValue, 1);
ui->iambicKeyDotAssign->setCurrentIndex(aValue);
d.readS32(5, &aValue, 2);
ui->iambicKeyDashAssign->setCurrentIndex(aValue);
applySettings();
return true;
}
}
// === SLOTS ==================================================================
void CWKeyerGUI::on_cwTextClear_clicked(bool checked)
{
ui->cwTextEdit->clear();
m_cwKeyer->setText("");
}
void CWKeyerGUI::on_cwTextEdit_editingFinished()
{
m_cwKeyer->setText(ui->cwTextEdit->text());
}
void CWKeyerGUI::on_cwSpeed_valueChanged(int value)
{
ui->cwSpeedText->setText(QString("%1").arg(value));
m_cwKeyer->setWPM(value);
}
void CWKeyerGUI::on_morseKey_toggled(bool checked)
{
//ui->morseKey->setEnabled(!checked);
ui->iambicKey->setEnabled(!checked);
ui->playStop->setEnabled(!checked);
m_cwKeyer->setMode(checked ? CWKeyer::CWKey : CWKeyer::CWNone);
}
void CWKeyerGUI::on_morseKeyAssign_currentIndexChanged(int index)
{
if ((index >= 0) && (index < 9)) {
m_key = 0x30 + index;
}
}
void CWKeyerGUI::on_iambicKey_toggled(bool checked)
{
ui->morseKey->setEnabled(!checked);
//ui->iambicKey->setEnabled(!checked);
ui->playStop->setEnabled(!checked);
m_cwKeyer->setMode(checked ? CWKeyer::CWIambic : CWKeyer::CWNone);
}
void CWKeyerGUI::on_iambicKeyDotAssign_currentIndexChanged(int index)
{
if ((index >= 0) && (index < 9)) {
m_keyDot = 0x30 + index;
}
}
void CWKeyerGUI::on_iambicKeyDashAssign_currentIndexChanged(int index)
{
if ((index >= 0) && (index < 9)) {
m_keyDash = 0x30 + index;
}
}
void CWKeyerGUI::on_playText_toggled(bool checked)
{
ui->morseKey->setEnabled(!checked);
ui->iambicKey->setEnabled(!checked);
//ui->playStop->setEnabled(!checked);
m_cwKeyer->setMode(checked ? CWKeyer::CWText : CWKeyer::CWNone);
}
void CWKeyerGUI::on_playLoop_toggled(bool checked)
{
m_cwKeyer->setLoop(checked);
}
void CWKeyerGUI::on_playStop_toggled(bool checked)
{
if (checked)
{
m_cwKeyer->resetText();
}
else
{
m_cwKeyer->stopText();
}
}
// === End SLOTS ==============================================================
void CWKeyerGUI::applySettings()
{
int value;
m_cwKeyer->setText(ui->cwTextEdit->text());
value = ui->cwSpeed->value();
ui->cwSpeedText->setText(QString("%1").arg(value));
m_cwKeyer->setWPM(value);
value = ui->morseKeyAssign->currentIndex();
if ((value >= 0) && (value < 9)) {
m_key = 0x30 + value;
}
value = ui->iambicKeyDotAssign->currentIndex();
if ((value >= 0) && (value < 9)) {
m_keyDot = 0x30 + value;
}
value = ui->iambicKeyDashAssign->currentIndex();
if ((value >= 0) && (value < 9)) {
m_keyDash = 0x30 + value;
}
}
void CWKeyerGUI::keyPressEvent(QKeyEvent* keyEvent)
{
int key = keyEvent->key();
// Escape halts CW engine and releases keyboard immediately
// Thus keyboard cannot be left stuck
if (key == Qt::Key_Escape)
{
m_cwKeyer->setMode(CWKeyer::CWNone);
ui->morseKey->setChecked(false);
ui->iambicKey->setChecked(false);
ui->playStop->setChecked(false);
ui->morseKey->setEnabled(true);
ui->iambicKey->setEnabled(true);
ui->playStop->setEnabled(true);
this->releaseKeyboard();
return;
}
if (!keyEvent->isAutoRepeat())
{
qDebug() << "CWKeyerGUI::keyPressEvent: key"
<< ": " << key;
if (key == m_key)
{
m_cwKeyer->setKey(true);
}
else if (key == m_keyDot)
{
m_cwKeyer->setDot(true);
}
else if (key == m_keyDash)
{
m_cwKeyer->setDash(true);
}
}
}
void CWKeyerGUI::keyReleaseEvent(QKeyEvent* keyEvent)
{
if (!keyEvent->isAutoRepeat())
{
qDebug() << "CWKeyerGUI::keyReleaseEvent: key"
<< ": " << keyEvent->key();
if (keyEvent->key() == m_key)
{
m_cwKeyer->setKey(false);
}
else if (keyEvent->key() == m_keyDot)
{
m_cwKeyer->setDot(false);
}
else if (keyEvent->key() == m_keyDash)
{
m_cwKeyer->setDash(false);
}
}
}

75
sdrbase/gui/cwkeyergui.h Normal file
View File

@ -0,0 +1,75 @@
///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2016 F4EXB //
// written by Edouard Griffiths //
// //
// 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 //
// //
// 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 SDRBASE_GUI_CWKEYERGUI_H_
#define SDRBASE_GUI_CWKEYERGUI_H_
#include <QWidget>
#include "dsp/dsptypes.h"
#include "util/export.h"
namespace Ui {
class CWKeyerGUI;
}
class MessageQueue;
class CWKeyer;
class SDRANGEL_API CWKeyerGUI : public QWidget {
Q_OBJECT
public:
explicit CWKeyerGUI(QWidget* parent = NULL);
~CWKeyerGUI();
void setBuddies(MessageQueue* messageQueue, CWKeyer* cwKeyer);
void resetToDefaults();
QByteArray serialize() const;
bool deserialize(const QByteArray& data);
protected:
void keyPressEvent(QKeyEvent* keyEvent);
void keyReleaseEvent(QKeyEvent* keyEvent);
private:
Ui::CWKeyerGUI* ui;
MessageQueue* m_messageQueue;
CWKeyer* m_cwKeyer;
int m_key;
int m_keyDot;
int m_keyDash;
void applySettings();
private slots:
void on_cwTextClear_clicked(bool checked);
void on_cwTextEdit_editingFinished();
void on_cwSpeed_valueChanged(int value);
void on_morseKey_toggled(bool checked);
void on_morseKeyAssign_currentIndexChanged(int index);
void on_iambicKey_toggled(bool checked);
void on_iambicKeyDotAssign_currentIndexChanged(int index);
void on_iambicKeyDashAssign_currentIndexChanged(int index);
void on_playText_toggled(bool checked);
void on_playLoop_toggled(bool checked);
void on_playStop_toggled(bool checked);
};
#endif /* SDRBASE_GUI_CWKEYERGUI_H_ */

File diff suppressed because it is too large Load Diff