Signed-off-by: Severin Kaderli <severin@kaderli.dev>
This commit is contained in:
parent
db99961a62
commit
f38c4239b2
8 changed files with 433 additions and 0 deletions
|
@ -125,3 +125,14 @@
|
|||
* Adjust header and crc size
|
||||
* Clear up CRC error correction rate
|
||||
* Add glossary
|
||||
|
||||
# 2022-11-04
|
||||
* Meeting with Expert
|
||||
* Defense: 27.01.2023 10:30 - 11:30
|
||||
* Describe pro and cons of other NFC methods
|
||||
|
||||
# 2022-11-09+
|
||||
* Developing website
|
||||
|
||||
# 2022-11-21
|
||||
* Cleanup and finish up website
|
||||
|
|
8
src/.editorconfig
Normal file
8
src/.editorconfig
Normal file
|
@ -0,0 +1,8 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
max_line_length = 120
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
153
src/website/index.html
Normal file
153
src/website/index.html
Normal file
|
@ -0,0 +1,153 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Document</title>
|
||||
<link href="./styles/main.css" rel="stylesheet" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<input required minlength="1" maxlength="16" id="textInput" type="text" placeholder="Enter text..." />
|
||||
<p id="message"></p>
|
||||
<div class="buttons">
|
||||
<button id="start-sending-button" id="start-sending-button">Start Sending</button>
|
||||
<button class="hide" id="stop-sending-button">Stop Sending</button>
|
||||
<button id="start-calibration-button">Start Calibration</button>
|
||||
<button class="hide" id="stop-calibration-button">Stop Calibration</button>
|
||||
</div>
|
||||
<p id="log"></p>
|
||||
</main>
|
||||
<script src="./scripts/Constants.js"></script>
|
||||
<script src="./scripts/Utility.js"></script>
|
||||
<script src="./scripts/Packet.js"></script>
|
||||
<script>
|
||||
const hideClass = "hide";
|
||||
let isSending = false;
|
||||
let workers = [];
|
||||
|
||||
const textInput = document.getElementById("textInput");
|
||||
const startSendingButton = document.getElementById("start-sending-button");
|
||||
const stopSendingButton = document.getElementById("stop-sending-button");
|
||||
const startCalibrationButton = document.getElementById("start-calibration-button");
|
||||
const stopCalibrationButton = document.getElementById("stop-calibration-button");
|
||||
const message = document.getElementById("message");
|
||||
startCalibrationButton.addEventListener("click", startCalibration);
|
||||
stopCalibrationButton.addEventListener("click", stopCalibration);
|
||||
startSendingButton.addEventListener("click", startSending);
|
||||
stopSendingButton.addEventListener("click", stopSending);
|
||||
|
||||
function getWebWorker() {
|
||||
return new Worker("./scripts/Worker.js");
|
||||
}
|
||||
|
||||
function setMessage(text) {
|
||||
message.textContent = text;
|
||||
}
|
||||
|
||||
function startWorkers() {
|
||||
for (let i = 0; i < 8; i++) {
|
||||
workers.push(getWebWorker());
|
||||
}
|
||||
}
|
||||
|
||||
function stopWorkers() {
|
||||
for (const worker of workers) {
|
||||
worker.terminate();
|
||||
}
|
||||
|
||||
workers = [];
|
||||
}
|
||||
|
||||
function startCalibration() {
|
||||
Utility.log("Starting calibration");
|
||||
|
||||
startCalibrationButton.classList.add(hideClass);
|
||||
stopCalibrationButton.classList.remove(hideClass);
|
||||
setMessage("Calibration currently ongoing.");
|
||||
|
||||
startWorkers();
|
||||
}
|
||||
|
||||
function stopCalibration() {
|
||||
Utility.log("Stopping calibration");
|
||||
|
||||
stopCalibrationButton.classList.add(hideClass);
|
||||
startCalibrationButton.classList.remove(hideClass);
|
||||
setMessage("");
|
||||
|
||||
stopWorkers();
|
||||
}
|
||||
|
||||
function startSending() {
|
||||
startSendingButton.classList.add(hideClass);
|
||||
stopSendingButton.classList.remove(hideClass);
|
||||
setMessage(`Sending message: ${textInput.value}`);
|
||||
Utility.log(`Start sending message: ${textInput.value}`);
|
||||
|
||||
const packet = new Packet(textInput.value);
|
||||
const signal = Utility.manchesterEncode(packet.getData());
|
||||
|
||||
isSending = true;
|
||||
transmitSignal(signal);
|
||||
}
|
||||
|
||||
function stopSending() {
|
||||
Utility.log("Stop Sending");
|
||||
setMessage("");
|
||||
|
||||
stopSendingButton.classList.add(hideClass);
|
||||
startSendingButton.classList.remove(hideClass);
|
||||
isSending = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transmit the given bit array until the sending is stopped.
|
||||
*
|
||||
* @param {Number[]} signal
|
||||
*/
|
||||
async function transmitSignal(signal) {
|
||||
while (isSending) {
|
||||
// Send the preamble
|
||||
for (const bit of PREAMBLE) {
|
||||
if (!isSending) {
|
||||
stopWorkers();
|
||||
return;
|
||||
}
|
||||
|
||||
Utility.log(`Sending preamble: ${bit}`);
|
||||
await transmitBit(bit);
|
||||
}
|
||||
|
||||
for (let i = 0; i < signal.length; i++) {
|
||||
if (!isSending) {
|
||||
stopWorkers();
|
||||
return;
|
||||
}
|
||||
|
||||
Utility.log(`Sending bit ${i + 1} of ${signal.length} of packet: ${signal[i]}`);
|
||||
await transmitBit(signal[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This either starts or stops the web workers depending on the bit value
|
||||
* and then waits for a duration of CLOCK_TIME.
|
||||
*
|
||||
* @param {Number} bit
|
||||
*/
|
||||
function transmitBit(bit) {
|
||||
if (bit === 1) {
|
||||
startWorkers();
|
||||
return new Promise((resolve) => setTimeout(resolve, CLOCK_TIME));
|
||||
}
|
||||
|
||||
stopWorkers();
|
||||
return new Promise((resolve) => setTimeout(resolve, CLOCK_TIME));
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
13
src/website/scripts/Constants.js
Normal file
13
src/website/scripts/Constants.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
/**
|
||||
* The amount of time in ms a bit takes to transmit.
|
||||
*
|
||||
* @type {Number}
|
||||
*/
|
||||
const CLOCK_TIME = 500;
|
||||
|
||||
/**
|
||||
* The preamble that is sent before the packet.
|
||||
*
|
||||
* @type {Number[]}
|
||||
*/
|
||||
const PREAMBLE = [1, 1, 1, 0, 0, 0, 1, 1, 1];
|
99
src/website/scripts/Packet.js
Normal file
99
src/website/scripts/Packet.js
Normal file
|
@ -0,0 +1,99 @@
|
|||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class Packet {
|
||||
/**
|
||||
* The header is a nibble that contains the length of the payload in
|
||||
* bytes.
|
||||
*
|
||||
* @type {Number[]}
|
||||
*/
|
||||
#header = [];
|
||||
|
||||
/**
|
||||
* The payload of the packet. This can be up to 16 bytes in length.
|
||||
*
|
||||
* @type {Number[]}
|
||||
*/
|
||||
#payload = [];
|
||||
|
||||
/**
|
||||
* The checksum of the payload.
|
||||
*
|
||||
* @type {Number[]}
|
||||
*/
|
||||
#checksum = [];
|
||||
|
||||
/**
|
||||
* @param {String} payloadText
|
||||
*/
|
||||
constructor(payloadText) {
|
||||
this.#header = this.#numberToBitArray(payloadText.length, 4);
|
||||
this.#payload = this.#textToBitArray(payloadText);
|
||||
this.#checksum = this.#calculateChecksum(this.#textToCodePoints(payloadText));
|
||||
}
|
||||
|
||||
/**
|
||||
* This methods turns a number into an array of bits of the size
|
||||
* denoted by length.
|
||||
*
|
||||
* @param {Number} number
|
||||
* @param {Number} length
|
||||
* @return {Number[]}
|
||||
*/
|
||||
#numberToBitArray(number, length) {
|
||||
const bitArray = [];
|
||||
for (var i = length - 1; i >= 0; i--) {
|
||||
let bit = number & (1 << i);
|
||||
bit = bit ? 1 : 0;
|
||||
bitArray.push(bit);
|
||||
}
|
||||
return bitArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a string into an array of ASCII code points.
|
||||
*
|
||||
* @param {String} text
|
||||
* @return {Number[]}
|
||||
*/
|
||||
#textToCodePoints(text) {
|
||||
const codePoints = [];
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
codePoints.push(text.charCodeAt(i));
|
||||
}
|
||||
return codePoints;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn a string into an bit array.
|
||||
*
|
||||
* @param {String} text
|
||||
* @return {Number[]}
|
||||
*/
|
||||
#textToBitArray(text) {
|
||||
const codePoints = this.#textToCodePoints(text);
|
||||
let bitArray = [];
|
||||
for (const codePoint of codePoints) {
|
||||
bitArray = bitArray.concat(this.#numberToBitArray(codePoint, 8));
|
||||
}
|
||||
return bitArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the checksum of the given array of bytes and return it
|
||||
* as an bit array.
|
||||
*
|
||||
* @param {Number[]} bytes
|
||||
* @return {Number[]}
|
||||
*/
|
||||
#calculateChecksum(bytes) {
|
||||
const checksum = Utility.crc8Autosar(bytes);
|
||||
return this.#numberToBitArray(checksum, 8);
|
||||
}
|
||||
|
||||
getData() {
|
||||
return [...this.#header, ...this.#payload, ...this.#checksum];
|
||||
}
|
||||
}
|
73
src/website/scripts/Utility.js
Normal file
73
src/website/scripts/Utility.js
Normal file
|
@ -0,0 +1,73 @@
|
|||
const logParagraph = document.getElementById("log");
|
||||
|
||||
class Utility {
|
||||
/**
|
||||
* This method calculates the CRC-8-AUTOSAR checksum of the given
|
||||
* array of bytes.
|
||||
*
|
||||
* This code is based on the code examples from the following page:
|
||||
* http://www.sunshine2k.de/articles/coding/crc/understanding_crc.html
|
||||
*
|
||||
* The specifics of the algorithm is coming from the AUTOSAR CRC specification:
|
||||
* https://www.autosar.org/fileadmin/user_upload/standards/classic/21-11/AUTOSAR_SWS_CRCLibrary.pdf
|
||||
*
|
||||
* > Utility.crc8([0x0, 0x0, 0x0, 0x0]).toString(16)
|
||||
* "12"
|
||||
* > Utility.crc8([0xF2, 0x01, 0x83]).toString(16)
|
||||
* "c2"
|
||||
* > Utility.crc8([0x0F, 0xAA, 0x00, 0x55]).toString(16)
|
||||
* "c6"
|
||||
* > Utility.crc8([0x00, 0xff, 0x55, 0x11]).toString(16)
|
||||
* "77"
|
||||
* > Utility.crc8([0x33, 0x22, 0x55, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]).toString(16)
|
||||
* "11"
|
||||
* > Utility.crc8([0x92, 0x6b, 0x55]).toString(16)
|
||||
* "33"
|
||||
* > Utility.crc8([0xff, 0xff, 0xff, 0xff]).toString(16)
|
||||
* "6c"
|
||||
*
|
||||
* @param {Number[]} bytes
|
||||
* @return {Number}
|
||||
*/
|
||||
static crc8Autosar(bytes) {
|
||||
const polynomial = 0x2f;
|
||||
const xorOut = 0xff;
|
||||
let crc = 0xff;
|
||||
|
||||
for (const byte of bytes) {
|
||||
crc ^= byte;
|
||||
|
||||
for (let i = 0; i < 8; i++) {
|
||||
if ((crc & 0x80) != 0) {
|
||||
crc = ((crc << 1) ^ polynomial) & 0xff;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crc ^ xorOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a bit array and encode the contents using manchester encoding.
|
||||
*
|
||||
* @param {Number[]} bitArray
|
||||
* @return {Number[]}
|
||||
*/
|
||||
static manchesterEncode(bitArray) {
|
||||
const encodedBits = [];
|
||||
|
||||
for (const bit of bitArray) {
|
||||
encodedBits.push(bit ^ 1);
|
||||
encodedBits.push(bit ^ 0);
|
||||
}
|
||||
|
||||
return encodedBits;
|
||||
}
|
||||
|
||||
static log(text) {
|
||||
//logParagraph.innerHTML = `${text}<br>${logParagraph.innerHTML}`;
|
||||
console.log(text);
|
||||
}
|
||||
}
|
1
src/website/scripts/Worker.js
Normal file
1
src/website/scripts/Worker.js
Normal file
|
@ -0,0 +1 @@
|
|||
while (true) {}
|
75
src/website/styles/main.css
Normal file
75
src/website/styles/main.css
Normal file
|
@ -0,0 +1,75 @@
|
|||
:root {
|
||||
--spacing: 12px;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#message {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#log {
|
||||
height: 300px;
|
||||
font-size: 0.8rem;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: var(--spacing);
|
||||
}
|
||||
|
||||
button {
|
||||
width: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/*https://loading.io/css/*/
|
||||
#message:not(:empty):after {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid black;
|
||||
border-color: black transparent black transparent;
|
||||
animation: loading-indicator 1.2s linear infinite;
|
||||
right: 0;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
@keyframes loading-indicator {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
Reference in a new issue