vail-adapter

Firmware for USB morse code key adapter
git clone https://git.woozle.org/neale/vail-adapter.git

WrathPak  ·  2025-08-18

keyers.cpp

  1#include <stddef.h>
  2#include "keyers.h"
  3
  4#define len(t) (sizeof(t)/sizeof(*t))
  5
  6// Queue Set: A Set you can shift and pop.
  7class QSet {
  8    int arr[MAX_KEYER_QUEUE];
  9    unsigned int arrlen = 0;
 10
 11public:
 12    int shift() {
 13        if (arrlen == 0) {
 14            return -1;
 15        }
 16        int ret = arr[0];
 17        arrlen--;
 18        for (int i = 0; i < arrlen; i++) {
 19            arr[i] = arr[i+1];
 20        }
 21        return ret;
 22    }
 23
 24    int pop() {
 25        if (arrlen == 0) {
 26            return -1;
 27        }
 28        int ret = arr[arrlen];
 29        arrlen--;
 30        return ret;
 31    }
 32
 33    void add(int val) {
 34        if (arrlen == MAX_KEYER_QUEUE) {
 35            return;
 36        }
 37        for (int i = 0; i < arrlen; i++) {
 38            if (arr[i] == val) {
 39                return;
 40            }
 41        }
 42        arr[arrlen] = val;
 43        arrlen++;
 44    }
 45};
 46
 47
 48class StraightKeyer: public Keyer {
 49public:
 50    Transmitter *output;
 51    unsigned int ditDuration;
 52    bool txRelays[2];
 53
 54    StraightKeyer() {
 55        this->Reset();
 56    }
 57
 58    void SetOutput(Transmitter *output) {
 59        this->output = output;
 60    }
 61
 62    void Reset() {
 63        if (this->output) {
 64            this->output->EndTx();
 65        }
 66        this->ditDuration = 100;
 67    }
 68
 69    void SetDitDuration(unsigned int duration) {
 70        this->ditDuration = duration;
 71    }
 72
 73    void Release() {
 74        this->Reset();
 75    }
 76
 77    bool TxClosed() {
 78        for (int i = 0; i < len(this->txRelays); i++) {
 79            if (this->TxClosed(i)) {
 80                return true;
 81            }
 82        }
 83        return false;
 84    }
 85
 86    bool TxClosed(int relay) {
 87        return this->txRelays[relay];
 88    }
 89
 90    void Tx(int relay, bool closed) {
 91        bool wasClosed = this->TxClosed();
 92        this->txRelays[relay] = closed;
 93        bool nowClosed = this->TxClosed();
 94
 95        if (wasClosed != nowClosed) {
 96            if (nowClosed) {
 97                this->output->BeginTx();
 98            } else {
 99                this->output->EndTx();
100            }
101        }
102    }
103
104    void Key(Paddle key, bool pressed) {
105        this->Tx(key, pressed);
106    }
107
108    void Tick(unsigned int millis) {};
109};
110
111class BugKeyer: public StraightKeyer {
112public:
113    unsigned int nextPulse = 0;
114    bool keyPressed[2];
115
116    using StraightKeyer::StraightKeyer;
117
118    void Reset() {
119        StraightKeyer::Reset();
120        this->nextPulse = 0;
121        this->keyPressed[0] = false;
122        this->keyPressed[1] = false;
123    }
124
125    void Key(Paddle key, bool pressed) {
126        this->keyPressed[key] = pressed;
127        if (key == 0) {
128            this->beginPulsing();
129        } else {
130            StraightKeyer::Key(key, pressed);
131        }
132    }
133
134    void Tick(unsigned int millis) {
135        if (this->nextPulse && (millis >= this->nextPulse)) {
136            this->pulse(millis);
137        }
138    }
139
140    void beginPulsing() {
141        if (!this->nextPulse) {
142            this->nextPulse = 1;
143        }
144    }
145
146    virtual void pulse(unsigned int millis) {
147        if (this->TxClosed(0)) {
148            this->Tx(0, false);
149        } else if (this->keyPressed[0]) {
150            this->Tx(0, true);
151        } else {
152            this->nextPulse = 0;
153            return;
154        }
155        this->nextPulse = millis + this->ditDuration;
156    }
157};
158
159class ElBugKeyer: public BugKeyer {
160public:
161    unsigned int nextRepeat;
162
163    using BugKeyer::BugKeyer;
164
165    void Reset() {
166        BugKeyer::Reset();
167        this->nextRepeat = -1;
168    }
169
170    // Return which key is pressed. If none, return -1.
171    int whichKeyPressed() {
172        for (int i = 0; i < len(this->keyPressed); i++) {
173            if (this->keyPressed[i]) {
174                return i;
175            }
176        }
177        return -1;
178    }
179
180    void Key(Paddle key, bool pressed) {
181        this->keyPressed[key] = pressed;
182        if (pressed) {
183            this->nextRepeat = key;
184            this->beginPulsing();
185        } else {
186            this->nextRepeat = this->whichKeyPressed();
187        }
188    }
189
190    unsigned int keyDuration(int key) {
191        switch (key) {
192        case PADDLE_DIT:
193            return this->ditDuration;
194        case PADDLE_DAH:
195            return 3 * (this->ditDuration);
196        }
197        return this->ditDuration; // XXX
198    }
199
200    virtual int nextTx() {
201        if (this->whichKeyPressed() == -1) {
202            return -1;
203        }
204        return this->nextRepeat;
205    }
206
207    virtual void pulse(unsigned int millis) {
208        int nextPulse = 0;
209        if (this->TxClosed(0)) {
210            // Pause if we're currently transmitting
211            nextPulse = this->keyDuration(PADDLE_DIT);
212            this->Tx(0, false);
213        } else {
214            int next = this->nextTx();
215            if (next >= 0) {
216                nextPulse = this->keyDuration(next);
217                this->Tx(0, true);
218            }
219        }
220
221        if (nextPulse) {
222            this->nextPulse = millis + nextPulse;
223        } else {
224            this->nextPulse = 0;
225        }
226    }
227};
228
229class UltimaticKeyer: public ElBugKeyer {
230public:
231    QSet queue;
232    
233    using ElBugKeyer::ElBugKeyer;
234
235    void Key(Paddle key, bool pressed) {
236        if (pressed) {
237            this->queue.add(key);
238        }
239        ElBugKeyer::Key(key, pressed);
240    }
241
242    virtual int nextTx() {
243        int key = this->queue.shift();
244        if (key != -1) {
245            return key;
246        }
247        return ElBugKeyer::nextTx();
248    }
249};
250
251class SingleDotKeyer: public ElBugKeyer {
252public:
253    QSet queue;
254
255    using ElBugKeyer::ElBugKeyer;
256    
257    void Key(Paddle key, bool pressed) {
258        if (pressed && (key == PADDLE_DIT)) {
259            this->queue.add(key);
260        }
261        ElBugKeyer::Key(key, pressed);
262    }
263
264    virtual int nextTx() {
265        int key = this->queue.shift();
266        if (key != -1) {
267            return key;
268        }
269        if (this->keyPressed[1]) return 1;
270        if (this->keyPressed[0]) return 0;
271        return -1;
272    }
273};
274
275class IambicKeyer: public ElBugKeyer {
276public:
277
278    using ElBugKeyer::ElBugKeyer;
279
280    virtual int nextTx() {
281        int next = ElBugKeyer::nextTx();
282        if (this->keyPressed[PADDLE_DIT] && this->keyPressed[PADDLE_DAH]) {
283            this->nextRepeat = 1 - this->nextRepeat;
284        }
285        return next;
286    }
287};
288
289class IambicAKeyer: public IambicKeyer {
290public:
291    QSet queue;
292
293    using IambicKeyer::IambicKeyer;
294    
295    void Key(Paddle key, bool pressed) {
296        if (pressed && (key == PADDLE_DIT)) {
297            this->queue.add(key);
298        }
299        IambicKeyer::Key(key, pressed);
300    }
301
302    virtual int nextTx() {
303        int next = IambicKeyer::nextTx();
304        int key = this->queue.shift();
305        if (key != -1) {
306            return key;
307        }
308        return next;
309    }
310};
311
312class IambicBKeyer: public IambicKeyer {
313public:
314    QSet queue;
315
316    using IambicKeyer::IambicKeyer;
317
318    void Reset() {
319        IambicKeyer::Reset();
320    }
321
322    void Key(Paddle key, bool pressed) {
323        if (pressed) {
324            this->queue.add(key);
325        }
326        IambicKeyer::Key(key, pressed);
327    }
328
329    virtual int nextTx() {
330        for (int key = 0; key < len(this->keyPressed); key++) {
331            if (this->keyPressed[key]) {
332                this->queue.add(key);
333            }
334        }
335
336        return this->queue.shift();
337    }
338};
339
340class KeyaheadKeyer: public ElBugKeyer {
341public:
342    int queue[MAX_KEYER_QUEUE];
343    unsigned int qlen;
344
345    using ElBugKeyer::ElBugKeyer;
346
347    void Reset() {
348        ElBugKeyer::Reset();
349        this->qlen = 0;
350    }
351
352    void Key(Paddle key, bool pressed) {
353        if (pressed) {
354            if (this->qlen < MAX_KEYER_QUEUE) {
355                this->queue[this->qlen++] = key;
356            }
357        }
358        ElBugKeyer::Key(key, pressed);
359    }
360
361    virtual int nextTx() {
362        if (this->qlen > 0) {
363            int next = this->queue[0];
364            this->qlen--;
365            for (int i = 0; i < this->qlen; i++) {
366                this->queue[i] = this->queue[i+1];
367            }
368            return next;
369        }
370        return ElBugKeyer::nextTx();
371    }
372};
373
374StraightKeyer straightKeyer = StraightKeyer();
375BugKeyer bugKeyer = BugKeyer();
376ElBugKeyer elBugKeyer = ElBugKeyer();
377SingleDotKeyer singleDotKeyer = SingleDotKeyer();
378UltimaticKeyer ultimaticKeyer = UltimaticKeyer();
379IambicKeyer iambicKeyer = IambicKeyer();
380IambicAKeyer iambicAKeyer = IambicAKeyer();
381IambicBKeyer iambicBKeyer = IambicBKeyer();
382KeyaheadKeyer keyaheadKeyer = KeyaheadKeyer();
383
384Keyer *keyers[] = {
385    NULL,
386    &straightKeyer,
387    &bugKeyer,
388    &elBugKeyer,
389    &singleDotKeyer,
390    &ultimaticKeyer,
391    &iambicKeyer,
392    &iambicAKeyer,
393    &iambicBKeyer,
394    &keyaheadKeyer,
395};
396
397Keyer *GetKeyerByNumber(int n, Transmitter *output) {
398    if (n >= len(keyers)) {
399        return NULL;
400    }
401
402    Keyer *k = keyers[n];
403    k->SetOutput(output);
404    return k;
405}
406
407int getKeyerNumber(Keyer* k) {
408    if (k == NULL) {
409        return 1; // Default to straight key if NULL
410    }
411    
412    for (int i = 1; i < len(keyers); i++) {
413        if (keyers[i] == k) {
414            return i;
415        }
416    }
417    return 1; // Default to straight key if not found
418}