CW Keyer: test with AM modulator and fixes

This commit is contained in:
f4exb 2016-12-10 08:12:16 +01:00
parent d6c9b2f707
commit 2defcc7cec
8 changed files with 201 additions and 134 deletions

View File

@ -66,6 +66,13 @@ AMMod::AMMod() :
m_toneNco.setFreq(1000.0, m_config.m_audioSampleRate);
DSPEngine::instance()->addAudioSource(&m_audioFifo);
// 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);
}
AMMod::~AMMod()
@ -176,6 +183,20 @@ void AMMod::pullAF(Real& sample)
m_audioFifo.read(reinterpret_cast<quint8*>(audioSample), 1, 10);
sample = ((audioSample[0] + audioSample[1]) / 65536.0f) * m_running.m_volumeFactor;
break;
case AMModInputCWTone:
if (m_cwKeyer.getSample())
{
sample = m_toneNco.next();
}
else
{
sample = 0.0f;
m_toneNco.setPhase(0);
// if (m_cwKeyer.eom()) {
// m_cwKeyer.resetText();
// }
}
break;
case AMModInputNone:
default:
sample = 0.0f;
@ -331,6 +352,11 @@ void AMMod::apply()
m_settingsMutex.unlock();
}
if (m_config.m_audioSampleRate != m_running.m_audioSampleRate)
{
m_cwKeyer.setSampleRate(m_config.m_audioSampleRate);
}
m_running.m_outputSampleRate = m_config.m_outputSampleRate;
m_running.m_inputFrequencyOffset = m_config.m_inputFrequencyOffset;
m_running.m_rfBandwidth = m_config.m_rfBandwidth;

View File

@ -27,6 +27,7 @@
#include "dsp/interpolator.h"
#include "dsp/movingaverage.h"
#include "dsp/agc.h"
#include "dsp/cwkeyer.h"
#include "audio/audiofifo.h"
#include "util/message.h"
@ -39,7 +40,8 @@ public:
AMModInputNone,
AMModInputTone,
AMModInputFile,
AMModInputAudio
AMModInputAudio,
AMModInputCWTone
} AMModInputAF;
class MsgConfigureFileSourceName : public Message
@ -306,6 +308,8 @@ private:
quint32 m_levelCalcCount;
Real m_peakLevel;
Real m_levelSum;
CWKeyer m_cwKeyer;
static const int m_levelNbSamples;
void apply();

View File

