Add files via upload

This commit is contained in:
Eddie Kim 2024-10-25 12:16:42 -07:00 committed by Michael
parent 65dd203ab4
commit 82a505344c
2 changed files with 370 additions and 0 deletions

116
static/scripts/decoder.js Normal file
View File

@ -0,0 +1,116 @@
const morseToAlphabet = new Map([
["12", "A"],
["2111", "B"],
["2121", "C"],
["211", "D"],
["1", "E"],
["1121", "F"],
["221", "G"],
["1111", "H"],
["11", "I"],
["1222", "J"],
["212", "K"],
["1211", "L"],
["22", "M"],
["21", "N"],
["222", "O"],
["1221", "P"],
["2212", "Q"],
["121", "R"],
["111", "S"],
["2", "T"],
["112", "U"],
["1112", "V"],
["122", "W"],
["2112", "X"],
["2122", "Y"],
["2211", "Z"],
["12222", "1"],
["11222", "2"],
["11122", "3"],
["11112", "4"],
["11111", "5"],
["21111", "6"],
["22111", "7"],
["22211", "8"],
["22221", "9"],
["22222", "0"],
]);
class Decoder {
constructor() {
this.lastLetter = '';
this.decodeArray = '';
this.unit = 80; // adjustment: short dit reduces, long dah lengthens
this.keyStartTime = null;
this.keyEndTime = null;
this.spaceTimer = null;
this.farnsworth = 3;
}
keyOn() {
clearTimeout(this.spaceTimer);
this.keyStartTime = Date.now();
//var pauseDuration = (this.keyEndTime) ? this.keyStartTime - this.keyEndTime : 0;
//if (pauseDuration > this.unit + (this.unit/10)) { // end sequence and decode letter
//this.registerLetter();
//}
}
keyOff() {
this.keyEndTime = Date.now();
var keyDuration = (this.keyStartTime) ? this.keyEndTime - this.keyStartTime : 0;
if (keyDuration < this.unit) {
// reduce unit based on short dit
this.unit = (keyDuration + this.unit) / 2;
this.registerDit();
} else if (keyDuration > this.unit * 3) {
// lengthen unit based on long dah
this.unit = ((keyDuration / 3) + this.unit) / 2;
this.registerDah();
} else {
var ditAndDahThreshold = (this.unit * 2);
if (keyDuration >= ditAndDahThreshold) {
this.registerDah();
} else {
this.registerDit();
}
}
let spaceTime = this.unit * this.farnsworth;
this.spaceTimer = setTimeout(() => { // end sequence and decode letter
this.updateLastLetter(this.morseToLetter(this.decodeArray));
this.decodeArray = '';
}, spaceTime, "keyOff");
}
registerDit() {
this.decodeArray += '1';
}
registerDah() {
this.decodeArray += '2';
}
updateLastLetter(letter) {
updateCurrentLetter(letter);
this.lastLetter = letter;
//console.log(this.lastLetter);
}
morseToLetter(sequence) {
var letter = morseToAlphabet.get(sequence);
if (letter) {
return letter;
} else {
return '*';
}
}
calculateWpm() {
return 60000 / (this.unit * 50);
}
setFarnsworth(farnsworth) {
this.farnsworth = farnsworth;
}
}

254
static/scripts/keyer.js Normal file
View File

