diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json
index 09ee8d15..dbe06803 100644
--- a/src/core/config/Categories.json
+++ b/src/core/config/Categories.json
@@ -118,7 +118,9 @@
"Multiple Bombe",
"Typex",
"Lorenz",
- "Colossus"
+ "Colossus",
+ "To Baudot Code",
+ "From Baudot Code"
]
},
{
diff --git a/src/core/operations/FromBaudotCode.mjs b/src/core/operations/FromBaudotCode.mjs
new file mode 100644
index 00000000..cd1d4a7e
--- /dev/null
+++ b/src/core/operations/FromBaudotCode.mjs
@@ -0,0 +1,91 @@
+/**
+ * @author piggymoe [me@piggy.moe]
+ * @copyright Crown Copyright 2020
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+
+/**
+ * From Baudot Code operation
+ */
+class FromBaudotCode extends Operation {
+
+ /**
+ * FromBaudotCode constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "From Baudot Code";
+ this.module = "Default";
+ this.description = "Translates Baudot code to (upper case) characters using International Telegraph Alphabet No. 2 (ITA2).";
+ this.infoURL = "https://wikipedia.org/wiki/Baudot_code";
+ this.inputType = "string";
+ this.outputType = "string";
+ this.args = [
+ {
+ "name": "Endianness",
+ "type": "option",
+ "value": ["Big-endian", "Little-endian"]
+ }
+ ];
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ run(input, args) {
+ const isLittleEndian = args[0] === "Little-endian";
+
+ let output = "";
+
+ let currentTable = LETTER_TABLE;
+ let bits = 0;
+ let value = 0;
+
+ for (let i = 0; i < input.length; i++) {
+ const char = input[i];
+ if (char !== "0" && char !== "1") {
+ continue;
+ }
+ if (!isLittleEndian) {
+ value = value * 2 + parseInt(char, 2);
+ } else {
+ value = (parseInt(char, 2) << bits) + value;
+ }
+ bits++;
+ if (bits === 5) {
+ switch (value) {
+ case LETTER_TABLE.marker:
+ currentTable = LETTER_TABLE;
+ break;
+ case FIGURE_TABLE.marker:
+ currentTable = FIGURE_TABLE;
+ break;
+ default:
+ output += currentTable.data[value];
+ }
+ bits = 0;
+ value = 0;
+ }
+ }
+
+ return output;
+ }
+
+}
+
+const LETTER_TABLE = {
+ marker: 0x1f,
+ data: "\x00\x45\x0a\x41\x20\x53\x49\x55\x0d\x44\x52\x4a\x4e\x46\x43\x4b\x54\x5a\x4c\x57\x48\x59\x50\x51\x4f\x42\x47\x0e\x4d\x58\x56\x0f"
+};
+
+const FIGURE_TABLE = {
+ marker: 0x1b,
+ data: "\x00\x33\x0a\x2d\x20\x27\x38\x37\x0d\x05\x34\x07\x2c\x21\x3a\x28\x35\x2b\x29\x32\xa3\x36\x30\x31\x39\x3f\x26\x0e\x2e\x2f\x3d\x0f"
+};
+
+export default FromBaudotCode;
diff --git a/src/core/operations/ToBaudotCode.mjs b/src/core/operations/ToBaudotCode.mjs
new file mode 100644
index 00000000..84aeb633
--- /dev/null
+++ b/src/core/operations/ToBaudotCode.mjs
@@ -0,0 +1,110 @@
+/**
+ * @author piggymoe [me@piggy.moe]
+ * @copyright Crown Copyright 2020
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+
+/**
+ * To Baudot Code operation
+ */
+class ToBaudotCode extends Operation {
+
+ /**
+ * ToBaudotCode constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "To Baudot Code";
+ this.module = "Default";
+ this.description = "Translates characters into Baudot code using International Telegraph Alphabet No. 2 (ITA2).
Ignores non-Baudot characters.
e.g. BAUDOT
becomes 11001 00011 00111 01001 11000 10000
";
+ this.infoURL = "https://wikipedia.org/wiki/Baudot_code";
+ this.inputType = "string";
+ this.outputType = "string";
+ this.args = [
+ {
+ "name": "Endianness",
+ "type": "option",
+ "value": ["Big-endian", "Little-endian"]
+ }
+ ];
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ run(input, args) {
+ if (!this.initialized) {
+ this.initialize();
+ }
+
+ let output = [];
+ let currentTable = LETTER_TABLE;
+ let anotherTable = FIGURE_TABLE;
+
+ for (let i = 0; i < input.length; i++) {
+ const char = input[i].toUpperCase();
+ let signal = currentTable.reverse[char];
+ if (signal !== undefined) {
+ output.push(signal);
+ continue;
+ }
+ signal = anotherTable.reverse[char];
+ if (signal === undefined) {
+ continue;
+ }
+ [currentTable, anotherTable] = [anotherTable, currentTable];
+ output.push(currentTable.marker);
+ output.push(signal);
+ }
+
+ if (args[0] === "Little-endian") {
+ output = output.map((x) => (((x & 1) << 4) ^ ((x & 2) << 2) ^ (x & 4) ^ ((x & 8) >> 2) ^ ((x & 16) >> 4)));
+ }
+
+ return output
+ .map((x) => x.toString(2).padStart(5, "0"))
+ .join(" ");
+ }
+
+ /**
+ * Initialize all Baudot Code lookup table
+ */
+ initialize() {
+ this.initialized = true;
+ this.initializeTable(LETTER_TABLE);
+ this.initializeTable(FIGURE_TABLE);
+ }
+
+ /**
+ * Initialize one Baudot Code lookup table
+ * @param table the table to initialize
+ */
+ initializeTable(table) {
+ for (let i = 0; i < table.data.length; i++) {
+ const signal = table.data[i];
+ table.reverse[signal] = i;
+ }
+ }
+
+}
+
+const LETTER_TABLE = {
+ marker: 0x1f,
+ data: "\x00\x45\x0a\x41\x20\x53\x49\x55\x0d\x44\x52\x4a\x4e\x46\x43\x4b" +
+ "\x54\x5a\x4c\x57\x48\x59\x50\x51\x4f\x42\x47\x0e\x4d\x58\x56\x0f",
+ reverse: {}
+};
+
+const FIGURE_TABLE = {
+ marker: 0x1b,
+ data: "\x00\x33\x0a\x2d\x20\x27\x38\x37\x0d\x05\x34\x07\x2c\x21\x3a\x28" +
+ "\x35\x2b\x29\x32\xa3\x36\x30\x31\x39\x3f\x26\x0e\x2e\x2f\x3d\x0f",
+ reverse: {}
+};
+
+export default ToBaudotCode;
diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs
index 9add20b9..06863924 100644
--- a/tests/operations/index.mjs
+++ b/tests/operations/index.mjs
@@ -23,6 +23,7 @@ import "./tests/BaconCipher.mjs";
import "./tests/Base58.mjs";
import "./tests/Base64.mjs";
import "./tests/Base62.mjs";
+import "./tests/BaudotCode.mjs";
import "./tests/BitwiseOp.mjs";
import "./tests/ByteRepr.mjs";
import "./tests/CartesianProduct.mjs";
diff --git a/tests/operations/tests/BaudotCode.mjs b/tests/operations/tests/BaudotCode.mjs
new file mode 100644
index 00000000..76d7d2b1
--- /dev/null
+++ b/tests/operations/tests/BaudotCode.mjs
@@ -0,0 +1,56 @@
+/**
+ * Baudot code tests.
+ *
+ * @author piggymoe [me@piggy.moe]
+ *
+ * @copyright Crown Copyright 2020
+ * @license Apache-2.0
+ */
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ name: "To Baudot Code (BE)",
+ input: "1+1 EQUALS 2",
+ expectedOutput: "11011 10111 10001 10111 00100 11111 00001 10111 00111 00011 10010 00101 00100 11011 10011",
+ recipeConfig: [
+ {
+ op: "To Baudot Code",
+ args: ["Big-endian"],
+ },
+ ],
+ },
+ {
+ name: "To Baudot Code (LE)",
+ input: "1+1 EQUALS 2",
+ expectedOutput: "11011 11101 10001 11101 00100 11111 10000 11101 11100 11000 01001 10100 00100 11011 11001",
+ recipeConfig: [
+ {
+ op: "To Baudot Code",
+ args: ["Little-endian"],
+ },
+ ],
+ },
+ {
+ name: "From Baudot Code (BE)",
+ input: "11011 10111 10001 10111 00100 11111 00001 10111 00111 00011 10010 00101 00100 11011 10011",
+ expectedOutput: "1+1 EQUALS 2",
+ recipeConfig: [
+ {
+ op: "From Baudot Code",
+ args: ["Big-endian"],
+ },
+ ],
+ },
+ {
+ name: "From Baudot Code (LE)",
+ input: "11011 11101 10001 11101 00100 11111 10000 11101 11100 11000 01001 10100 00100 11011 11001",
+ expectedOutput: "1+1 EQUALS 2",
+ recipeConfig: [
+ {
+ op: "From Baudot Code",
+ args: ["Little-endian"],
+ },
+ ],
+ },
+]);