@ -242,6 +242,7 @@ void AMModGUI::on_playLoop_toggled(bool checked)
void AMModGUI::on_play_toggled(bool checked)
{
ui->tone->setEnabled(!checked); // release other source inputs
ui->morseKeyer->setEnabled(!checked);
ui->mic->setEnabled(!checked);
m_modAFInput = checked ? AMMod::AMModInputFile : AMMod::AMModInputNone;
AMMod::MsgConfigureAFInput* message = AMMod::MsgConfigureAFInput::create(m_modAFInput);
@ -253,15 +254,27 @@ void AMModGUI::on_play_toggled(bool checked)
void AMModGUI::on_tone_toggled(bool checked)
{
ui->play->setEnabled(!checked); // release other source inputs
ui->morseKeyer->setEnabled(!checked);
ui->mic->setEnabled(!checked);
m_modAFInput = checked ? AMMod::AMModInputTone : AMMod::AMModInputNone;
AMMod::MsgConfigureAFInput* message = AMMod::MsgConfigureAFInput::create(m_modAFInput);
m_amMod->getInputMessageQueue()->push(message);
}
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);
m_modAFInput = checked ? AMMod::AMModInputCWTone : AMMod::AMModInputNone;
AMMod::MsgConfigureAFInput* message = AMMod::MsgConfigureAFInput::create(m_modAFInput);
m_amMod->getInputMessageQueue()->push(message);
}
void AMModGUI::on_mic_toggled(bool checked)
{
ui->play->setEnabled(!checked); // release other source inputs
ui->morseKeyer->setEnabled(!checked);
ui->tone->setEnabled(!checked); // release other source inputs
m_modAFInput = checked ? AMMod::AMModInputAudio : AMMod::AMModInputNone;
AMMod::MsgConfigureAFInput* message = AMMod::MsgConfigureAFInput::create(m_modAFInput);

View File

@ -68,6 +68,7 @@ private slots:
void on_toneFrequency_valueChanged(int value);
void on_mic_toggled(bool checked);
void on_play_toggled(bool checked);
void on_morseKeyer_toggled(bool checked);
void on_playLoop_toggled(bool checked);
void on_navTimeSlider_valueChanged(int value);

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>342</width>
<height>195</height>
<height>249</height>
</rect>
</property>
<property name="sizePolicy">
@ -40,7 +40,7 @@
<x>10</x>
<y>10</y>
<width>320</width>
<height>161</height>
<height>211</height>
</rect>
</property>
<property name="minimumSize">
@ -377,6 +377,20 @@
</property>
</widget>
</item>
<item>
<widget class="ButtonSwitch" name="morseKeyer">
<property name="toolTip">
<string>Morse keyer at tone frequency</string>
</property>
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../sdrbase/resources/res.qrc">
<normaloff>:/morsekey.png</normaloff>:/morsekey.png</iconset>
</property>
</widget>
</item>
<item>
<widget class="QDial" name="toneFrequency">
<property name="maximumSize">
@ -634,6 +648,11 @@
</widget>
</widget>
<customwidgets>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>RollupWidget</class>
<extends>QWidget</extends>
@ -646,11 +665,6 @@
<header>gui/valuedial.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>ButtonSwitch</class>
<extends>QToolButton</extends>
<header>gui/buttonswitch.h</header>
</customwidget>
<customwidget>
<class>LevelMeterVU</class>
<extends>QWidget</extends>

View File

@ -16,6 +16,7 @@
///////////////////////////////////////////////////////////////////////////////////
#include <QChar>
#include <QDebug>
#include "cwkeyer.h"
/**
@ -23,135 +24,135 @@
* 1: dash
* -1: end of sequence
*/
const char CWKeyer::m_asciiToMorse[][128] = {
{-1}, // 0
{-1}, // 1
{-1}, // 2
{-1}, // 3
{-1}, // 4
{-1}, // 5
{-1}, // 6
{-1}, // 7
{-1}, // 8
{-1}, // 9
{-1}, // 10
{-1}, // 11
{-1}, // 12
{-1}, // 13
{-1}, // 14
{-1}, // 15
{-1}, // 16
{-1}, // 17
{-1}, // 18
{-1}, // 19
{-1}, // 20
{-1}, // 21
{-1}, // 22
{-1}, // 23
{-1}, // 24
{-1}, // 25
{-1}, // 26
{-1}, // 27
{-1}, // 28
{-1}, // 29
{-1}, // 30
{-1}, // 31
{-1}, // 32 space is treated as word separator
const char CWKeyer::m_asciiToMorse[128][7] = {
{-1,0,0,0,0,0,0}, // 0
{-1,0,0,0,0,0,0}, // 1
{-1,0,0,0,0,0,0}, // 2
{-1,0,0,0,0,0,0}, // 3
{-1,0,0,0,0,0,0}, // 4
{-1,0,0,0,0,0,0}, // 5
{-1,0,0,0,0,0,0}, // 6
{-1,0,0,0,0,0,0}, // 7
{-1,0,0,0,0,0,0}, // 8
{-1,0,0,0,0,0,0}, // 9
{-1,0,0,0,0,0,0}, // 10
{-1,0,0,0,0,0,0}, // 11
{-1,0,0,0,0,0,0}, // 12
{-1,0,0,0,0,0,0}, // 13
{-1,0,0,0,0,0,0}, // 14
{-1,0,0,0,0,0,0}, // 15
{-1,0,0,0,0,0,0}, // 16
{-1,0,0,0,0,0,0}, // 17
{-1,0,0,0,0,0,0}, // 18
{-1,0,0,0,0,0,0}, // 19
{-1,0,0,0,0,0,0}, // 20
{-1,0,0,0,0,0,0}, // 21
{-1,0,0,0,0,0,0}, // 22
{-1,0,0,0,0,0,0}, // 23
{-1,0,0,0,0,0,0}, // 24
{-1,0,0,0,0,0,0}, // 25
{-1,0,0,0,0,0,0}, // 26
{-1,0,0,0,0,0,0}, // 27
{-1,0,0,0,0,0,0}, // 28
{-1,0,0,0,0,0,0}, // 29
{-1,0,0,0,0,0,0}, // 30
{-1,0,0,0,0,0,0}, // 31
{-1,0,0,0,0,0,0}, // 32 space is treated as word separator
{1,0,1,0,1,1,-1}, // 33 !
{0,1,0,0,1,0,-1}, // 34 "
{-1}, // 35
{-1}, // 36
{-1}, // 37
{-1}, // 38
{-1,0,0,0,0,0,0}, // 35
{-1,0,0,0,0,0,0}, // 36
{-1,0,0,0,0,0,0}, // 37
{-1,0,0,0,0,0,0}, // 38
{0,1,1,1,1,0,-1}, // 39 '
{1,0,1,1,0,1,-1}, // 40 (
{1,0,1,1,0,1,-1}, // 41 )
{-1}, // 42
{0,1,0,1,0,-1}, // 43 +
{-1,0,0,0,0,0,0}, // 42
{0,1,0,1,0,-1,0}, // 43 +
{1,1,0,0,1,1,-1}, // 44 ,
{1,0,0,0,0,1,-1}, // 45 -
{0,1,0,1,0,1,-1}, // 46 .
{1,0,0,1,0,-1}, // 47 /
{1,1,1,1,1,-1}, // 48 0
{0,1,1,1,1,-1}, // 49 1
{0,0,1,1,1,-1}, // 50 2
{0,0,0,1,1,-1}, // 51 3
{0,0,0,0,1,-1}, // 52 4
{0,0,0,0,0,-1}, // 53 5
{1,0,0,0,0,-1}, // 54 6
{1,1,0,0,0,-1}, // 55 7
{1,1,1,0,0,-1}, // 56 8
{1,1,1,1,0,-1}, // 57 9
{1,0,0,1,0,-1,0}, // 47 /
{1,1,1,1,1,-1,0}, // 48 0
{0,1,1,1,1,-1,0}, // 49 1
{0,0,1,1,1,-1,0}, // 50 2
{0,0,0,1,1,-1,0}, // 51 3
{0,0,0,0,1,-1,0}, // 52 4
{0,0,0,0,0,-1,0}, // 53 5
{1,0,0,0,0,-1,0}, // 54 6
{1,1,0,0,0,-1,0}, // 55 7
{1,1,1,0,0,-1,0}, // 56 8
{1,1,1,1,0,-1,0}, // 57 9
{1,1,1,0,0,0,-1}, // 58 :
{1,0,1,0,1,0,-1}, // 59 ;
{-1}, // 60 <
{1,0,0,0,1,-1}, // 61 =
{-1}, // 62 >
{-1,0,0,0,0,0,0}, // 60 <
{1,0,0,0,1,-1,0}, // 61 =
{-1,0,0,0,0,0,0}, // 62 >
{0,0,1,1,0,0,-1}, // 63 ?
{0,1,1,0,1,0,-1}, // 64 @
{0,1,-1}, // 65 A
{1,0,0,0,-1}, // 66 B
{1,0,1,0,-1}, // 67 C
{1,0,0,-1}, // 68 D
{0,-1}, // 69 E
{0,0,1,0,-1}, // 70 F
{1,1,0,-1}, // 71 G
{0,0,0,0,-1}, // 72 H
{0,0,-1}, // 73 I
{0,1,1,1,-1}, // 74 J
{1,0,1,-1}, // 75 K
{0,1,0,0,-1}, // 76 L
{1,1,-1}, // 77 M
{1,0,-1}, // 78 N
{1,1,1,-1}, // 79 O
{0,1,1,0,-1}, // 80 P
{1,1,0,1,-1}, // 81 Q
{0,1,0,-1}, // 82 R
{0,0,0,-1}, // 83 S
{1,-1}, // 84 T
{0,0,1,-1}, // 85 U
{0,0,0,1,-1}, // 86 V
{0,1,1,-1}, // 87 W
{1,0,0,1,-1}, // 88 X
{1,0,1,1,-1}, // 89 Y
{1,1,0,0,-1}, // 90 Z
{-1}, // 91 [
{-1}, // 92 \
{-1}, // 93 ]
{-1}, // 94 ^
{-1}, // 95 _
{-1}, // 96 `
{0,1,-1}, // 97 A lowercase same as uppercase
{1,0,0,0,-1}, // 98 B
{1,0,1,0,-1}, // 99 C
{1,0,0,-1}, // 100 D
{0,-1}, // 101 E
{0,0,1,0,-1}, // 102 F
{1,1,0,-1}, // 103 G
{0,0,0,0,-1}, // 104 H
{0,0,-1}, // 105 I
{0,1,1,1,-1}, // 106 J
{1,0,1,-1}, // 107 K
{0,1,0,0,-1}, // 108 L
{1,1,-1}, // 109 M
{1,0,-1}, // 110 N
{1,1,1,-1}, // 111 O
{0,1,1,0,-1}, // 112 P
{1,1,0,1,-1}, // 113 Q
{0,1,0,-1}, // 114 R
{0,0,0,-1}, // 115 S
{1,-1}, // 116 T
{0,0,1,-1}, // 117 U
{0,0,0,1,-1}, // 118 V
{0,1,1,-1}, // 119 W
{1,0,0,1,-1}, // 120 X
{1,0,1,1,-1}, // 121 Y
{1,1,0,0,-1}, // 122 Z
{-1}, // 123 {
{-1}, // 124 |
{-1}, // 125 }
{-1}, // 126 ~
{-1}, // 127 DEL
{0,1,-1,0,0,0,0}, // 65 A
{1,0,0,0,-1,0,0}, // 66 B
{1,0,1,0,-1,0,0}, // 67 C
{1,0,0,-1,0,0,0}, // 68 D
{0,-1,0,0,0,0,0}, // 69 E
{0,0,1,0,-1,0,0}, // 70 F
{1,1,0,-1,0,0,0}, // 71 G
{0,0,0,0,-1,0,0}, // 72 H
{0,0,-1,0,0,0,0}, // 73 I
{0,1,1,1,-1,0,0}, // 74 J
{1,0,1,-1,0,0,0}, // 75 K
{0,1,0,0,-1,0,0}, // 76 L
{1,1,-1,0,0,0,0}, // 77 M
{1,0,-1,0,0,0,0}, // 78 N
{1,1,1,-1,0,0,0}, // 79 O
{0,1,1,0,-1,0,0}, // 80 P
{1,1,0,1,-1,0,0}, // 81 Q
{0,1,0,-1,0,0,0}, // 82 R
{0,0,0,-1,0,0,0}, // 83 S
{1,-1,0,0,0,0,0}, // 84 T
{0,0,1,-1,0,0,0}, // 85 U
{0,0,0,1,-1,0,0}, // 86 V
{0,1,1,-1,0,0,0}, // 87 W
{1,0,0,1,-1,0,0}, // 88 X
{1,0,1,1,-1,0,0}, // 89 Y
{1,1,0,0,-1,0,0}, // 90 Z
{-1,0,0,0,0,0,0}, // 91 [
{-1,0,0,0,0,0,0}, // 92 back /
{-1,0,0,0,0,0,0}, // 93 ]
{-1,0,0,0,0,0,0}, // 94 ^
{-1,0,0,0,0,0,0}, // 95 _
{-1,0,0,0,0,0,0}, // 96 `
{0,1,-1,0,0,0,0}, // 97 A lowercase same as uppercase
{1,0,0,0,-1,0,0}, // 98 B
{1,0,1,0,-1,0,0}, // 99 C
{1,0,0,-1,0,0,0}, // 100 D
{0,-1,0,0,0,0,0}, // 101 E
{0,0,1,0,-1,0,0}, // 102 F
{1,1,0,-1,0,0,0}, // 103 G
{0,0,0,0,-1,0,0}, // 104 H
{0,0,-1,0,0,0,0}, // 105 I
{0,1,1,1,-1,0,0}, // 106 J
{1,0,1,-1,0,0,0}, // 107 K
{0,1,0,0,-1,0,0}, // 108 L
{1,1,-1,0,0,0,0}, // 109 M
{1,0,-1,0,0,0,0}, // 110 N
{1,1,1,-1,0,0,0}, // 111 O
{0,1,1,0,-1,0,0}, // 112 P
{1,1,0,1,-1,0,0}, // 113 Q
{0,1,0,-1,0,0,0}, // 114 R
{0,0,0,-1,0,0,0}, // 115 S
{1,-1,0,0,0,0,0}, // 116 T
{0,0,1,-1,0,0,0}, // 117 U
{0,0,0,1,-1,0,0}, // 118 V
{0,1,1,-1,0,0,0}, // 119 W
{1,0,0,1,-1,0,0}, // 120 X
{1,0,1,1,-1,0,0}, // 121 Y
{1,1,0,0,-1,0,0}, // 122 Z
{-1,0,0,0,0,0,0}, // 123 {
{-1,0,0,0,0,0,0}, // 124 |
{-1,0,0,0,0,0,0}, // 125 }
{-1,0,0,0,0,0,0}, // 126 ~
{-1,0,0,0,0,0,0}, // 127 DEL
};
CWKeyer::CWKeyer() :
@ -182,7 +183,7 @@ void CWKeyer::setWPM(int wpm)
if ((wpm > 0) && (wpm < 21))
{
m_wpm = wpm;
m_dotLength = (int) (m_sampleRate / (2.4f * wpm));
m_dotLength = (int) (0.24f * m_sampleRate * (wpm / 5.0f));
}
}
@ -330,6 +331,7 @@ void CWKeyer::nextStateText()
if (m_textPointer < m_text.length())
{
m_asciiChar = (m_text.at(m_textPointer)).toLatin1();
// qDebug() << "CWKeyer::nextStateText: TextStartChar: " << m_asciiChar;
if (m_asciiChar < 0) { // non ASCII
m_asciiChar = 0;
@ -352,14 +354,15 @@ void CWKeyer::nextStateText()
break;
case TextStartElement:
m_samplePointer = 0;
if (m_asciiToMorse[m_elementPointer][m_asciiChar] == -1) // end of morse character
// qDebug() << "CWKeyer::nextStateText: TextStartElement: " << (int) m_asciiToMorse[m_asciiChar][m_elementPointer];
if (m_asciiToMorse[m_asciiChar][m_elementPointer] == -1) // end of morse character
{
m_elementPointer = 0;
m_textState = TextCharSpace;
}
else
{
if (m_asciiToMorse[m_elementPointer][m_asciiChar] == 0) // dot
if (m_asciiToMorse[m_asciiChar][m_elementPointer] == 0) // dot
{
m_dot = true;
m_dash = false;
@ -369,19 +372,23 @@ void CWKeyer::nextStateText()
m_dot = false;
m_dash = true;
}
m_textState = TextElement;
m_keyIambicState = KeySilent; // reset iambic state
nextStateIambic(); // init dash or dot
m_dot = false; // release keys
m_dash = false;
m_textState = TextElement;
m_elementPointer++;
}
break;
case TextElement:
nextStateIambic(); // dash or dot
if (m_samplePointer == 0) // done
if (m_keyIambicState == KeySilent) // done
{
m_textState = TextStartElement; // next element
}
break;
case TextCharSpace:
if (m_samplePointer < 2*m_dotLength) // 1 dot length space from element
if (m_samplePointer < 2*m_dotLength) // - 1 dot length space from element
{
m_samplePointer++;
m_key = false;
@ -392,7 +399,7 @@ void CWKeyer::nextStateText()
}
break;
case TextWordSpace:
if (m_samplePointer < 7*m_dotLength)
if (m_samplePointer < 4*m_dotLength) // - 3 dot length space from character
{
m_samplePointer++;
m_key = false;

View File

@ -55,7 +55,7 @@ public:
void setSampleRate(int sampleRate) { m_sampleRate = sampleRate; }
void setWPM(int wpm);
void setText(QString& text) { m_text = text; }
void setText(const QString& text) { m_text = text; }
void setMode(CWMode mode) { m_mode = mode; }
void setKey(bool key) { m_key = key; };
@ -64,6 +64,7 @@ public:
int getSample();
bool eom();
void resetText() { m_textState = TextStart; }
private:
int m_sampleRate;
@ -84,7 +85,7 @@ private:
CWKeyIambicState m_keyIambicState;
CWTextState m_textState;
static const char m_asciiToMorse[][128];
static const char m_asciiToMorse[128][7];
void nextStateIambic();
void nextStateText();

View File

@ -38,6 +38,7 @@ public:
NCO();
void setFreq(Real freq, Real sampleRate);
void setPhase(int phase) { m_phase = phase; }
void nextPhase() //!< Increment phase
{