puzzle-box/morse.cpp

121 lines
2.8 KiB
C++

#include "morse.h"
// Each morse dit/dah is stored as a nybble
#define dit 1
#define dah 3
#define Pack(a, b, c, d) ((a << 0) | (b << 2) | (c << 4) | (d << 6))
#define Unpack(m, pos) ((m >> (pos * 2)) & 0b11)
const uint8_t MorseLetters[] = {
Pack(dit, dah, 0, 0), // A
Pack(dah, dit, dit, dit), // B
Pack(dah, dit, dah, dit), // C
Pack(dah, dit, dit, 0), // D
Pack(dit, 0, 0, 0), // E
Pack(dit, dit, dah, dit), // F
Pack(dah, dah, dit, 0), // G
Pack(dit, dit, dit, dit), // H
Pack(dit, dit, 0, 0), // I
Pack(dit, dah, dah, dah), // J
Pack(dah, dit, dah, 0), // K
Pack(dit, dah, dit, dit), // L
Pack(dah, dah, 0, 0), // M
Pack(dah, dit, 0, 0), // N
Pack(dah, dah, dah, 0), // O
Pack(dit, dah, dah, dit), // P
Pack(dah, dah, dit, dah), // Q
Pack(dit, dah, dit, 0), // R
Pack(dit, dit, dit, 0), // S
Pack(dah, 0, 0, 0), // T
Pack(dit, dit, dah, 0), // U
Pack(dit, dit, dit, dah), // V
Pack(dit, dah, dah, 0), // W
Pack(dah, dit, dit, dah), // X
Pack(dah, dit, dah, dah), // Y
Pack(dah, dah, dit, dit), // Z
};
MorseEncoder::MorseEncoder() {
SetText("");
}
MorseEncoder::MorseEncoder(const char *s) {
SetText(s);
}
void MorseEncoder::SetText(const char *s) {
p = s;
crumb = 0;
ticksLeft = 0;
Transmitting = false;
}
void MorseEncoder::Quiet(int ticks) {
Transmitting = false;
ticksLeft = ticks;
}
bool MorseEncoder::Tick() {
--ticksLeft;
if (ticksLeft > 0) {
return true;
}
// We're out of ticks
if (!p || !*p) {
return false;
}
// If we were just transmitting, we have to stop for at least one dit
if (Transmitting) {
Transmitting = false;
ticksLeft = MORSE_DIT;
return true;
}
// If that was the end of the letter, we have to pause more
if (crumb == 4) {
crumb = 0;
++p;
ticksLeft = MORSE_PAUSE_LETTER - MORSE_DIT;
return true;
}
switch (*p) {
case '\0':
return false;
case 'a' ... 'z':
Transmitting = true;
ticksLeft = Unpack(MorseLetters[*p - 'a'], crumb++);
break;
case 'A' ... 'Z':
Transmitting = true;
ticksLeft = Unpack(MorseLetters[*p - 'A'], crumb++);
break;
case ' ':
crumb = 0;
++p;
Transmitting = false;
ticksLeft = MORSE_PAUSE_WORD - MORSE_DIT;
break;
default: // this should never happen! Transmit for a word pause to indicate weirdness.
crumb = 0;
++p;
Transmitting = true;
ticksLeft = MORSE_PAUSE_WORD;
break;
}
if (0 == ticksLeft) {
// Unpack can return 0 if there are fewer than 4 emissions for a letter.
// In that case, we 're done with the letter.
crumb = 0;
++p;
Transmitting = false;
ticksLeft = MORSE_PAUSE_LETTER - MORSE_DIT;
}
return true;
}