raspikeyer/src/keyer.cpp

348 lines
11 KiB
C++
Raw Normal View History

2024-02-10 23:51:55 +01:00
#include <stdio.h>
#include "pico/stdlib.h"
2024-02-09 12:20:30 +01:00
#include "keyer.h"
2024-02-12 15:57:50 +01:00
#include "sidetone.h"
2024-02-23 23:26:59 +01:00
#include "morse.h"
2024-02-09 12:20:30 +01:00
2024-02-12 13:48:58 +01:00
extern const uint LED_PIN;
2024-02-10 23:51:55 +01:00
extern const uint LEFT_PADDLE_PIN;
2024-02-12 13:48:58 +01:00
extern const uint RIGHT_PADDLE_PIN;
2024-02-18 17:14:47 +01:00
extern const uint BUZZER_PIN;
2024-02-18 10:21:54 +01:00
extern const uint CW_OUT_PIN;
2024-02-23 09:18:02 +01:00
// extern const uint AUDIO_OUT_PIN;
2024-02-10 23:51:55 +01:00
2024-02-12 15:57:50 +01:00
const uint SIDETONE_FREQ = 622;
2024-02-10 23:51:55 +01:00
bool left_paddle_pressed()
{
2024-02-16 20:56:03 +01:00
if (!gpio_get(LEFT_PADDLE_PIN)) {
2024-02-10 23:51:55 +01:00
return true;
}
return false;
}
2024-02-12 13:48:58 +01:00
bool right_paddle_pressed()
{
2024-02-16 20:56:03 +01:00
if (!gpio_get(RIGHT_PADDLE_PIN)) {
2024-02-12 13:48:58 +01:00
return true;
}
return false;
}
uint64_t calcElementDurationUs(uint8_t wpm)
{
uint64_t duration = static_cast<uint64_t>(1.2 / static_cast<uint64_t>(wpm) * 1000 * 1000);
return duration;
}
2024-02-21 12:07:21 +01:00
Keyer::Keyer(uint8_t wpm, Mode mode) : m_wpm(wpm), m_mode(mode), m_buzzer(BUZZER_PIN) //, m_audioOut(AUDIO_OUT_PIN)
2024-02-18 17:14:47 +01:00
{
m_elementDuration = calcElementDurationUs(m_wpm);
}
2024-02-12 13:48:58 +01:00
2024-02-14 11:05:33 +01:00
void Keyer::setSpeed(uint8_t wpm)
{
m_wpm = wpm;
m_elementDuration = calcElementDurationUs(wpm);
}
2024-02-25 19:39:13 +01:00
void Keyer::sendMessage(std::string msg)
2024-02-23 14:29:36 +01:00
{
2024-02-23 23:26:59 +01:00
std::string morse = messageToMorse(msg);
2024-02-25 21:37:37 +01:00
2024-02-26 15:20:28 +01:00
for (char c : morse) {
2024-02-23 14:29:36 +01:00
m_messageQueue.push(c);
}
}
2024-02-26 15:57:13 +01:00
void Keyer::sendCharacter(char ch)
{
std::string morse = charToMorse(ch);
for (char c : morse) {
m_messageQueue.push(c);
}
}
2024-02-09 12:20:30 +01:00
void Keyer::run()
{
2024-02-12 13:48:58 +01:00
auto timestamp = get_absolute_time();
2024-02-23 14:45:42 +01:00
// If there is some message to send …
2024-02-26 15:57:13 +01:00
if (!m_messageQueue.empty() || m_messageKeyingState != MessageState::Wait) {
2024-02-23 14:29:36 +01:00
// Stop all paddle keying, if necessary
2024-02-23 14:45:42 +01:00
if (m_paddleKeyingState != State::Wait) {
2024-02-26 15:57:13 +01:00
gpio_put(LED_PIN, 0);
gpio_put(CW_OUT_PIN, 0);
m_buzzer.off();
m_keyNextIambicB = false;
m_currentlyPausing = false;
m_currentlyKeying = false;
m_previousState = State::Abort;
m_paddleKeyingState = State::Wait;
2024-02-23 14:29:36 +01:00
}
switch (m_messageKeyingState) {
case MessageState::Wait:
m_messageChar = m_messageQueue.front();
2024-02-23 14:45:42 +01:00
m_messageQueue.pop();
2024-02-23 14:29:36 +01:00
switch (m_messageChar) {
case '.':
m_messageKeyingState = MessageState::Dit;
break;
case '-':
2024-02-23 23:26:59 +01:00
m_messageKeyingState = MessageState::Dah;
2024-02-23 14:29:36 +01:00
break;
case 'i':
m_messageKeyingState = MessageState::IntraCharSpace;
break;
case 'c':
m_messageKeyingState = MessageState::InterCharSpace;
break;
case 'w':
m_messageKeyingState = MessageState::InterWordSpace;
break;
}
break;
case MessageState::Dit:
if (!m_currentlyKeying) {
m_currentlyKeying = true;
m_keying_until = make_timeout_time_us(m_elementDuration);
gpio_put(LED_PIN, 1);
gpio_put(CW_OUT_PIN, 1);
m_buzzer.on(SIDETONE_FREQ);
}
if (left_paddle_pressed() || right_paddle_pressed()) {
m_messageKeyingState = MessageState::Abort;
}
if (absolute_time_diff_us(timestamp, m_keying_until) <= 0) {
m_currentlyKeying = false;
gpio_put(LED_PIN, 0);
gpio_put(CW_OUT_PIN, 0);
m_buzzer.off();
m_messageKeyingState = MessageState::Wait;
}
break;
case MessageState::Dah:
if (!m_currentlyKeying) {
m_currentlyKeying = true;
m_keying_until = make_timeout_time_us(m_elementDuration * 3);
gpio_put(LED_PIN, 1);
gpio_put(CW_OUT_PIN, 1);
m_buzzer.on(SIDETONE_FREQ);
}
if (left_paddle_pressed() || right_paddle_pressed()) {
m_messageKeyingState = MessageState::Abort;
}
if (absolute_time_diff_us(timestamp, m_keying_until) <= 0) {
m_currentlyKeying = false;
gpio_put(LED_PIN, 0);
gpio_put(CW_OUT_PIN, 0);
m_buzzer.off();
m_messageKeyingState = MessageState::Wait;
}
break;
case MessageState::IntraCharSpace:
if (!m_currentlyPausing) {
m_pausing_until = make_timeout_time_us(m_elementDuration);
m_currentlyPausing = true;
}
if (left_paddle_pressed() || right_paddle_pressed()) {
m_messageKeyingState = MessageState::Abort;
}
if (absolute_time_diff_us(timestamp, m_pausing_until) <= 0) {
m_currentlyPausing = false;
m_messageKeyingState = MessageState::Wait;
}
break;
case MessageState::InterCharSpace:
if (!m_currentlyPausing) {
m_pausing_until = make_timeout_time_us(m_elementDuration * 3);
m_currentlyPausing = true;
}
if (left_paddle_pressed() || right_paddle_pressed()) {
m_messageKeyingState = MessageState::Abort;
}
if (absolute_time_diff_us(timestamp, m_pausing_until) <= 0) {
m_currentlyPausing = false;
m_messageKeyingState = MessageState::Wait;
}
break;
case MessageState::InterWordSpace:
if (!m_currentlyPausing) {
2024-02-26 15:57:13 +01:00
m_pausing_until = make_timeout_time_us(m_elementDuration * (7-3)); // 7-3, because we aleady have the InterCharSpace of 3
2024-02-23 14:29:36 +01:00
m_currentlyPausing = true;
}
if (left_paddle_pressed() || right_paddle_pressed()) {
m_messageKeyingState = MessageState::Abort;
}
if (absolute_time_diff_us(timestamp, m_pausing_until) <= 0) {
m_currentlyPausing = false;
m_messageKeyingState = MessageState::Wait;
}
break;
case MessageState::Abort:
{
gpio_put(LED_PIN, 0);
gpio_put(CW_OUT_PIN, 0);
m_buzzer.off();
m_currentlyPausing = false;
m_currentlyKeying = false;
std::queue<char> emptyQueue;
std::swap(m_messageQueue, emptyQueue);
2024-02-23 14:45:42 +01:00
m_messageKeyingState = MessageState::Wait;
2024-02-23 14:29:36 +01:00
break;
}
default:
break;
}
return;
2024-02-23 09:18:02 +01:00
}
2024-02-20 15:24:51 +01:00
// If we are in Straight key mode …
if (m_mode == Mode::Straight) {
if (left_paddle_pressed()) {
gpio_put(LED_PIN, 1);
gpio_put(CW_OUT_PIN, 1);
m_buzzer.on(SIDETONE_FREQ);
} else {
gpio_put(LED_PIN, 0);
gpio_put(CW_OUT_PIN, 0);
m_buzzer.off();
}
return;
}
// If we are in IambicA or IambicB-Mode …
2024-02-23 14:45:42 +01:00
switch (m_paddleKeyingState) {
2024-02-10 23:51:55 +01:00
case State::Wait:
2024-02-16 20:56:03 +01:00
if (left_paddle_pressed()) {
2024-02-13 10:46:11 +01:00
m_keyNextIambicB = false;
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::Dit;
2024-02-16 20:56:03 +01:00
} else if (right_paddle_pressed()) {
2024-02-13 10:46:11 +01:00
m_keyNextIambicB = false;
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::Dah;
2024-02-16 20:56:03 +01:00
} else {
2024-02-20 15:24:51 +01:00
if (m_mode == Mode::IambicB && m_keyNextIambicB) {
2024-02-13 10:46:11 +01:00
if (m_previousState == State::Dit)
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::Dah;
2024-02-13 10:46:11 +01:00
else
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::Dit;
2024-02-13 10:46:11 +01:00
m_keyNextIambicB = false;
}
}
2024-02-10 23:51:55 +01:00
break;
2024-02-11 16:26:55 +01:00
case State::Dit:
2024-02-16 20:56:03 +01:00
if (!m_currentlyKeying) {
2024-02-12 13:48:58 +01:00
m_currentlyKeying = true;
m_keying_until = make_timeout_time_us(m_elementDuration);
gpio_put(LED_PIN, 1);
2024-02-18 10:21:54 +01:00
gpio_put(CW_OUT_PIN, 1);
2024-02-18 17:14:47 +01:00
m_buzzer.on(SIDETONE_FREQ);
2024-02-23 09:18:02 +01:00
// m_audioOut.on(SIDETONE_FREQ);
2024-02-16 20:56:03 +01:00
} else {
2024-02-16 20:05:03 +01:00
// If right paddle üressed -> note for Iambic B
2024-02-16 20:56:03 +01:00
if (right_paddle_pressed() && !m_keyNextIambicB) {
2024-02-13 09:00:39 +01:00
m_keyNextIambicB = true;
2024-02-16 09:46:46 +01:00
}
2024-02-13 09:00:39 +01:00
2024-02-16 20:56:03 +01:00
if (absolute_time_diff_us(timestamp, m_keying_until) <= 0) {
2024-02-12 13:48:58 +01:00
m_currentlyKeying = false;
gpio_put(LED_PIN, 0);
2024-02-18 10:21:54 +01:00
gpio_put(CW_OUT_PIN, 0);
2024-02-18 17:14:47 +01:00
m_buzzer.off();
2024-02-23 09:18:02 +01:00
// m_audioOut.off();
2024-02-12 13:48:58 +01:00
m_previousState = State::Dit;
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::DitPause;
2024-02-12 13:48:58 +01:00
}
2024-02-11 16:26:55 +01:00
}
break;
2024-02-12 13:48:58 +01:00
case State::Dah:
2024-02-16 20:56:03 +01:00
if (!m_currentlyKeying) {
2024-02-12 13:48:58 +01:00
m_currentlyKeying = true;
m_keying_until = make_timeout_time_us(m_elementDuration * 3);
gpio_put(LED_PIN, 1);
2024-02-18 10:21:54 +01:00
gpio_put(CW_OUT_PIN, 1);
2024-02-18 17:14:47 +01:00
m_buzzer.on(SIDETONE_FREQ);
2024-02-23 09:18:02 +01:00
// m_audioOut.on(SIDETONE_FREQ);
2024-02-16 20:56:03 +01:00
} else {
2024-02-16 20:05:03 +01:00
// If left paddle pressed -> Note for Iambic B
2024-02-16 20:56:03 +01:00
if (left_paddle_pressed() && !m_keyNextIambicB) {
2024-02-13 09:00:39 +01:00
m_keyNextIambicB = true;
2024-02-16 09:46:46 +01:00
}
2024-02-13 09:00:39 +01:00
2024-02-16 20:56:03 +01:00
if (absolute_time_diff_us(timestamp, m_keying_until) <= 0) {
2024-02-12 13:48:58 +01:00
m_currentlyKeying = false;
gpio_put(LED_PIN, 0);
2024-02-18 10:21:54 +01:00
gpio_put(CW_OUT_PIN, 0);
2024-02-18 17:14:47 +01:00
m_buzzer.off();
2024-02-23 09:18:02 +01:00
// m_audioOut.off();
2024-02-12 13:48:58 +01:00
m_previousState = State::Dah;
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::DahPause;
2024-02-12 13:48:58 +01:00
}
}
break;
2024-02-16 09:46:46 +01:00
case State::DitPause:
2024-02-16 20:56:03 +01:00
if (!m_currentlyPausing) {
2024-02-12 13:48:58 +01:00
m_pausing_until = make_timeout_time_us(m_elementDuration);
2024-02-13 09:00:39 +01:00
m_currentlyPausing = true;
2024-02-16 20:56:03 +01:00
} else {
if (absolute_time_diff_us(timestamp, m_pausing_until) <= 0) {
2024-02-16 09:46:46 +01:00
m_currentlyPausing = false;
2024-02-16 20:56:03 +01:00
if (right_paddle_pressed()) {
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::Dah;
2024-02-16 09:46:46 +01:00
m_keyNextIambicB = false;
2024-02-16 20:56:03 +01:00
} else if (left_paddle_pressed()) {
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::Dit;
2024-02-16 09:46:46 +01:00
m_keyNextIambicB = false;
2024-02-16 20:56:03 +01:00
} else {
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::Wait;
2024-02-16 09:46:46 +01:00
}
}
}
2024-02-16 09:46:46 +01:00
break;
case State::DahPause:
2024-02-16 20:56:03 +01:00
if (!m_currentlyPausing) {
2024-02-16 09:46:46 +01:00
m_pausing_until = make_timeout_time_us(m_elementDuration);
m_currentlyPausing = true;
2024-02-16 20:56:03 +01:00
} else {
if (absolute_time_diff_us(timestamp, m_pausing_until) <= 0) {
m_currentlyPausing = false;
2024-02-16 09:46:46 +01:00
2024-02-16 20:56:03 +01:00
if (left_paddle_pressed()) {
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::Dit;
2024-02-16 09:46:46 +01:00
m_keyNextIambicB = false;
2024-02-16 20:56:03 +01:00
} else if (right_paddle_pressed()) {
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::Dah;
2024-02-16 09:46:46 +01:00
m_keyNextIambicB = false;
2024-02-16 20:56:03 +01:00
} else {
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::Wait;
2024-02-16 09:46:46 +01:00
}
}
2024-02-12 13:48:58 +01:00
}
2024-02-09 12:20:30 +01:00
break;
2024-02-14 11:32:09 +01:00
case State::Abort:
gpio_put(LED_PIN, 0);
2024-02-18 10:21:54 +01:00
gpio_put(CW_OUT_PIN, 0);
2024-02-18 17:14:47 +01:00
m_buzzer.off();
2024-02-23 09:18:02 +01:00
// m_audioOut.off();
2024-02-14 11:32:09 +01:00
m_keyNextIambicB = false;
m_currentlyPausing = false;
m_currentlyKeying = false;
m_previousState = State::Abort;
2024-02-23 14:45:42 +01:00
m_paddleKeyingState = State::Wait;
2024-02-14 11:32:09 +01:00
break;
2024-02-09 12:20:30 +01:00
default:
break;
}
2024-02-14 11:32:09 +01:00
}
2024-02-23 14:45:42 +01:00
void Keyer::stop() { m_paddleKeyingState = State::Abort; }