1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| """CodeShell.kr - Crossfeed: demodulate the L-R phase carrier.
The payload rides in the channel difference L-R as a 1375 Hz carrier. The choice of 1375 Hz matters: 1375 Hz x 640/16000 samples = exactly 55 cycles per bit, so a 640-sample window correlation gives the same answer for every window phase and the demodulation needs no bit-timing search at all.
Three passes are transmitted, each 312 bits: 24-bit preamble A5A5A5, then 256 bits (32 ASCII hex characters), then a CRC32 over those characters. The CRC32 is what makes the recovered 32-character seed trustworthy, so it is checked before the seed is used. """ import zlib
import numpy as np import wave
WAV = "crossfeed.wav" SPB = 640 CARRIER = 1375.0 PRE = 24 HEX = 256 CRC = 32 PASS = PRE + HEX + CRC FIRST = 8
def load(path=WAV): w = wave.open(path) nch, fs, n = w.getnchannels(), w.getframerate(), w.getnframes() dtype = {2: "<i2", 4: "<i4"}[w.getsampwidth()] d = np.frombuffer(w.readframes(n), dtype=dtype).reshape(-1, nch).astype(np.float64) return d[:, 0] - d[:, 1], fs
def demod(x, fs): """One bit per SPB-sample window: sign of the window's complex sum.""" idx = np.arange(len(x)) y = x * np.exp(-2j * np.pi * CARRIER * idx / fs) csum = np.concatenate([[0], np.cumsum(y)]) nbits = len(x) // SPB starts = SPB * np.arange(nbits) ends = starts + SPB return ((csum[ends] - csum[starts]).real > 0).astype(np.uint8)
def to_bytes(bits): return bytes(int("".join(map(str, bits[i:i + 8])), 2) for i in range(0, len(bits) - 7, 8))
def main(): x, fs = load() bits = demod(x, fs) pre = np.array([int(c) for c in "".join(f"{v:08b}" for v in b"\xa5\xa5\xa5")], dtype=np.uint8)
for pol in (0, 1): b = bits if pol == 0 else 1 - bits for start in range(0, SPB): if np.array_equal(b[start:start + PRE], pre): print(f"preamble at bit {start}, polarity {'normal' if not pol else 'inverted'}") break else: continue break else: raise SystemExit("no preamble found")
passes = [] for p in range(3): s = start + p * PASS seg = b[s:s + PASS] hx = to_bytes(seg[PRE:PRE + HEX]).decode("ascii", errors="replace") crc = int.from_bytes(to_bytes(seg[PRE + HEX:PASS]), "big") print(f"pass{p}: hex={hx!r} crc={crc:08x}") passes.append(seg[PRE:PRE + HEX].astype(int))
maj = np.where(sum(passes) >= 2, 1, 0).astype(np.uint8) print("bitwise disagreements:", int(sum((passes[0] != passes[1]) | (passes[0] != passes[2])))) seed_hex = to_bytes(maj).decode("ascii") print("majority hex:", seed_hex) crc = int.from_bytes(to_bytes(b[start + 2 * PASS + PRE + HEX: start + 2 * PASS + PASS]), "big") calc = zlib.crc32(seed_hex.encode()) & 0xFFFFFFFF print(f"crc32(seed) = {calc:08x} transmitted = {crc:08x} " f"{'OK' if crc == calc else 'BAD'}") return seed_hex
if __name__ == "__main__": main()
|