WebAudio での通信用に使おうと思って書いていたけど、やる気が失せてしまった。WebAudio 非依存部分だけ習作的に書いた。思ったよりややこしいデコード方法になることがわかった。
プリアンブルとして 1 (01) を n 回連続して送信したあと、0 (10) を送信してクロック同期をとり、任意長のビットをデコードするかたち。(イーサネットのプリアンブルとは互換性なし)
ManchesterEncoding = function () { this.init.apply(this, arguments) };
ManchesterEncoding.prototype = {
/**
* @constructor
*/
init : function (opts) {
var self = this;
self.clock = opts.clock;
self.preamble = opts.preamble || 8;
},
/**
* @param {Array|ByteArray|string} bytes
*/
encode : function (bytes) {
var self = this;
var preamble = self.preamble;
var clock = self.clock;
if (typeof bytes === 'string') {
var tmp = [];
for (var i = 0, len = bytes.length; i < len; i++) {
tmp.push(bytes.charCodeAt(i));
}
bytes = tmp;
}
var data = [];
var current = 0;
function sendBit(bit) {
// Send 1 as 01 (_-)
// 0 as 10 (-_)
for (var i = 0; i < clock; i++) {
data[current++] = bit ? -1 : 1;
}
for (var i = 0; i < clock; i++) {
data[current++] = bit ? 1 : -1;
}
}
// preamble: send repeated 1
for (var i = 0; i < preamble; i++) {
sendBit(1);
}
// start bit: after repeated 1 sync with logic 0
sendBit(0);
for (var i = 0, len = bytes.length; i < len; i++) {
var byte = bytes[i];
for (var b = 0; b < 8; b++) {
// msb first
if (byte & (1<<(7-b))) {
sendBit(1, 1);
} else {
sendBit(0, 1);
}
}
}
return data;
},
/**
* @param {Function} callback
* @return {{ reset: function(), decode: function(Array|ByteArray) }}
*/
decoder : function (callback) {
var self = this;
var logic = true, count = 0, clock = self.clock;
var sync = false, syncAvg = 0, syncCount = 0;
var byte = 0, bitCount = 0, bit;
var short = 0, long = 0;
var state = 'start';
return {
reset : function () {
// reset and re-wait for preamble
this.decode = self.decoder(callback).decode;
},
decode : function (data) {
for (var i = 0, len = data.length; i < len; i++) {
var current =
data[i] < -0.5 ? false :
data[i] > 0.5 ? true :
current;
var logicChanged = logic !== current;
if (logicChanged) {
// clock adjustment
if (clock * 0.5 <= count && count <= clock * 1.5) {
syncAvg += count;
syncCount++;
clock = syncAvg / syncCount;
} else
if (clock * 1.5 <= count && count <= clock * 2.5) {
syncAvg += count / 2;
syncCount++;
clock = syncAvg / syncCount;
} else {
// ERROR
clock = self.clock;
sync = false;
syncAvg = 0;
syncCount = 0;
}
logic = !logic;
if (!sync) {
// surely synchronized with preamble clock
// and detect transition to logic zero
// ~_-_-_-_-_--_
if (syncCount >= self.preamble && clock * 1.5 < count) {
sync = true;
bit = logic;
}
} else {
if (count <= clock) {
short++;
} else {
long++;
}
if (long === 1) {
long = 0;
bit = !bit;
if (bit) {
byte = byte << 1 | 1;
} else {
byte = byte << 1;
}
bitCount++;
} else
if (short == 2) {
short = 0;
if (bit) {
byte = byte << 1 | 1;
} else {
byte = byte << 1;
}
bitCount++;
}
if (bitCount == 8) {
callback(byte);
byte = 0;
bitCount = 0;
}
}
count = 0;
}
count++;
}
}
};
}
};
for (var clock = 1; clock < 10; clock++) {
var code = new ManchesterEncoding({ clock: clock });
var data = code.encode([1, 0, 24]);
var result = '';
var decoder = code.decoder(function (byte) {
// console.log([byte, String.fromCharCode(byte)]);
result += String.fromCharCode(byte);
});
var data = code.encode("Hello, World");
var noise = [];
for (var i = 0; i < 100; i++) noise.push(Math.random() < 0.5 ? 1 : 0);
decoder.decode(noise.concat(data));
console.log(result === 'Hello, World');
}