2022-05-22 21:55:22 -06:00
|
|
|
#include <stddef.h>
|
|
|
|
#include "keyers.h"
|
|
|
|
|
|
|
|
#define len(t) (sizeof(t)/sizeof(*t))
|
|
|
|
|
|
|
|
// Queue Set: A Set you can shift and pop.
|
|
|
|
class QSet {
|
|
|
|
int arr[MAX_KEYER_QUEUE];
|
|
|
|
unsigned int arrlen = 0;
|
|
|
|
|
|
|
|
public:
|
|
|
|
int shift() {
|
|
|
|
if (arrlen == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
int ret = arr[0];
|
|
|
|
arrlen--;
|
|
|
|
for (int i = 0; i < arrlen; i++) {
|
|
|
|
arr[i] = arr[i+1];
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int pop() {
|
|
|
|
if (arrlen == 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
int ret = arr[arrlen];
|
|
|
|
arrlen--;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void add(int val) {
|
2022-05-28 21:23:26 -06:00
|
|
|
if (arrlen == MAX_KEYER_QUEUE) {
|
2022-05-22 21:55:22 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < arrlen; i++) {
|
2022-05-28 21:23:26 -06:00
|
|
|
if (arr[i] == val) {
|
2022-05-22 21:55:22 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
arr[arrlen] = val;
|
|
|
|
arrlen++;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class StraightKeyer: public Keyer {
|
|
|
|
public:
|
2024-10-20 17:34:41 -06:00
|
|
|
bool transmitting = false;
|
|
|
|
unsigned int ditDuration = 100;
|
2022-05-22 21:55:22 -06:00
|
|
|
bool txRelays[2];
|
|
|
|
|
2022-05-28 15:18:28 -06:00
|
|
|
StraightKeyer() {
|
2022-05-22 21:55:22 -06:00
|
|
|
this->Reset();
|
|
|
|
}
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
char *Name() {
|
|
|
|
return "straight";
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
|
|
|
|
2022-06-26 10:55:28 -06:00
|
|
|
void SetDitDuration(unsigned int duration) {
|
2022-05-22 21:55:22 -06:00
|
|
|
this->ditDuration = duration;
|
|
|
|
}
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
void Reset() {
|
|
|
|
this->transmitting = false;
|
2022-05-28 15:18:28 -06:00
|
|
|
}
|
2022-05-22 21:55:22 -06:00
|
|
|
|
|
|
|
bool TxClosed() {
|
|
|
|
for (int i = 0; i < len(this->txRelays); i++) {
|
|
|
|
if (this->TxClosed(i)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TxClosed(int relay) {
|
|
|
|
return this->txRelays[relay];
|
|
|
|
}
|
|
|
|
|
|
|
|
void Tx(int relay, bool closed) {
|
|
|
|
bool wasClosed = this->TxClosed();
|
|
|
|
this->txRelays[relay] = closed;
|
2024-10-20 17:34:41 -06:00
|
|
|
this->transmitting = this->TxClosed();
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
void Key(Paddle key, bool pressed) {
|
|
|
|
this->Tx(key, pressed);
|
|
|
|
}
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
bool Tick(unsigned long now) {
|
|
|
|
return this->transmitting;
|
|
|
|
}
|
2022-05-22 21:55:22 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
class BugKeyer: public StraightKeyer {
|
|
|
|
public:
|
2022-05-28 15:18:28 -06:00
|
|
|
unsigned int nextPulse = 0;
|
2022-05-22 21:55:22 -06:00
|
|
|
bool keyPressed[2];
|
|
|
|
|
|
|
|
using StraightKeyer::StraightKeyer;
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
char *Name() {
|
|
|
|
return "bug";
|
|
|
|
}
|
|
|
|
|
2022-05-22 21:55:22 -06:00
|
|
|
void Reset() {
|
|
|
|
StraightKeyer::Reset();
|
2022-05-28 15:18:28 -06:00
|
|
|
this->nextPulse = 0;
|
2022-05-22 21:55:22 -06:00
|
|
|
this->keyPressed[0] = false;
|
|
|
|
this->keyPressed[1] = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Key(Paddle key, bool pressed) {
|
|
|
|
this->keyPressed[key] = pressed;
|
|
|
|
if (key == 0) {
|
|
|
|
this->beginPulsing();
|
|
|
|
} else {
|
|
|
|
StraightKeyer::Key(key, pressed);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
bool Tick(unsigned long now) {
|
|
|
|
if (this->nextPulse && (now >= this->nextPulse)) {
|
|
|
|
this->pulse(now);
|
2022-05-28 15:18:28 -06:00
|
|
|
}
|
2024-10-20 17:34:41 -06:00
|
|
|
return this->transmitting;
|
2022-05-28 15:18:28 -06:00
|
|
|
}
|
|
|
|
|
2022-05-22 21:55:22 -06:00
|
|
|
void beginPulsing() {
|
2022-05-28 15:38:10 -06:00
|
|
|
if (!this->nextPulse) {
|
|
|
|
this->nextPulse = 1;
|
|
|
|
}
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
virtual void pulse(unsigned int now) {
|
2022-05-22 21:55:22 -06:00
|
|
|
if (this->TxClosed(0)) {
|
|
|
|
this->Tx(0, false);
|
|
|
|
} else if (this->keyPressed[0]) {
|
|
|
|
this->Tx(0, true);
|
|
|
|
} else {
|
2022-05-28 15:18:28 -06:00
|
|
|
this->nextPulse = 0;
|
2022-05-22 21:55:22 -06:00
|
|
|
return;
|
|
|
|
}
|
2024-10-20 17:34:41 -06:00
|
|
|
this->nextPulse = now + this->ditDuration;
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class ElBugKeyer: public BugKeyer {
|
|
|
|
public:
|
|
|
|
unsigned int nextRepeat;
|
|
|
|
|
|
|
|
using BugKeyer::BugKeyer;
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
char *Name() {
|
|
|
|
return "el bug";
|
|
|
|
}
|
|
|
|
|
2022-05-22 21:55:22 -06:00
|
|
|
void Reset() {
|
|
|
|
BugKeyer::Reset();
|
|
|
|
this->nextRepeat = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return which key is pressed. If none, return -1.
|
|
|
|
int whichKeyPressed() {
|
|
|
|
for (int i = 0; i < len(this->keyPressed); i++) {
|
|
|
|
if (this->keyPressed[i]) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Key(Paddle key, bool pressed) {
|
|
|
|
this->keyPressed[key] = pressed;
|
|
|
|
if (pressed) {
|
|
|
|
this->nextRepeat = key;
|
2022-05-28 15:18:28 -06:00
|
|
|
this->beginPulsing();
|
2022-05-22 21:55:22 -06:00
|
|
|
} else {
|
|
|
|
this->nextRepeat = this->whichKeyPressed();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned int keyDuration(int key) {
|
|
|
|
switch (key) {
|
2022-05-28 15:18:28 -06:00
|
|
|
case PADDLE_DIT:
|
2022-05-22 21:55:22 -06:00
|
|
|
return this->ditDuration;
|
2022-05-28 15:18:28 -06:00
|
|
|
case PADDLE_DAH:
|
|
|
|
return 3 * (this->ditDuration);
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
2022-05-28 15:18:28 -06:00
|
|
|
return this->ditDuration; // XXX
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
|
|
|
|
2022-05-28 15:18:28 -06:00
|
|
|
virtual int nextTx() {
|
2022-05-22 21:55:22 -06:00
|
|
|
if (this->whichKeyPressed() == -1) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
return this->nextRepeat;
|
|
|
|
}
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
virtual void pulse(unsigned int now) {
|
2022-05-22 21:55:22 -06:00
|
|
|
int nextPulse = 0;
|
|
|
|
if (this->TxClosed(0)) {
|
|
|
|
// Pause if we're currently transmitting
|
2022-05-28 15:18:28 -06:00
|
|
|
nextPulse = this->keyDuration(PADDLE_DIT);
|
2022-05-22 21:55:22 -06:00
|
|
|
this->Tx(0, false);
|
|
|
|
} else {
|
|
|
|
int next = this->nextTx();
|
|
|
|
if (next >= 0) {
|
|
|
|
nextPulse = this->keyDuration(next);
|
|
|
|
this->Tx(0, true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nextPulse) {
|
2024-10-20 17:34:41 -06:00
|
|
|
this->nextPulse = now + nextPulse;
|
2022-05-22 21:55:22 -06:00
|
|
|
} else {
|
2022-05-28 15:18:28 -06:00
|
|
|
this->nextPulse = 0;
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class UltimaticKeyer: public ElBugKeyer {
|
|
|
|
public:
|
2022-05-28 15:38:10 -06:00
|
|
|
QSet queue;
|
2022-05-22 21:55:22 -06:00
|
|
|
|
|
|
|
using ElBugKeyer::ElBugKeyer;
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
char *Name() {
|
|
|
|
return "ultimatic";
|
|
|
|
}
|
|
|
|
|
2022-05-22 21:55:22 -06:00
|
|
|
void Key(Paddle key, bool pressed) {
|
|
|
|
if (pressed) {
|
2022-05-28 15:38:10 -06:00
|
|
|
this->queue.add(key);
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
|
|
|
ElBugKeyer::Key(key, pressed);
|
|
|
|
}
|
|
|
|
|
2022-05-28 15:18:28 -06:00
|
|
|
virtual int nextTx() {
|
2022-05-28 15:38:10 -06:00
|
|
|
int key = this->queue.shift();
|
2022-05-22 21:55:22 -06:00
|
|
|
if (key != -1) {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
return ElBugKeyer::nextTx();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class SingleDotKeyer: public ElBugKeyer {
|
|
|
|
public:
|
2022-05-28 15:38:10 -06:00
|
|
|
QSet queue;
|
2022-05-22 21:55:22 -06:00
|
|
|
|
|
|
|
using ElBugKeyer::ElBugKeyer;
|
2024-10-20 17:34:41 -06:00
|
|
|
|
|
|
|
char *Name() {
|
|
|
|
return "single dot";
|
|
|
|
}
|
|
|
|
|
2022-05-22 21:55:22 -06:00
|
|
|
void Key(Paddle key, bool pressed) {
|
2022-05-28 15:38:10 -06:00
|
|
|
if (pressed && (key == PADDLE_DIT)) {
|
|
|
|
this->queue.add(key);
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
|
|
|
ElBugKeyer::Key(key, pressed);
|
|
|
|
}
|
|
|
|
|
2022-05-28 15:18:28 -06:00
|
|
|
virtual int nextTx() {
|
2022-05-28 15:38:10 -06:00
|
|
|
int key = this->queue.shift();
|
2022-05-22 21:55:22 -06:00
|
|
|
if (key != -1) {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
if (this->keyPressed[1]) return 1;
|
|
|
|
if (this->keyPressed[0]) return 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class IambicKeyer: public ElBugKeyer {
|
|
|
|
public:
|
|
|
|
using ElBugKeyer::ElBugKeyer;
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
char *Name() {
|
|
|
|
return "iambic plain";
|
|
|
|
}
|
|
|
|
|
2022-05-28 15:18:28 -06:00
|
|
|
virtual int nextTx() {
|
2022-05-22 21:55:22 -06:00
|
|
|
int next = ElBugKeyer::nextTx();
|
2022-05-28 15:38:10 -06:00
|
|
|
if (this->keyPressed[PADDLE_DIT] && this->keyPressed[PADDLE_DAH]) {
|
2022-05-22 21:55:22 -06:00
|
|
|
this->nextRepeat = 1 - this->nextRepeat;
|
|
|
|
}
|
|
|
|
return next;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class IambicAKeyer: public IambicKeyer {
|
|
|
|
public:
|
2022-05-28 15:38:10 -06:00
|
|
|
QSet queue;
|
2022-05-22 21:55:22 -06:00
|
|
|
|
|
|
|
using IambicKeyer::IambicKeyer;
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
char *Name() {
|
|
|
|
return "iambic a";
|
|
|
|
}
|
|
|
|
|
2022-05-22 21:55:22 -06:00
|
|
|
void Key(Paddle key, bool pressed) {
|
2022-05-28 15:38:10 -06:00
|
|
|
if (pressed && (key == PADDLE_DIT)) {
|
|
|
|
this->queue.add(key);
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
|
|
|
IambicKeyer::Key(key, pressed);
|
|
|
|
}
|
|
|
|
|
2022-05-28 15:18:28 -06:00
|
|
|
virtual int nextTx() {
|
2022-05-22 21:55:22 -06:00
|
|
|
int next = IambicKeyer::nextTx();
|
2022-05-28 15:38:10 -06:00
|
|
|
int key = this->queue.shift();
|
2022-05-22 21:55:22 -06:00
|
|
|
if (key != -1) {
|
|
|
|
return key;
|
|
|
|
}
|
|
|
|
return next;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class IambicBKeyer: public IambicKeyer {
|
|
|
|
public:
|
2022-05-28 15:38:10 -06:00
|
|
|
QSet queue;
|
2024-10-20 17:34:41 -06:00
|
|
|
int sending;
|
2022-05-22 21:55:22 -06:00
|
|
|
|
|
|
|
using IambicKeyer::IambicKeyer;
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
char *Name() {
|
|
|
|
return "iambic b";
|
|
|
|
}
|
|
|
|
|
2022-05-22 21:55:22 -06:00
|
|
|
void Reset() {
|
2024-10-20 17:34:41 -06:00
|
|
|
this->sending = -1;
|
2022-05-22 21:55:22 -06:00
|
|
|
IambicKeyer::Reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Key(Paddle key, bool pressed) {
|
2024-10-20 17:34:41 -06:00
|
|
|
if (pressed && (this->sending != key)) {
|
2022-05-28 15:38:10 -06:00
|
|
|
this->queue.add(key);
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
|
|
|
IambicKeyer::Key(key, pressed);
|
|
|
|
}
|
|
|
|
|
2022-05-28 15:18:28 -06:00
|
|
|
virtual int nextTx() {
|
2022-05-28 15:38:10 -06:00
|
|
|
for (int key = 0; key < len(this->keyPressed); key++) {
|
2022-05-22 21:55:22 -06:00
|
|
|
if (this->keyPressed[key]) {
|
2022-05-28 15:38:10 -06:00
|
|
|
this->queue.add(key);
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
this->sending = this->queue.shift();
|
|
|
|
return this->sending;
|
2022-05-22 21:55:22 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class KeyaheadKeyer: public ElBugKeyer {
|
|
|
|
public:
|
|
|
|
int queue[MAX_KEYER_QUEUE];
|
|
|
|
unsigned int qlen;
|
|
|
|
|
|
|
|
using ElBugKeyer::ElBugKeyer;
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
char *Name() {
|
|
|
|
return "keyahead";
|
|
|
|
}
|
|
|
|
|
2022-05-22 21:55:22 -06:00
|
|
|
void Reset() {
|
|
|
|
ElBugKeyer::Reset();
|
|
|
|
this->qlen = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Key(Paddle key, bool pressed) {
|
|
|
|
if (pressed) {
|
|
|
|
if (this->qlen < MAX_KEYER_QUEUE) {
|
|
|
|
this->queue[this->qlen++] = key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ElBugKeyer::Key(key, pressed);
|
|
|
|
}
|
|
|
|
|
2022-05-28 15:18:28 -06:00
|
|
|
virtual int nextTx() {
|
2022-05-22 21:55:22 -06:00
|
|
|
if (this->qlen > 0) {
|
|
|
|
int next = this->queue[0];
|
|
|
|
this->qlen--;
|
|
|
|
for (int i = 0; i < this->qlen; i++) {
|
|
|
|
this->queue[i] = this->queue[i+1];
|
|
|
|
}
|
|
|
|
return next;
|
|
|
|
}
|
|
|
|
return ElBugKeyer::nextTx();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-05-28 15:38:10 -06:00
|
|
|
StraightKeyer straightKeyer = StraightKeyer();
|
|
|
|
BugKeyer bugKeyer = BugKeyer();
|
|
|
|
ElBugKeyer elBugKeyer = ElBugKeyer();
|
|
|
|
SingleDotKeyer singleDotKeyer = SingleDotKeyer();
|
|
|
|
UltimaticKeyer ultimaticKeyer = UltimaticKeyer();
|
|
|
|
IambicKeyer iambicKeyer = IambicKeyer();
|
|
|
|
IambicAKeyer iambicAKeyer = IambicAKeyer();
|
|
|
|
IambicBKeyer iambicBKeyer = IambicBKeyer();
|
|
|
|
KeyaheadKeyer keyaheadKeyer = KeyaheadKeyer();
|
2022-05-28 15:18:28 -06:00
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
Keyer *AllKeyers[] = {
|
2022-05-28 15:38:10 -06:00
|
|
|
&straightKeyer,
|
|
|
|
&bugKeyer,
|
|
|
|
&elBugKeyer,
|
|
|
|
&singleDotKeyer,
|
|
|
|
&ultimaticKeyer,
|
|
|
|
&iambicKeyer,
|
|
|
|
&iambicAKeyer,
|
|
|
|
&iambicBKeyer,
|
|
|
|
&keyaheadKeyer,
|
2022-05-28 15:18:28 -06:00
|
|
|
};
|
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
Keyer *GetKeyerByNumber(int n) {
|
|
|
|
if (n >= len(AllKeyers)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
2022-05-28 15:18:28 -06:00
|
|
|
|
2024-10-20 17:34:41 -06:00
|
|
|
return AllKeyers[n];
|
|
|
|
}
|