@ -0,0 +1,254 @@
class Keyer {
constructor(sndr, decoder) {
this.sndr = sndr; // Sounder instance
this.decoder = decoder; // Decoder instance
// Using [ and ] for dits and dahs respectively for compatibility with the vband usb interface
this.ditKey1 = 'ControlLeft';
this.dahKey1 = 'ControlRight';
this.ditKey2 = 'BracketLeft';
this.dahKey2 = 'BracketRight';
this.wpm = 20;
this.unit = 60; // length of dit in milliseconds; 60 is 20wpm
this.mode = 2; // 1: straight key, 2: iambicA, 3: iambicB, 4: ultimatic
this.tone = 550;
this.queue = [];
this.ditKeyState = 0;
this.dahKeyState = 0;
this.lastKey = null;
this.ditStreak = 0;
this.dahStreak = 0;
this.streak = 0;
this.ditStart = null;
this.ditStop = null;
this.dahStart = null;
this.dahStop = null;
this.sending = false;
this.lastSendTimestamp = null;
this.oscillatorTimer = setInterval(() => {
this.oscillate();
}, 0);
}
setWpm(wpm){
this.wpm = wpm;
this.unit = 60000 / (wpm * 50) // based on the PARIS method 60 seconds / 50 elements per word * WPM
this.decoder.unit = this.unit;
}
setMode(mode){
this.mode = mode;
}
setTone(tone){
this.tone = tone;
}
sendSignal() {
this.sending = true;
//console.log('startSignal');
if (restartAudioNeeded()) {
restartAudio();
}
this.sndr.setTone(this.tone);
this.sndr.on();
this.decoder.keyOn();
}
stopSignal() {
//console.log('stopSignal');
this.sndr.off();
this.decoder.keyOff();
this.lastSendTimestamp = Date.now();
setTimeout(() => {
this.sending = false;
}, this.unit);
}
press(event, down, mode=this.mode) {
if (mode > 1 && event.code != this.ditKey1 && event.code != this.dahKey1 && event.code != this.ditKey2 && event.code != this.dahKey2) return;
if (mode == 1) {
if (down) {
if (restartAudioNeeded()) {
restartAudio();
}
this.sndr.setTone(this.tone);
this.sndr.on();
this.decoder.keyOn();
} else {
this.sndr.off();
this.decoder.keyOff();
}
} else if (mode > 1) {
//console.log(key);
if (event.code == this.ditKey1 || event.code == this.ditKey2) {
if (down) { // dit key down
this.ditKeyState = 1;
this.ditStart = Date.now()
} else { // dit key up
this.ditKeyState = 0;
this.ditStop = Date.now()
}
}
if (event.code == this.dahKey1 || event.code == this.dahKey2) {
if (down) { // dah key down
this.dahKeyState = 1;
this.dahStart = Date.now()
} else { // dah key up
this.dahKeyState = 0;
this.dahStop = Date.now()
}
}
}
}
processQueue() {
//console.log('processQueue');
if (!this.sending && this.queue.length) {
this.lastKey = this.queue.shift();
var signalLength = (this.lastKey == 1) ? this.unit : this.unit * 3;
this.sendSignal();
setTimeout(() => {
this.stopSignal();
}, signalLength);
}
}
oscillatev1() {
if (!this.ditKeyState && !this.dahKeyState) {
this.queue = [];
}
if (this.ditKeyState) {
if (this.queue.length == 0) {
if (!this.dahKeyState && !this.sending || this.lastKey == 2) {
this.queue.push(1);
}
}
}
if (this.dahKeyState) {
if (this.queue.length == 0) {
if (!this.ditKeyState && !this.sending || this.lastKey == 1) {
this.queue.push(2);
}
}
}
if (!this.sending && Date.now() - this.lastSendTimestamp > this.unit) {
this.processQueue();
}
}
oscillatev2() {
if (this.mode == 2 && !this.ditKeyState && !this.dahKeyState) { // Iambic B doesn't clear the queue
if (this.streak > 1) {
//console.log(this.streak + " queue: " + this.queue[0]);
this.queue = [];
}
this.streak = 0;
}
if (this.ditKeyState) {
if (this.queue.length < 1) {
if (!this.sending || this.lastKey == 2) {
this.queue.push(1);
if (this.lastKey == 2 && this.dahKeyState) {
this.streak++;
} else {
this.streak = 0;
}
}
}
}
if (this.dahKeyState) {
if (this.queue.length < 1) {
if (!this.sending || this.lastKey == 1) {
this.queue.push(2);
if (this.lastKey == 1 && this.ditKeyState) {
this.streak++;
} else {
this.streak = 0;
}
}
}
}
if (!this.sending && Date.now() - this.lastSendTimestamp >= this.unit) {
this.processQueue();
}
}
oscillatev3() {
if (this.mode == 2 && !this.ditKeyState && !this.dahKeyState && this.queue.length) { // Iambic B doesn't clear the queue
if ((this.ditStreak && this.queue[0] == 1) || (this.dahStreak && this.queue[0] == 2)) {
console.log("ditStreak: "+this.ditStreak+" dahStreak: "+this.dahStreak+" queue: "+this.queue[0]);
this.queue = [];
}
//console.log("NO CLEAR ditStreak: "+this.ditStreak+" dahStreak: "+this.dahStreak+" queue: "+this.queue[0]);
this.ditStreak = 0;
this.dahStreak = 0;
}
if (this.ditKeyState) {
if (!this.queue.length) {
if (!this.sending || this.lastKey == 2) {
this.queue.push(1);
this.ditStreak++;
}
} else if (!this.dahKeyState && this.queue[0] == 2) { // dah was canceled. Replace in queue
this.queue[0] = 1;
this.dahStreak = 0;
this.ditStreak++;
}
}
if (this.dahKeyState) {
if (!this.queue.length) {
if (!this.sending || this.lastKey == 1) {
this.queue.push(2);
this.dahStreak++;
}
} else if (!this.ditKeyState && this.queue[0] == 1) { // dit was canceled. Replace in queue
this.queue[0] = 2;
this.ditStreak = 0;
this.dahStreak++;
}
}
if (!this.sending && Date.now() - this.lastSendTimestamp >= this.unit) {
this.processQueue();
}
}
oscillate() {
if (this.mode == 2 && !this.ditKeyState && !this.dahKeyState && this.queue.length) {
if (this.queue[0] == 1) { // Dit is in the queue
if (this.ditStart < this.dahStart || this.ditStop - this.ditStart > this.unit * 4) {
this.queue.pop();
}
} else { // Dah is in the queue
if (this.dahStart < this.ditStart || this.dahStop - this.dahStart > this.unit * 2) {
this.queue.pop();
}
}
}
if (this.ditKeyState) {
if (this.queue.length == 0) {
if ((!this.dahKeyState && !this.sending) || this.lastKey == 2) {
this.queue.push(1);
}
} else { // dah key was lifted and is still in queue
if (this.mode == 2 && !this.dahKeyState && this.dahStart < this.ditStart && this.queue[0] == 2) {
this.queue.pop();
}
}
}
if (this.dahKeyState) {
if (this.queue.length == 0) {
if ((!this.ditKeyState && !this.sending) || this.lastKey == 1) {
this.queue.push(2);
}
} else { // dit key was lifted and is still in queue
if (this.mode == 2 && !this.ditKeyState && this.ditStart < this.dahStart && this.queue[0] == 1) {
this.queue.pop();
}
}
}
if (!this.sending && Date.now() - this.lastSendTimestamp > this.unit) {
this.processQueue();
}
}
}