Multi-coding for user account numbers

by i_e_b

HTML

<h1>Multi-coding for hand input data</h1>

<button id="reRoll">New random data</button><br/>
<br/>
Hex data: <input type="text" id="hexTagId" value="?"/><br/>
MultiCode: <code id="displayedCode">?</code><br/>
User input: <input type="text" id="userInput" value="?"/>
<button id="checkBtn">Check</button>
<button id="resetBtn">Reset</button><br/>
<br/>
Result: <code id="resultCode">?</code><br/>

<h2>Design</h2>
<p>
  This demo assumes a fixed-length ID number, initially encoded as hex;
  plus <span id="infoCD">?</span> additional check codes. The general algorithm
  works for any fixed length of input and check codes.
</p>

<p>
  Before passing to a FEC (in this case, Reed-Solomon), we look for 
  patterns in the input, and try to correct for them, increasing the chance
  of the FEC successfully correcting the input.
</p>

<p>
  Start with 32 characters, from the ASCII alpha-numeric set with indistict glyphs <code>OLIU</code> removed,
  then split into an 'odd' and 'even' set, resulting in 16 characters in each set (for 4 bit grouping)
  <pre>
 0 1 2 3 6 7 8 9 b G J N q X Y Z
4 5 A C D E F H K M P R s T V W
  </pre>
  <code>S</code>, <code>Q</code>, and <code>B</code> are presented as lower case
  to prevent confusion with <code>5</code>, <code>0</code>, and <code>8</code>.
  As no pair of even or odd characters will be next to each other, we can optimise population of these
  sets to reduce the chance of accidental obscenity. The likelyhood of accidental word forming is already
  quite low with this set.
</p>

<p>
  The generated code alternates between the two sets.
  We know if an input has mistakes if it is not following this alternation.
  This has a short-coming that we can't tell the difference between pairs of deleted characters
  at the start or end of the input. We try rotating the input during the Reed-Solomon step,
  to the limit of deleted characters.
</p>


<h2>Error Examples</h2>
<table>
  <tr>
   ...

JavaScript

'strict';

/* Settings */
const extraCodes = 6; // increase for longer, but more resilient codes
const expectedCodeLength = extraCodes + 8; // 8 bytes of original code
const tryReallyHard = true; // try rotations in the Reed-Solomon decoder. Slower, and might increase incorrect acceptance.

// Odd and even sets of display characters. Note: '~' is for error.
// Q and S are lower cased to look less like 0 and 5.
const oddSet    = ['0','1','2','3','6','7','8','9','b','G','J','N','q','X','Y','Z', '~'];
const evenSet = ['4','5','A','C','D','E','F','H','K','M','P','R','s','T','V','W','~'];
// Characters likely to be entered as spaces. These will be trimmed from input
const spaces = [' ', '-', '.', '·', '_', '+', '*', '#'];
// Likely mistakes. Map these inputs to corrected characters:
const correction = { O: '0', L: '1', I: '1', U: 'V' };
// Case changes to improve letter/number distiction:
const caseChanges = { B: 'b', Q: 'q', S: 's' };


/* UI */
function clearLog() {
  document.getElementById('outp').innerText = '';
}
function log(msg) {
  document.getElementById('outp').innerText += msg + '\r\n';
}
function setCode(c) {
  document.getElementById('displayedCode').innerText = c.join('');
}
function setTagId(c) {
  document.getElementById('hexTagId').value = '' + c;
}
function getTagId() {
  return document.getElementById('hexTagId').value;
}
function setResult(msg) {
  document.getElementById('resultCode').innerText = msg;
}
function getInput() {
  return document.getElementById('userInput').value.split('');
}
function copyCodeToInput() {
  document.getElementById('userInput').value =
    document.getElementById('displayedCode').innerText;
}

/* GF 16 --> */
let gf_table = null;

function gfTable() {
  if (gf_table) return gf_table;
  gf_table = {
    exp: Array(32).fill(0),
    log: Array(16).fill(0),
  };
  let x = 1;
  let prim = 19; // critical to get this right!

  for (let i = 0; i < 16; i++) {
   ...