diff --git a/plugins/channeltx/modam/ammod.cpp b/plugins/channeltx/modam/ammod.cpp index 5647cac76..e290541b3 100644 --- a/plugins/channeltx/modam/ammod.cpp +++ b/plugins/channeltx/modam/ammod.cpp @@ -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(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; diff --git a/plugins/channeltx/modam/ammod.h b/plugins/channeltx/modam/ammod.h index e832f12d5..f6ee5a445 100644 --- a/plugins/channeltx/modam/ammod.h +++ b/plugins/channeltx/modam/ammod.h @@ -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(); diff --git a/plugins/channeltx/modam/ammodgui.cpp b/plugins/channeltx/modam/ammodgui.cpp index 362245229..58c9be86b 100644 --- a/plugins/channeltx/modam/ammodgui.cpp +++ b/plugins/channeltx/modam/ammodgui.cpp @@ -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); diff --git a/plugins/channeltx/modam/ammodgui.h b/plugins/channeltx/modam/ammodgui.h index 5e9dbc73a..e2ec7ed18 100644 --- a/plugins/channeltx/modam/ammodgui.h +++ b/plugins/channeltx/modam/ammodgui.h @@ -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); diff --git a/plugins/channeltx/modam/ammodgui.ui b/plugins/channeltx/modam/ammodgui.ui index 74d1a2f84..6e57945a9 100644 --- a/plugins/channeltx/modam/ammodgui.ui +++ b/plugins/channeltx/modam/ammodgui.ui @@ -7,7 +7,7 @@ 0 0 342 - 195 + 249 @@ -40,7 +40,7 @@ 10 10 320 - 161 + 211 @@ -377,6 +377,20 @@ + + + + Morse keyer at tone frequency + + + ... + + + + :/morsekey.png:/morsekey.png + + + @@ -634,6 +648,11 @@ + + ButtonSwitch + QToolButton +
gui/buttonswitch.h
+
RollupWidget QWidget @@ -646,11 +665,6 @@
gui/valuedial.h
1
- - ButtonSwitch - QToolButton -
gui/buttonswitch.h
-
LevelMeterVU QWidget diff --git a/sdrbase/dsp/cwkeyer.cpp b/sdrbase/dsp/cwkeyer.cpp index 675a07af5..ab0e76b2e 100644 --- a/sdrbase/dsp/cwkeyer.cpp +++ b/sdrbase/dsp/cwkeyer.cpp @@ -16,6 +16,7 @@ /////////////////////////////////////////////////////////////////////////////////// #include +#include #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; diff --git a/sdrbase/dsp/cwkeyer.h b/sdrbase/dsp/cwkeyer.h index b1f844795..33b8aaf90 100644 --- a/sdrbase/dsp/cwkeyer.h +++ b/sdrbase/dsp/cwkeyer.h @@ -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(); diff --git a/sdrbase/dsp/nco.h b/sdrbase/dsp/nco.h index 8b3f0317d..3d98a4724 100644 --- a/sdrbase/dsp/nco.h +++ b/sdrbase/dsp/nco.h @@ -38,6 +38,7 @@ public: NCO(); void setFreq(Real freq, Real sampleRate); + void setPhase(int phase) { m_phase = phase; } void nextPhase() //!< Increment phase {