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');
}

ref. Manchester Coding Basics

  1. トップ
  2. tech
  3. Manchester Encoding を JS で

関連エントリー

▲ この日のエントリ