wip: big refactor
This commit is contained in:
115
AudioSender.js
Normal file
115
AudioSender.js
Normal file
@@ -0,0 +1,115 @@
|
||||
let audioContext;
|
||||
let CHANNELS = [];
|
||||
let DESTINATION;
|
||||
let ON_START;
|
||||
let ON_STOP;
|
||||
let ON_SEND;
|
||||
let CHANNEL_OSCILLATORS = [];
|
||||
let WAVE_FORM;
|
||||
|
||||
let stopOscillatorsTimeoutId;
|
||||
|
||||
export const changeConfiguration = ({
|
||||
channels,
|
||||
destination,
|
||||
startCallback,
|
||||
stopCallback,
|
||||
sendCallback,
|
||||
waveForm
|
||||
}) => {
|
||||
CHANNELS = channels;
|
||||
DESTINATION = destination;
|
||||
ON_START = startCallback;
|
||||
ON_STOP = stopCallback;
|
||||
ON_SEND = sendCallback;
|
||||
WAVE_FORM = waveForm;
|
||||
}
|
||||
|
||||
export const setAudioContext = ctx => audioContext = ctx;
|
||||
|
||||
function getAudioContext() {
|
||||
if(!audioContext) {
|
||||
throw 'Audio context not provided.';
|
||||
}
|
||||
if(audioContext.state === 'suspended') {
|
||||
audioContext.resume();
|
||||
}
|
||||
return audioContext;
|
||||
}
|
||||
|
||||
const getChannels = () => CHANNELS;
|
||||
const getDestination = () => DESTINATION;
|
||||
|
||||
export const now = () => getAudioContext().currentTime;
|
||||
|
||||
export function beginAt(streamStartSeconds) {
|
||||
stopTimeout();
|
||||
const oscillators = getOscillators();
|
||||
if(oscillators.length !== 0) stop();
|
||||
const audioContext = getAudioContext();
|
||||
const channels = getChannels();
|
||||
const channelCount = channels.length;
|
||||
const destination = getDestination();;
|
||||
// create our oscillators
|
||||
for(let i = 0; i < channelCount; i++) {
|
||||
const oscillator = audioContext.createOscillator();
|
||||
oscillator.connect(destination);
|
||||
oscillator.type = WAVE_FORM;
|
||||
oscillator.start(streamStartSeconds);
|
||||
oscillators.push(oscillator);
|
||||
}
|
||||
callFn(ON_START);
|
||||
return oscillators;
|
||||
}
|
||||
function getOscillators() {
|
||||
return CHANNEL_OSCILLATORS;
|
||||
}
|
||||
export function send(bits, startSeconds) {
|
||||
const oscillators = getOscillators();
|
||||
getChannels().forEach((channel, i) => {
|
||||
// send missing bits as zero
|
||||
const isHigh = bits[i] ?? 0;
|
||||
callFn(ON_SEND, isHigh);
|
||||
const oscillator = oscillators[i];
|
||||
// already at correct frequency
|
||||
if(oscillator.on === isHigh) return;
|
||||
oscillator.on = isHigh;
|
||||
const hz = channel[isHigh ? 1 : 0];
|
||||
oscillator.frequency.setValueAtTime(hz, startSeconds);
|
||||
});
|
||||
}
|
||||
const stopTimeout = () => {
|
||||
if(stopOscillatorsTimeoutId) {
|
||||
window.setTimeout(stopOscillatorsTimeoutId);
|
||||
stopOscillatorsTimeoutId = undefined;
|
||||
}
|
||||
}
|
||||
export function stopAt(streamEndSeconds) {
|
||||
const channels = getChannels();
|
||||
const oscillators = getOscillators();
|
||||
const channelCount = channels.length;
|
||||
// silence oscillators when done
|
||||
for(let channel = 0; channel < channelCount; channel++) {
|
||||
const oscillator = oscillators[channel];
|
||||
oscillator?.stop(streamEndSeconds);
|
||||
}
|
||||
stopTimeout();
|
||||
stopOscillatorsTimeoutId = window.setTimeout(
|
||||
stop,
|
||||
(streamEndSeconds - now()) * 1000
|
||||
);
|
||||
}
|
||||
export function stop() {
|
||||
const time = now();
|
||||
const oscillators = getOscillators();
|
||||
oscillators.forEach(
|
||||
oscillator => {
|
||||
oscillator?.stop(time);
|
||||
oscillator?.disconnect();
|
||||
}
|
||||
)
|
||||
oscillators.length = 0;
|
||||
callFn(ON_STOP);
|
||||
stopTimeout();
|
||||
}
|
||||
const callFn = (fn, ...args) => typeof fn === 'function' ? fn(...args) : 0;
|
||||
103
CRC.js
Normal file
103
CRC.js
Normal file
@@ -0,0 +1,103 @@
|
||||
function calcCrc(
|
||||
bytes,
|
||||
size,
|
||||
polynomial,
|
||||
{
|
||||
initialization = 0,
|
||||
reflectIn = false,
|
||||
reflectOut = false,
|
||||
xorOut = 0
|
||||
} = {}
|
||||
) {
|
||||
if(bytes.length === 0) return 0;
|
||||
const validBits = (1 << size) - 1;
|
||||
const mostSignificantBit = 1 << size - 1;
|
||||
const bitsBeforeLastByte = size - 8;
|
||||
|
||||
// setup our initial value
|
||||
let crc = initialization;
|
||||
|
||||
function reverseBits(value, size) {
|
||||
let reversed = 0;
|
||||
for(let i = 0; i < size; i++) {
|
||||
// if bit position is on
|
||||
if(value & (1<<i)) {
|
||||
// turn on bit in reverse order
|
||||
reversed |= 1 << (size - 1 - i);
|
||||
}
|
||||
}
|
||||
return reversed;
|
||||
}
|
||||
|
||||
for(let byte of bytes) {
|
||||
// reflect incoming bits?
|
||||
if(reflectIn){
|
||||
byte = reverseBits(byte, 8);
|
||||
}
|
||||
// xor current byte against first byte of crc
|
||||
crc ^= byte << bitsBeforeLastByte;
|
||||
// loop through the first 8 bits of the crc
|
||||
for(let i = 0; i < 8; i++) {
|
||||
// is first bit 1?
|
||||
const isFlagged = crc & mostSignificantBit;
|
||||
// if flagged, xor the first bit to prevent overflow
|
||||
if(isFlagged) crc ^= mostSignificantBit;
|
||||
// shift bits left
|
||||
crc <<= 1;
|
||||
// remove invalid bits
|
||||
crc &= validBits;
|
||||
// xor the polynomial
|
||||
if(isFlagged) crc ^= polynomial;
|
||||
}
|
||||
}
|
||||
|
||||
// We only want the last [size] bits
|
||||
crc &= validBits;
|
||||
|
||||
// reflect final bits?
|
||||
if(reflectOut) crc = reverseBits(crc, size);
|
||||
|
||||
// xor the final value going out
|
||||
crc ^= xorOut;
|
||||
|
||||
// remove sign
|
||||
if(size >= 32 && crc & mostSignificantBit) {
|
||||
crc >>>= 0;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
export function check(bytes, bitCount = 8) {
|
||||
switch(bitCount) {
|
||||
case 8: return crc8(bytes);
|
||||
case 16: return crc16(bytes);
|
||||
case 32: return crc32(bytes);
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
function crc8(bytes) { return calcCrc(bytes, 8, 0x07); }
|
||||
function crc16(bytes) {
|
||||
return calcCrc(
|
||||
bytes,
|
||||
16,
|
||||
0x8005,
|
||||
{
|
||||
initialization: 0,
|
||||
reflectIn: true,
|
||||
reflectOut: true,
|
||||
xorOut: 0
|
||||
}
|
||||
);
|
||||
}
|
||||
function crc32(bytes) {
|
||||
return calcCrc(
|
||||
bytes,
|
||||
32,
|
||||
0x04C11DB7,
|
||||
{
|
||||
initialization: 0xFFFFFFFF,
|
||||
reflectIn: true,
|
||||
reflectOut: true,
|
||||
xorOut: 0x0
|
||||
}
|
||||
);
|
||||
}
|
||||
53
HammingEncoding.js
Normal file
53
HammingEncoding.js
Normal file
@@ -0,0 +1,53 @@
|
||||
// Encoding to encode/decode data with Hamming Error Correction
|
||||
export const DECODED_SIZE = 4;
|
||||
export const ENCODED_SIZE = 7;
|
||||
|
||||
export const blockSize = () => ({
|
||||
encoded: ENCODED_SIZE,
|
||||
decoded: DECODED_SIZE
|
||||
});
|
||||
|
||||
export const encode = (bits) => {
|
||||
const encodedBits = [];
|
||||
for(let i = 0; i < bits.length; i+= DECODED_SIZE) {
|
||||
const block = bits.slice(i, i + DECODED_SIZE);
|
||||
encodedBits.push(...encodeBlock(block));
|
||||
}
|
||||
return encodedBits;
|
||||
}
|
||||
export const decode = bits => {
|
||||
const decodedBits = [];
|
||||
for(let i = 0; i < bits.length; i += ENCODED_SIZE) {
|
||||
const block = bits.slice(i, i + ENCODED_SIZE);
|
||||
decodedBits.push(...decodeBlock(block));
|
||||
}
|
||||
return decodedBits;
|
||||
}
|
||||
|
||||
const encodeBlock = bits => {
|
||||
if(bits.length !== DECODED_SIZE) return [];
|
||||
return [
|
||||
bits[0] ^ bits[1] ^ bits[3],
|
||||
bits[0] ^ bits[2] ^ bits[3],
|
||||
bits[0],
|
||||
bits[1] ^ bits[2] ^ bits[3],
|
||||
bits[1],
|
||||
bits[2],
|
||||
bits[3]
|
||||
]
|
||||
}
|
||||
|
||||
const decodeBlock = bits => {
|
||||
if(bits.length !== ENCODED_SIZE) return [];
|
||||
const error_1 = bits[0] ^ bits[2] ^ bits[4] ^ bits[6];
|
||||
const error_2 = bits[1] ^ bits[2] ^ bits[5] ^ bits[6];
|
||||
const error_3 = bits[3] ^ bits[4] ^ bits[5] ^ bits[6];
|
||||
let error = (error_3 << 2) | (error_2 << 1) | error_1;
|
||||
if(error !== 0) bits[error - 1] ^= 1;
|
||||
return [
|
||||
bits[2],
|
||||
bits[4],
|
||||
bits[5],
|
||||
bits[6]
|
||||
];
|
||||
}
|
||||
58
Humanize.js
Normal file
58
Humanize.js
Normal file
@@ -0,0 +1,58 @@
|
||||
export function byteSize(count) {
|
||||
let unitIndex = 0;
|
||||
const units = ['bytes', 'kb', 'mb', 'gb', 'tb', 'pb'];
|
||||
while(count > 999) {
|
||||
count /= 1024;
|
||||
unitIndex++;
|
||||
if(unitIndex === units.length - 1) break;
|
||||
}
|
||||
count = Math.floor(count * 10) * 0.1
|
||||
return `${count.toLocaleString()} ${units[unitIndex]}`
|
||||
}
|
||||
export function bitsPerSecond(bps) {
|
||||
let unitIndex = 0;
|
||||
const units = ['baud', 'Kbps', 'Mbps', 'Gbps', 'Tbps', 'Pbps'];
|
||||
while(bps > 999) {
|
||||
bps /= 1024;
|
||||
unitIndex++;
|
||||
if(unitIndex === units.length - 1) break;
|
||||
}
|
||||
bps = Math.floor(bps * 10) * 0.1
|
||||
return `${bps.toLocaleString()} ${units[unitIndex]}`
|
||||
}
|
||||
export function durationMilliseconds(milliseconds) {
|
||||
const lookup = [
|
||||
{mod: 1000, singular: 'ms', plural: 'ms'},
|
||||
{mod: 60, singular: 's', plural: 's'},
|
||||
{mod: 60, singular: 'm', plural: 'm'},
|
||||
{mod: 24, singular: 'h', plural: 'h'},
|
||||
{mod: 7, singular: 'd', plural: 'd'},
|
||||
{mod: 53, singular: 'w', plural: 'w'},
|
||||
{mod: 10, singular: 'y', plural: 'y'},
|
||||
{mod: 10, singular: 'd', plural: 'd'},
|
||||
{mod: 10, singular: 'c', plural: 'c'},
|
||||
{mod: 10, singular: 'm', plural: 'm'},
|
||||
]
|
||||
const units = [];
|
||||
let remaining = Math.floor(milliseconds);
|
||||
for(let i = 0; i < lookup.length; i++) {
|
||||
const {mod, singular, plural} = lookup[i];
|
||||
const value = remaining % mod;
|
||||
units.push({value, singular, plural});
|
||||
remaining -= value;
|
||||
if(remaining < mod) break;
|
||||
remaining = remaining / mod;
|
||||
}
|
||||
|
||||
return units
|
||||
// largest units first
|
||||
.reverse()
|
||||
// top 2 largest values
|
||||
.filter((_, i) => i < 2)
|
||||
// drop values of zero
|
||||
.filter(({value}) => value > 0)
|
||||
// humanize unit
|
||||
.map(({value, singular, plural}) => value + (value === 1 ? singular : plural))
|
||||
// combine
|
||||
.join(' ');
|
||||
}
|
||||
54
InterleaverEncoding.js
Normal file
54
InterleaverEncoding.js
Normal file
@@ -0,0 +1,54 @@
|
||||
// InterleaverEncoding
|
||||
|
||||
// Rolls / shifts elements of an array so that each
|
||||
// block of N data elements contains the minimal
|
||||
// amount of original data possible.
|
||||
// Elements toward the end of an array are wrapped
|
||||
// around to the beginning
|
||||
|
||||
// This is primarily used for data transfer where
|
||||
// multiple channels representing various bits in
|
||||
// the same block may be subsceptable to noise and
|
||||
// provide the wrong value for one or more bits in
|
||||
// a block
|
||||
|
||||
let BLOCK_SIZE = 1;
|
||||
|
||||
export const blockSize = () => ({
|
||||
encoded: BLOCK_SIZE,
|
||||
decoded: BLOCK_SIZE
|
||||
});
|
||||
|
||||
export const changeConfiguration = ({
|
||||
blockSize
|
||||
}) => {
|
||||
BLOCK_SIZE = blockSize ?? 1;
|
||||
}
|
||||
|
||||
export const decode = bits => encode(bits, true);
|
||||
|
||||
export const encode = (bits, undo = false) => {
|
||||
// Block sizes of 1 or less are unable to move bits
|
||||
if(BLOCK_SIZE <= 1) return bits;
|
||||
|
||||
// We need at least 1 extra bit for one bit to escape the block
|
||||
if(bits.length <= BLOCK_SIZE ) return bits;
|
||||
|
||||
// loop through indexes of a block
|
||||
for(let blockMovement = 1; blockMovement < BLOCK_SIZE; blockMovement++) {
|
||||
// Move every N bit N blocks over...
|
||||
bits.filter((_, i) =>
|
||||
// values to be moved to different blocks
|
||||
i % BLOCK_SIZE === blockMovement
|
||||
).map((_,i,a) => {
|
||||
// values moved N blocks
|
||||
if(undo) i -= blockMovement; else i += blockMovement;
|
||||
i = ((i % a.length) + a.length) % a.length;
|
||||
return a[i];
|
||||
}).forEach((v, i) => {
|
||||
// replace with new values
|
||||
bits[blockMovement + (i * BLOCK_SIZE)] = v;
|
||||
})
|
||||
};
|
||||
return bits;
|
||||
}
|
||||
147
PacketUtils.js
Normal file
147
PacketUtils.js
Normal file
@@ -0,0 +1,147 @@
|
||||
let SEGMENT_DURATION = 30;
|
||||
let PACKET_SIZE_BITS = 8;
|
||||
let DATA_SIZE_BITS = 8;
|
||||
let DATA_SIZE_CRC_BITS = 8;
|
||||
let BITS_PER_SEGMENT = 1;
|
||||
let PACKET_ENCODING = false;
|
||||
let PACKET_ENCODING_SIZE = 7;
|
||||
let PACKET_DECODING_SIZE = 4;
|
||||
let ENCODING;
|
||||
|
||||
export const changeConfiguration = (config) => {
|
||||
const {
|
||||
segmentDurationMilliseconds,
|
||||
packetSizeBitCount,
|
||||
dataSizeBitCount,
|
||||
dataSizeCrcBitCount,
|
||||
bitsPerSegment,
|
||||
packetEncoding,
|
||||
packetEncodingBitCount,
|
||||
packetDecodingBitCount
|
||||
} = config;
|
||||
SEGMENT_DURATION = segmentDurationMilliseconds;
|
||||
PACKET_SIZE_BITS = packetSizeBitCount;
|
||||
DATA_SIZE_BITS = dataSizeBitCount;
|
||||
DATA_SIZE_CRC_BITS = dataSizeCrcBitCount;
|
||||
BITS_PER_SEGMENT = bitsPerSegment;
|
||||
PACKET_ENCODING = packetEncoding;
|
||||
PACKET_ENCODING_SIZE = packetEncodingBitCount;
|
||||
PACKET_DECODING_SIZE = packetDecodingBitCount;
|
||||
}
|
||||
export const setEncoding = (encoding) => {
|
||||
ENCODING = encoding;
|
||||
}
|
||||
const encodePacket = (bits) => ENCODING.encode(bits);
|
||||
export const getSegmentDurationMilliseconds = () => SEGMENT_DURATION;
|
||||
export const getPacketMaxByteCount = () => 2 ** PACKET_SIZE_BITS;
|
||||
export const getDataMaxByteCount = () => 2 ** DATA_SIZE_BITS;
|
||||
export const getBitsPerSegment = () => BITS_PER_SEGMENT;
|
||||
export const isPacketEncoded = () => PACKET_ENCODING;
|
||||
export const packetEncodingBlockSize = () => isPacketEncoded() ? PACKET_ENCODING_SIZE : 0;
|
||||
export const packetDecodingBlockSize = () => isPacketEncoded() ? PACKET_DECODING_SIZE : 0;
|
||||
export const getPacketDataBitCount = () => {
|
||||
if(isPacketEncoded()) return getPacketEncodingBlockCount() * PACKET_DECODING_SIZE;
|
||||
return getPacketMaxBitCount();
|
||||
}
|
||||
export function fromByteCountGetPacketLastUnusedBitCount(byteCount) {
|
||||
const bitCount = byteCount * 8;
|
||||
const availableBits = getPacketMaxBitCount();
|
||||
const dataBitsPerPacket = getPacketDataBitCount();
|
||||
let bitsInLastPacket = bitCount % dataBitsPerPacket;
|
||||
let usedBits = bitsInLastPacket;
|
||||
if(isPacketEncoded()) {
|
||||
const blocks = Math.ceil(usedBits / packetDecodingBlockSize())
|
||||
usedBits = blocks * packetEncodingBlockSize();
|
||||
}
|
||||
return availableBits - usedBits;
|
||||
}
|
||||
export function getPacketLastSegmentUnusedBitCount() {
|
||||
return (BITS_PER_SEGMENT - (getPacketMaxBitCount() % BITS_PER_SEGMENT));
|
||||
}
|
||||
export const getBaud = () => {
|
||||
return Math.floor(getBitsPerSegment() / getSegmentDurationSeconds());
|
||||
}
|
||||
export const getEffectiveBaud = () => {
|
||||
return Math.floor(getPacketDataBitCount() / getPacketDurationSeconds());
|
||||
}
|
||||
export const getEncodedPacketDataBitCount = () => {
|
||||
return isPacketEncoded() ? getPacketEncodingBitCount() : 0;
|
||||
}
|
||||
export const getPacketUsedBitCount = () =>
|
||||
isPacketEncoded() ? getPacketEncodingBitCount() : getPacketMaxBitCount();
|
||||
export const getPacketUnusedBitCount = () => getPacketMaxBitCount() - getPacketUsedBitCount();
|
||||
export const getMaxPackets = () =>
|
||||
Math.ceil((getDataMaxByteCount() * 8) / getPacketUsedBitCount());
|
||||
export const getMaxDurationMilliseconds = () => getMaxPackets() * getPacketDurationMilliseconds();
|
||||
export const getPacketEncodingBitCount = () => getPacketEncodingBlockCount() * packetEncodingBlockSize();
|
||||
export const canSendPacket = () => {
|
||||
const maxBits = getPacketMaxBitCount();
|
||||
if(maxBits < 1) return false;
|
||||
return isPacketEncoded() ? maxBits >= packetEncodingBlockSize() : true;
|
||||
}
|
||||
export const getPacketEncodingBlockCount = () =>
|
||||
isPacketEncoded() ? Math.floor(getPacketMaxBitCount() / packetEncodingBlockSize()) : 0;
|
||||
export const getPacketizationHeaderBitCount = () => DATA_SIZE_BITS + DATA_SIZE_CRC_BITS;
|
||||
export const getPacketizationBitCountFromBitCount = (bitCount) => bitCount + getPacketizationHeaderBitCount();
|
||||
export const getPacketizationBitCountFromByteCount = (byteCount) =>
|
||||
getPacketizationBitCountFromBitCount(byteCount * 8);
|
||||
export const getPacketizationByteCountFromByteCount = (byteCount) =>
|
||||
Math.ceil(getPacketizationBitCountFromByteCount(byteCount) / 8);
|
||||
|
||||
export const getPacketizationByteCountFromBitCount = bitCount =>
|
||||
Math.ceil(getPacketizationBitCountFromBitCount(bitCount) / 8);
|
||||
|
||||
export const getDataTransferDurationMillisecondsFromByteCount = (byteCount) =>
|
||||
getDataTransferDurationMilliseconds(getPacketizationBitCountFromByteCount(byteCount));
|
||||
export const getDataTransferDurationSeconds = (bitCount) =>
|
||||
getDataTransferDurationMilliseconds(bitCount) / 1000;
|
||||
export const getPacketCount = (bitCount) =>
|
||||
canSendPacket() ? Math.ceil(bitCount / getPacketDataBitCount()) : 0;
|
||||
export const getDataTransferDurationMilliseconds = (bitCount) =>
|
||||
getPacketCount(bitCount) * getPacketDurationMilliseconds();
|
||||
export const getPacketDurationSeconds = () => getPacketDurationMilliseconds() / 1000;
|
||||
export const getSegmentDurationSeconds = () => getSegmentDurationMilliseconds() / 1000;
|
||||
export const getPacketMaxBitCount = () => getPacketMaxByteCount() * 8;
|
||||
export const getPacketSegmentCount = () => Math.ceil(getPacketMaxBitCount() / getBitsPerSegment());
|
||||
export const getPacketDurationMilliseconds = () =>
|
||||
getPacketSegmentCount() * getSegmentDurationMilliseconds();
|
||||
export const getPacketIndex = (transferStartedMilliseconds, time) =>
|
||||
Math.floor((time - transferStartedMilliseconds) / getPacketDurationMilliseconds());
|
||||
|
||||
export function getPacketUsedBits(bits, packetIndex) {
|
||||
if(!canSendPacket()) return [];
|
||||
|
||||
// How many data bits will be in our packet?
|
||||
const dataBitCount = getPacketDataBitCount();
|
||||
|
||||
// grab our data
|
||||
const startIndex = packetIndex * dataBitCount;
|
||||
const endIndex = startIndex + dataBitCount;
|
||||
let packetBits = bits.slice(startIndex, endIndex);
|
||||
|
||||
return isPacketEncoded() ? encodePacket(packetBits) : packetBits;
|
||||
}
|
||||
export const getPacketBits = (bits, packetIndex) =>
|
||||
canSendPacket() ? getPacketUsedBits(bits, packetIndex) : [];
|
||||
|
||||
export function getPacketSegmentIndex(transferStartedMilliseconds, time) {
|
||||
return getTranserSegmentIndex(transferStartedMilliseconds, time) % getPacketSegmentCount();
|
||||
}
|
||||
export function getTranserSegmentIndex(transferStartedMilliseconds, time) {
|
||||
const transferMs = time - transferStartedMilliseconds;
|
||||
const segmentMs = getSegmentDurationMilliseconds();
|
||||
return Math.floor(transferMs / segmentMs);
|
||||
}
|
||||
export function getPacketSegmentStartMilliseconds(transferStartedMilliseconds, packetIndex, segmentIndex) {
|
||||
const packetStart = getPacketStartMilliseconds(transferStartedMilliseconds, packetIndex);
|
||||
const segmentOffset = segmentIndex * getSegmentDurationMilliseconds();
|
||||
return packetStart + segmentOffset;
|
||||
}
|
||||
export function getPacketStartMilliseconds(transferStartedMilliseconds, packetIndex) {
|
||||
if(packetIndex < 0) return 0;
|
||||
if(packetIndex === 0) return transferStartedMilliseconds;
|
||||
return transferStartedMilliseconds + (packetIndex * getPacketDurationMilliseconds());
|
||||
}
|
||||
export function getPacketSegmentEndMilliseconds(transferStartedMilliseconds, packetIndex, segmentIndex) {
|
||||
return getPacketSegmentStartMilliseconds(transferStartedMilliseconds, packetIndex, segmentIndex + 1) - 0.1;
|
||||
}
|
||||
72
Randomizer.js
Normal file
72
Randomizer.js
Normal file
@@ -0,0 +1,72 @@
|
||||
const UNICODE_TEXT = [
|
||||
'\u0041', // Latin
|
||||
'\u0410', // Cyrillic
|
||||
'\u0391', // Greek
|
||||
'\u05D0', // Hebrew
|
||||
'\u0627', // Arabic
|
||||
'\u0905', // Devanagari
|
||||
'\u0E01', // Thai
|
||||
'\u3042', // Japanese Hiragana
|
||||
'\u30A2', // Japanese Katakana
|
||||
'\uAC00', // Korean Hangul
|
||||
'\u10D0', // Georgian
|
||||
'\u0531', // Armenian
|
||||
'\u4E00', // Chinese
|
||||
'\u0F40', // Tibetan
|
||||
'\u0985', // Bengali
|
||||
'\u0A85', // Gujarati
|
||||
'\u0A05', // Gurmukhi
|
||||
'\u0C85', // Kannada
|
||||
'\u1780', // Khmer
|
||||
'\u0E81', // Lao
|
||||
'\u0D05', // Malayalam
|
||||
'\u1000', // Myanmar
|
||||
'\u0D85', // Sinhala
|
||||
'\u0B85', // Tamil
|
||||
'\u0C05', // Telugu
|
||||
'\u1200', // Amharic
|
||||
'\u1000', // Burmese
|
||||
'\u0C85', // Kannada
|
||||
'\u0B05', // Oriya
|
||||
'\u0D85' // Sinhala
|
||||
];
|
||||
const UNICODE_EMOJI = [
|
||||
"\u{1F600}", "\u{1F601}", "\u{1F602}", "\u{1F923}", "\u{1F603}", "\u{1F604}", "\u{1F605}", "\u{1F606}",
|
||||
"\u{1F609}", "\u{1F60A}", "\u{1F60B}", "\u{1F60E}", "\u{1F60D}", "\u{1F618}", "\u{1F617}", "\u{1F619}",
|
||||
"\u{1F61A}", "\u{1F61B}", "\u{263A}", "\u{1F642}", "\u{1F60F}", "\u{1F60C}", "\u{1F61C}", "\u{1F61D}",
|
||||
"\u{1F61E}", "\u{1F61F}", "\u{1F612}", "\u{1F613}", "\u{1F614}", "\u{1F615}", "\u{1F643}", "\u{1F610}",
|
||||
"\u{1F611}", "\u{1F636}", "\u{1F607}", "\u{1F60F}", "\u{1F623}", "\u{1F625}", "\u{1F62E}", "\u{1F62F}",
|
||||
"\u{1F62A}", "\u{1F62B}", "\u{1F634}", "\u{1F60D}", "\u{1F615}", "\u{1F625}", "\u{1F622}", "\u{1F62D}",
|
||||
"\u{1F631}", "\u{1F616}", "\u{1F623}", "\u{1F624}", "\u{1F630}", "\u{1F621}", "\u{1F620}", "\u{1F637}",
|
||||
"\u{1F912}", "\u{1F915}", "\u{1F922}", "\u{1F92A}", "\u{1F605}", "\u{1F624}", "\u{1F62C}", "\u{1F687}",
|
||||
"\u{1F636}", "\u{1F610}", "\u{1F611}", "\u{1F974}", "\u{1F612}", "\u{1F644}", "\u{1F913}", "\u{1F615}",
|
||||
"\u{1F62C}", "\u{1F636}", "\u{1F922}", "\u{1F927}", "\u{1F974}", "\u{1F975}", "\u{1F976}", "\u{1F92E}",
|
||||
"\u{1F927}", "\u{1F976}", "\u{1F925}", "\u{1F92F}", "\u{1F975}", "\u{1F976}", "\u{1F92E}", "\u{1F925}",
|
||||
"\u{1F924}", "\u{1F631}", "\u{1F634}", "\u{1F62C}", "\u{1F91E}", "\u{1F621}", "\u{1F608}", "\u{1F47F}",
|
||||
"\u{1F480}", "\u{1F47B}", "\u{1F47D}", "\u{1F916}", "\u{1F608}", "\u{1F47A}", "\u{1F479}", "\u{1F47C}",
|
||||
"\u{1F47E}", "\u{1F916}", "\u{1F4A9}", "\u{1F608}", "\u{1F4A4}", "\u{1F525}", "\u{1F4A3}", "\u{1F52E}",
|
||||
"\u{1F4A2}", "\u{1F4A1}", "\u{1F6A8}", "\u{1F3B6}", "\u{1F519}", "\u{1F5E8}", "\u{1F4F3}", "\u{1F4F1}",
|
||||
"\u{1F4F2}", "\u{1F514}", "\u{1F3A4}", "\u{1F4F9}", "\u{1F4F7}", "\u{1F4F8}", "\u{1F4F4}", "\u{1F4F6}",
|
||||
"\u{1F3AF}", "\u{1F4FD}", "\u{1F4FC}", "\u{1F4E5}"
|
||||
];
|
||||
const ASCII_CHARS = new Array(255)
|
||||
.fill(0)
|
||||
.map((_, code) => String.fromCharCode(code))
|
||||
.filter((_, code) =>
|
||||
// printable ascii
|
||||
(code >= 32 && code <= 126) ||
|
||||
// extended ascii (accented letters, currency, etc.)
|
||||
(code >= 160 && code <= 255)
|
||||
);
|
||||
const PRINTABLE_CHARS = [
|
||||
...UNICODE_EMOJI,
|
||||
...ASCII_CHARS,
|
||||
...UNICODE_TEXT
|
||||
];
|
||||
|
||||
function randomCharacter() {
|
||||
const index = Math.floor(Math.random() * PRINTABLE_CHARS.length);
|
||||
return PRINTABLE_CHARS[index];
|
||||
}
|
||||
|
||||
export const text = (length) => new Array(length).fill(0).map(randomCharacter).join('');
|
||||
111
StreamManager.js
Normal file
111
StreamManager.js
Normal file
@@ -0,0 +1,111 @@
|
||||
import { bitsToInt } from "./converters";
|
||||
|
||||
const BITS = [];
|
||||
let BITS_PER_PACKET = 0;
|
||||
let SEGMENTS_PER_PACKET = 0;
|
||||
let BITS_PER_SEGMENT = 0;
|
||||
let STREAM_HEADERS = [];
|
||||
let SEGMENT_ENCODING = {
|
||||
encode: bits => bits,
|
||||
decode: bits => bits
|
||||
};
|
||||
let PACKET_ENCODING = {
|
||||
encode: bits => bits,
|
||||
decode: bits => bits
|
||||
}
|
||||
|
||||
export const changeConfiguration = ({
|
||||
segmentsPerPacket,
|
||||
bitsPerPacket,
|
||||
bitsPerSegment,
|
||||
streamHeaders
|
||||
}) => {
|
||||
BITS_PER_PACKET = bitsPerPacket;
|
||||
SEGMENTS_PER_PACKET = segmentsPerPacket;
|
||||
BITS_PER_SEGMENT = bitsPerSegment;
|
||||
STREAM_HEADERS = streamHeaders;
|
||||
}
|
||||
const noEncoding = bits => bits;
|
||||
export const setSegmentEncoding = ({ encode, decode } = {}) => {
|
||||
SEGMENT_ENCODING.encode = encode ?? noEncoding;
|
||||
SEGMENT_ENCODING.decode = decode ?? noEncoding;
|
||||
}
|
||||
export const setPacketEncoding = ({ encode, decode } = {}) => {
|
||||
PACKET_ENCODING.encode = encode ?? noEncoding;
|
||||
PACKET_ENCODING.decode = decode ?? noEncoding;
|
||||
}
|
||||
export const addBits = (
|
||||
packetIndex,
|
||||
segmentIndex,
|
||||
bits
|
||||
) => {
|
||||
if(BITS[packetIndex] === undefined) {
|
||||
BITS[packetIndex] = [];
|
||||
}
|
||||
BITS[packetIndex][segmentIndex] = bits;
|
||||
}
|
||||
export const getPacketReceivedCount = () => {
|
||||
if(BITS.length === 0) return 1;
|
||||
return BITS.length;
|
||||
}
|
||||
export const getStreamBits = () => {
|
||||
const bits = [];
|
||||
const packetCount = getPacketReceivedCount();
|
||||
for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) {
|
||||
const packet = BITS[packetIndex] ?? [];
|
||||
for(let segmentIndex = 0; segmentIndex < SEGMENTS_PER_PACKET; segmentIndex++) {
|
||||
let segment = packet[segmentIndex] ?? [];
|
||||
for(let bitIndex = 0; bitIndex < BITS_PER_SEGMENT; bitIndex++) {
|
||||
const bit = segment[bitIndex];
|
||||
bits.push(bit ?? 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
export const getPacketSegmentBits = (packetIndex, segmentIndex) => BITS[packetIndex]?.[segmentIndex];
|
||||
export const getAllPacketBits = () => {
|
||||
const packetCount = getPacketReceivedCount();
|
||||
const bits = [];
|
||||
for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) {
|
||||
bits.push(...getPacketBits(packetIndex));
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
export const getPacketBits = (packetIndex, defaultBit = 0) => {
|
||||
const bits = [];
|
||||
const packet = BITS[packetIndex] ?? [];
|
||||
for(let segmentIndex = 0; segmentIndex < SEGMENTS_PER_PACKET; segmentIndex++) {
|
||||
let segment = packet[segmentIndex] ?? [];
|
||||
segment = SEGMENT_ENCODING.decode(segment);
|
||||
for(let bitIndex = 0; bitIndex < BITS_PER_SEGMENT; bitIndex++) {
|
||||
const bit = segment[bitIndex];
|
||||
bits.push(bit ?? defaultBit);
|
||||
if(bits.length === BITS_PER_PACKET) return bits;
|
||||
}
|
||||
}
|
||||
while(bits.length < BITS_PER_PACKET) bits.push(defaultBit);
|
||||
return bits;
|
||||
}
|
||||
export const getPacketBitsDecoded = (packetIndex, defaultBit = 0) => {
|
||||
const bits = getPacketBits(packetIndex, defaultBit);
|
||||
return PACKET_ENCODING.decode(bits);
|
||||
}
|
||||
const getStreamHeaderBits = name => {
|
||||
const header = STREAM_HEADERS[name];
|
||||
if(!header) return [];
|
||||
const { index, length } = header;
|
||||
const packetCount = getPacketReceivedCount();
|
||||
const bits = [];
|
||||
for(let packetIndex = 0; packetIndex < packetCount; packetIndex++) {
|
||||
bits.push(...getPacketBitsDecoded(packetIndex, 1));
|
||||
if(bits.length >= index + length) break;
|
||||
}
|
||||
return bits.slice(index, index + length);
|
||||
}
|
||||
export const getTransferByteCount = () => {
|
||||
const name = 'transfer byte count';
|
||||
const length = STREAM_HEADERS[name].length;
|
||||
const bits = getStreamHeaderBits(name);
|
||||
return bitsToInt(bits, length);
|
||||
}
|
||||
10
converters.js
Normal file
10
converters.js
Normal file
@@ -0,0 +1,10 @@
|
||||
export const bitsToInt = (bits, bitLength) => {
|
||||
parseInt(bits
|
||||
// only grab the bits we need
|
||||
.slice(0, bitLength)
|
||||
// combine into string
|
||||
.join('')
|
||||
// Assume missing bits were zeros
|
||||
.padEnd(bitLength, '0')
|
||||
);
|
||||
}
|
||||
278
index.html
278
index.html
@@ -4,80 +4,89 @@
|
||||
<head>
|
||||
<title>Data Over Audio</title>
|
||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||
<script src="index.js" type="text/javascript"></script>
|
||||
<script src="index.js" type="module" type="text/javascript"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Data Over Audio</h1>
|
||||
<div class="panels">
|
||||
<div>
|
||||
<h2>Message</h2>
|
||||
<h2>Communications</h2>
|
||||
<div>
|
||||
<h4>Send</h4>
|
||||
<label>
|
||||
<input type="checkbox" id="send-via-speaker">Speakers
|
||||
<input type="radio" name="send-via" id="send-via-analyzer" checked>Analyzer
|
||||
</label><br />
|
||||
<input type="text" id="text-to-send">
|
||||
<button id="send-button">Send</button><br />
|
||||
<div class="raw-data" id="sent-data"></div><br />
|
||||
</div>
|
||||
<div>
|
||||
<h2>Data</h2>
|
||||
Original Bytes: <span id="original-byte-count"></span><br>
|
||||
Packetization Bytes: <span id="packetization-byte-count"></span><br>
|
||||
Packetization Bits: <span id="packetization-bit-count"></span><br>
|
||||
Bits per Packet: <span id="packet-bit-count"></span><br>
|
||||
Data Bits per Packet: <span id="packet-data-bit-count"></span><br>
|
||||
Error Correction: <span id="packet-error-correction"></span><br>
|
||||
Error Blocks per packet: <span id="packet-error-block-count"></span><br>
|
||||
Error Bits per block: <span id="packet-error-bits-per-block"></span><br>
|
||||
Error Correcting Bits per Packet: <span id="packet-error-bit-count"></span><br>
|
||||
Unused bits per packet: <span id="packet-unused-bit-count"></span><br>
|
||||
Unused bits in last packet: <span id="last-packet-unused-bit-count"></span><br>
|
||||
Last segment unused channels in packet: <span id="last-segment-unused-channel-count"></span><br>
|
||||
Packet Count: <span id="packet-count"></span><br>
|
||||
Segments Per Packet: <span id="segments-per-packet"></span><br>
|
||||
Total Segments: <span id="total-segments"></span><br>
|
||||
Segment Duration: <span id="segment-transfer-duration"></span><br>
|
||||
Packet Duration: <span id="packet-transfer-duration"></span><br>
|
||||
Total Duration: <span id="data-transfer-duration"></span><br>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Error Correcting</h2>
|
||||
<div class="raw-data" id="error-correcting-data"></div><br />
|
||||
</div>
|
||||
<div>
|
||||
<h2 >Bits Sent</h2>
|
||||
<div class="raw-data" id="actual-bits-to-send"></div><br />
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
<input type="radio" name="send-via" id="send-via-speaker">Speakers
|
||||
</label><br />
|
||||
<h4>Receive</h4>
|
||||
<label>
|
||||
<input type="checkbox" id="is-listening-checkbox">Listening
|
||||
</label>
|
||||
<h2>Bits Received</h2>
|
||||
<div class="raw-data" id="received-raw-bits"></div><br />
|
||||
Error Percent: <span id="received-raw-bits-error-percent">N/A</span>%
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Encoded Bits Received</h2>
|
||||
<div class="raw-data" id="received-encoded-bits"></div><br />
|
||||
Error Percent: <span id="received-encoded-bits-error-percent">N/A</span>%
|
||||
</div>
|
||||
<h2>Message</h2>
|
||||
<div>
|
||||
<h2>Decoded Bits Received</h2>
|
||||
<div class="raw-data" id="received-decoded-bits"></div><br />
|
||||
Error Percent: <span id="received-decoded-bits-error-percent">N/A</span>%<br />
|
||||
</div>
|
||||
<div>
|
||||
<h2>Decoded</h2>
|
||||
Bytes: <span id="received-packet-original-bytes">N/A</span><br>
|
||||
Length CRC-8: <span id="received-packet-original-bytes-crc">N/A</span><br>
|
||||
<input type="text" id="text-to-send">
|
||||
<button id="send-button">Send</button><br />
|
||||
<h4>Received</h4>
|
||||
<div class="progress-container">
|
||||
<div id="received-progress" class="progress-bar"></div>
|
||||
</div>
|
||||
Text:
|
||||
<div class="raw-data" id="decoded-text"></div><br />
|
||||
<div class="raw-data-small" id="decoded-text"></div><br />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Configuration</h2>
|
||||
<div>
|
||||
<h4>Audio</h4>
|
||||
Wave Form: <select id="wave-form" value="sine">
|
||||
<option value="sine">Sine Wave</option>
|
||||
<option value="square">Square Wave</option>
|
||||
<option value="sawtooth">Sawtooth Wave</option>
|
||||
<option value="triangle">Triangle Wave</option>
|
||||
</select><br>
|
||||
Minimum Frequency: <input id="minimum-frequency" type="number" min="20" max="20000" value="900"><br>
|
||||
Maximum Frequency: <input id="maximum-frequency" type="number" min="20" max="20000" value="1200"><br>
|
||||
Segment Duration: <input id="bit-duration-text" type="number" min="0" max="1000" value="190">ms<br>
|
||||
FFT Size: 2^<input id="fft-size-power-text" type="number" min="5" max="15" value="90"><br>
|
||||
Frequency Resolution Multiplier: <input id="frequency-resolution-multiplier" type="number" min="1" max="20"
|
||||
value="2"><br>
|
||||
Channel Frequency Resolution Padding: <input id="channel-frequency-resolution-padding" type="number" min="0"
|
||||
max="20"><br>
|
||||
|
||||
<h4>Packetization</h4>
|
||||
Packet Size:
|
||||
2^<input id="packet-size-power" type="number" min="0" max="16">
|
||||
<span id="packet-size"></span>
|
||||
<br>
|
||||
<h4>Receiving</h4>
|
||||
Amplitude Threshold: <input id="amplitude-threshold-text" type="number" min="0" max="100" value="75"><br>
|
||||
Last Segment Percent: <input id="last-bit-percent" type="number" min="0" max="100" value="90">%<br>
|
||||
Smoothing Time Constant: <input id="smoothing-time-constant-text" type="number" min="0.00" max="1.00"
|
||||
step="0.01" value="0.00"><br>
|
||||
<h4>Encoding</h4>
|
||||
<label>
|
||||
<input type="checkbox" id="periodic-interleaving" checked>Interleaving
|
||||
</label><br />
|
||||
<label>
|
||||
<input type="checkbox" id="error-correction-hamming" checked>Error Correction
|
||||
</label><br>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Channels</h2>
|
||||
<div>
|
||||
<ol id="channel-list" start="0"></ol>
|
||||
</div>
|
||||
</div>
|
||||
<div class="chart">
|
||||
<h2>Frequency Graph</h2>
|
||||
<div>
|
||||
<canvas id="received-graph" width="800" height="150"></canvas><br>
|
||||
<canvas id="received-channel-graph" width="800" height="300"></canvas><br>
|
||||
<label>
|
||||
@@ -85,80 +94,131 @@
|
||||
</label><br>
|
||||
Max Segments Displayed: <input id="max-bits-displayed-on-graph" type="number" min="1" max="2000"><br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h2>Packetization Configuration</h2>
|
||||
<div>
|
||||
Maximum Data: <span id="packetization-max-bytes"></span><br>
|
||||
Maximum Packets: <span id="packetization-max-packets"></span><br>
|
||||
Maximum Duration: <span id="packetization-max-duration"></span><br>
|
||||
<h4>Speed</h4>
|
||||
Packetization: <span id="packetization-speed-bits-per-second"></span><br>
|
||||
Data Only: <span id="packetization-speed-effective-bits-per-second"></span><br>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Packet Configuration</h2>
|
||||
<div>
|
||||
Bytes per packet: <span id="bytes-per-packet"></span><br>
|
||||
Bits per Packet: <span id="bits-per-packet"></span><br>
|
||||
<h4>Encoding</h4>
|
||||
Packet Encoding: <span id="packet-encoding"></span><br>
|
||||
Bits per block: <span id="packet-encoding-bits-per-block"></span><br>
|
||||
Blocks per packet: <span id="packet-encoding-block-count"></span><br>
|
||||
Encoding bits per Packet: <span id="packet-encoding-bit-count"></span><br>
|
||||
<h4>Utilization</h4>
|
||||
Data Bits per Packet: <span id="packet-data-bit-count"></span><br>
|
||||
Unused bits per packet: <span id="packet-unused-bit-count"></span><br>
|
||||
<h4>Segments</h4>
|
||||
Segments Per Packet: <span id="segments-per-packet"></span><br>
|
||||
Bits per segment: <span id="bits-per-segment">N/A</span><br />
|
||||
Last segment unused bits: <span id="last-segment-unused-bit-count"></span><br>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Data</h2>
|
||||
<div>
|
||||
Original Bytes: <span id="original-byte-count"></span><br>
|
||||
Packetization Bytes: <span id="packetization-byte-count"></span><br>
|
||||
Packetization Bits: <span id="packetization-bit-count"></span><br>
|
||||
Packet Count: <span id="packet-count"></span><br>
|
||||
Total Segments: <span id="total-segments"></span><br>
|
||||
<h3>Transfer Time</h3>
|
||||
Per Segment: <span id="segment-transfer-duration"></span><br>
|
||||
Per Packet: <span id="packet-transfer-duration"></span><br>
|
||||
Total: <span id="data-transfer-duration"></span><br>
|
||||
<h3>Utilization</h3>
|
||||
Unused bits in last packet: <span id="last-packet-unused-bit-count"></span><br>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Original Data to send</h2>
|
||||
<div>
|
||||
<div class="raw-data" id="sent-data"></div><br />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Encoded packets to send</h2>
|
||||
<div>
|
||||
<div class="raw-data" id="error-correcting-data"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Encoded segments to send</h2>
|
||||
<div>
|
||||
<div class="raw-data" id="actual-bits-to-send"></div><br />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Decoded</h2>
|
||||
<div>
|
||||
Bytes: <span id="received-packet-original-bytes">N/A</span><br>
|
||||
Length CRC-8: <span id="received-packet-original-bytes-crc">N/A</span><br>
|
||||
<h4>Errors</h4>
|
||||
Encoded Segments: <span id="received-raw-bits-error-percent">N/A</span>%<br>
|
||||
Encoded Packets: <span id="received-encoded-bits-error-percent">N/A</span>%<br>
|
||||
Decoded Packets: <span id="received-decoded-bits-error-percent">N/A</span>%<br />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Encoded Segments Received</h2>
|
||||
<div>
|
||||
<div class="raw-data" id="received-encoded-segment-bits"></div><br />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Encoded Packets Received</h2>
|
||||
<div>
|
||||
<div class="raw-data" id="received-encoded-bits"></div><br />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Decoded Packets Received</h2>
|
||||
<div>
|
||||
<div class="raw-data" id="received-decoded-bits"></div><br />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Selected</h2>
|
||||
<div>
|
||||
Channel: <span id="selected-channel">N/A</span><br>
|
||||
Segment: <span id="selected-segment">N/A</span><br>
|
||||
Bit: <span id="selected-bit">N/A</span><br>
|
||||
<div id="selected-samples"></div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Configuration</h2>
|
||||
Wave Form: <select id="wave-form" value="sine">
|
||||
<option value="sine">Sine Wave</option>
|
||||
<option value="square">Square Wave</option>
|
||||
<option value="sawtooth">Sawtooth Wave</option>
|
||||
<option value="triangle">Triangle Wave</option>
|
||||
</select><br>
|
||||
Packet Size:
|
||||
2^<input id="packet-size-power" type="number" min="0" max="16">
|
||||
<span id="packet-size"></span>
|
||||
<br>
|
||||
Segment Duration: <input id="bit-duration-text" type="number" min="0" max="1000" value="190">ms<br>
|
||||
Amplitude Threshold: <input id="amplitude-threshold-text" type="number" min="0" max="100" value="75"><br>
|
||||
Minimum Frequency: <input id="minimum-frequency" type="number" min="20" max="20000" value="900"><br>
|
||||
Maximum Frequency: <input id="maximum-frequency" type="number" min="20" max="20000" value="1200"><br>
|
||||
Last Segment Percent: <input id="last-bit-percent" type="number" min="0" max="100" value="90">%<br>
|
||||
FFT Size: 2^<input id="fft-size-power-text" type="number" min="5" max="15" value="90"><br>
|
||||
Frequency Resolution Multiplier: <input id="frequency-resolution-multiplier" type="number" min="1" max="20"
|
||||
value="2"><br>
|
||||
Channel Frequency Resolution Padding: <input id="channel-frequency-resolution-padding" type="number" min="0" max="20"><br>
|
||||
Smoothing Time Constant: <input id="smoothing-time-constant-text" type="number" min="0.00" max="1.00" step="0.01"
|
||||
value="0.00"><br>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Data Integrity</h2>
|
||||
<label>
|
||||
<input type="checkbox" id="periodic-interleaving" checked>Periodic Interleaving
|
||||
</label><br />
|
||||
<label>
|
||||
<input type="checkbox" id="error-correction-hamming" checked>Hamming Code Error Correction
|
||||
</label><br>
|
||||
</div>
|
||||
<h2>Audio Spectrum</h2>
|
||||
<div>
|
||||
<h2>Packet</h2>
|
||||
Data Bits: <span id="data-bits-to-send">N/A</span> Bytes: <span id="data-bytes-to-send">N/A</span><br />
|
||||
Data Size Header Bits: <span id="data-size-header-bits">N/A</span><br />
|
||||
Error Correction Bits: <span id="error-correction-bits">N/A</span><br />
|
||||
Total Bits: <span id="total-bits-to-send">N/A</span> Bytes: <span id="total-bytes-to-send">N/A</span><br />
|
||||
Channels: <span id="packet-send-channel-count">N/A</span><br />
|
||||
Segment Count: <span id="packet-send-segment-count">N/A</span><br />
|
||||
Segment Duration: <span id="packet-send-segment-duration">N/A</span>s<br />
|
||||
Total Duration: <span id="duration-to-send">N/A</span>s<br />
|
||||
</div>
|
||||
<div>
|
||||
<h2>Information</h2>
|
||||
Frequency Resolution: <span id="frequency-resolution">N/A</span><br>
|
||||
Frequency Count: <span id="frequency-count">N/A</span><br>
|
||||
Samples Per Bit: <span id="samples-per-bit">0</span><br>
|
||||
Sample Rate: <span id="audio-context-sample-rate">N/A</span> per second.<br />
|
||||
Segments per second: <span id="durations-per-second">N/A</span><br />
|
||||
Bits per segment: <span id="bits-per-duration">N/A</span><br />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Speed</h2>
|
||||
Baud: <span id="data-transfer-speed-bits-per-second">N/A</span><br>
|
||||
Bytes/s: <span id="data-transfer-speed-bytes-per-second">N/A</span><br />
|
||||
Effective Baud: <span id="effective-speed-bits-per-second">N/A</span><br>
|
||||
Effective Bytes/s: <span id="effective-speed-bytes-per-second">N/A</span><br />
|
||||
<h2>Receiving</h2>
|
||||
<div>
|
||||
Samples Per Bit: <span id="samples-per-bit">0</span><br>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Channels</h2>
|
||||
<div class="panels">
|
||||
<ol id="channel-list" start="0"></ol>
|
||||
<canvas id="channel-frequency-graph" width="200" height="200"></canvas>
|
||||
<h2>Channel Tuning</h2>
|
||||
<div>
|
||||
<canvas id="channel-frequency-graph" width="160" height="160"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
|
||||
55
style.css
55
style.css
@@ -4,19 +4,53 @@ body {
|
||||
.panels {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.panels > div {
|
||||
border: 1px solid black;
|
||||
background-color: darkslategray;
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
padding-top: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
border-radius: 10px;
|
||||
color: rgb(75, 185, 75);
|
||||
color: rgb(163, 197, 163);
|
||||
max-height: 200px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.panels > div h2 {
|
||||
.panels > div.chart {
|
||||
max-height: unset;
|
||||
overflow: unset;
|
||||
}
|
||||
.panels > div.chart > div {
|
||||
overflow: unset;
|
||||
height: unset;
|
||||
}
|
||||
.panels > div > h2 {
|
||||
margin-top: 0;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
background-color: gray;
|
||||
border-bottom: 4px solid darkolivegreen;
|
||||
color: black;
|
||||
font-size: small;
|
||||
}
|
||||
.panels > div > div {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
font-size: small;
|
||||
height: 175px;
|
||||
|
||||
overflow: auto;
|
||||
}
|
||||
.panels > div > div h4 {
|
||||
font-size: small;
|
||||
background-color: gray;
|
||||
border-bottom: 1px solid rgb(58, 58, 58);
|
||||
color: rgb(58, 58, 58);
|
||||
margin-top: 3px;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
canvas {
|
||||
background-color: black;
|
||||
@@ -34,6 +68,7 @@ canvas {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
left: 0;
|
||||
width: 0;
|
||||
/* transition: width 0.3s ease; */
|
||||
}
|
||||
.xprogress-container::after {
|
||||
@@ -48,7 +83,17 @@ canvas {
|
||||
background-color: rgb(41, 59, 10);
|
||||
color: rgb(75, 185, 75);
|
||||
width: 250px;
|
||||
height: 300px;
|
||||
min-height: 150px;
|
||||
font-size: x-small;
|
||||
border: 1px solid grey;
|
||||
overflow: auto;
|
||||
font-family: monospace;
|
||||
}
|
||||
.raw-data-small {
|
||||
background-color: rgb(41, 59, 10);
|
||||
color: rgb(75, 185, 75);
|
||||
width: 250px;
|
||||
min-height: 100px;
|
||||
font-size: x-small;
|
||||
border: 1px solid grey;
|
||||
overflow: auto;
|
||||
@@ -68,8 +113,8 @@ canvas {
|
||||
color: green;
|
||||
}
|
||||
ol {
|
||||
overflow: auto;
|
||||
height: 75px;
|
||||
/* overflow: auto; */
|
||||
/* height: 75px; */
|
||||
background-color: rgb(41, 59, 10);
|
||||
color: rgb(75, 185, 75);
|
||||
border: 1px solid grey;
|
||||
|
||||
Reference in New Issue
Block a user