{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "da6e406c-cc91-40b7-b962-ed0f80e9d0a0",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "import itertools\n",
    "from pathlib import Path\n",
    "\n",
    "from matplotlib import pyplot as plt\n",
    "import numpy as np\n",
    "import scipy\n",
    "import scipy.signal as sig\n",
    "import RigolWFM.wfm as rigol\n",
    "from reedmuller.reedmuller import ReedMuller\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "3a4841cd-ed6e-49e2-995e-f7281c784744",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Processing 24002 total samples\n",
      "Processing 24002 total samples\n",
      "Processing 24002 total samples\n",
      "Processing 24002 total samples\n",
      "Processing 24002 total samples\n",
      "Processing 24002 total samples\n",
      "Processing 24002 total samples\n",
      "Processing 24002 total samples\n",
      "Processing 24002 total samples\n",
      "Processing 24002 total samples\n",
      "Processing 24002 total samples\n",
      "Processing 24002 total samples\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[<matplotlib.lines.Line2D at 0x6ffffa828f50>]"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 117340 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#wf = rigol.Wfm.from_file('barcode_test_varspeed.wfm', 'DS1054Z')\n",
    "# Initial test with waveforms/manchester/manchester_rm_1234567_04.wfm\n",
    "wfs = []\n",
    "for f in Path('waveforms/manchester/').glob('manchester_rm_*.wfm'):\n",
    "    wf = rigol.Wfm.from_file(str(f), 'DS1054Z')\n",
    "    data = wf.channels[0].volts\n",
    "    \n",
    "    downsampling = 500\n",
    "    data = scipy.signal.decimate(data, downsampling, ftype='fir')[::-1]\n",
    "    td = wf.channels[0].seconds_per_point * downsampling\n",
    "    times = np.linspace(0, len(data)*td, len(data))\n",
    "    \n",
    "    print(f'Processing {len(data)} total samples')\n",
    "    wfs.append((td, times, data))\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(28, 6))\n",
    "ax.plot(times, data)\n",
    "ax.set_xlim([times[0], times[-1]])\n",
    "\n",
    "deltas = data[:-1] - data[1:]\n",
    "ax.twinx().plot(times[:-1], deltas, color='red')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "4e86b18b-d17c-4cc9-a567-10e8777390b2",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 61016 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 100380 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 83660 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 249076 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 66372 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 112140 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 79648 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 87500 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 90312 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 67636 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 83532 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 80624 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def find_signal(td, times, data):\n",
    "    sos = sig.butter(1, 50, 'highpass', output='sos', fs=10e3)\n",
    "    jitter = np.abs(sig.sosfilt(sos, data))\n",
    "    \n",
    "    def sweep(ys, coeff=0.999):\n",
    "        out = np.zeros(ys.shape)\n",
    "        acc = 0\n",
    "        for i, y in enumerate(ys):\n",
    "            if y > acc:\n",
    "                acc = y\n",
    "            else:\n",
    "                acc *= coeff\n",
    "            out[i] = acc\n",
    "        return out\n",
    "    \n",
    "    loc = np.min(np.vstack([sweep(jitter), sweep(jitter[::-1])[::-1]]), axis=0)\n",
    "    \n",
    "    loc_th = 0.333\n",
    "    sel = (loc > loc_th).astype(int)\n",
    "    indices = np.squeeze(np.argwhere(sel[1:] - sel[:-1]))\n",
    "    if sel[0]:\n",
    "        indices = indices[1:]\n",
    "    \n",
    "    fig, ax = plt.subplots(figsize=(28, 6))\n",
    "    ax.plot(times, jitter)\n",
    "    ax.twinx().plot(times, loc, color='red')\n",
    "    ax.set_xlim([times[0], times[-1]])\n",
    "    \n",
    "    largest_interval = 0.02 # Min interval length\n",
    "    found_a, found_b = None, None\n",
    "    for a, b in zip(indices[0::2], indices[1::2]):\n",
    "        dt = times[b] - times[a]\n",
    "        if dt < 1.5 and dt > largest_interval:\n",
    "            largest_interval = dt\n",
    "            d_idx = b - a\n",
    "            guard = 0.05\n",
    "            found_a, found_b = round(a - d_idx*guard), round(b + d_idx*guard)\n",
    "    \n",
    "    ax.axvspan(times[found_a], times[found_b], color='orange', alpha=.2, zorder=-3)\n",
    "    for a, b in zip(indices[0::2], indices[1::2]):\n",
    "        dt = times[b] - times[a]\n",
    "        if dt < 1.5 and dt > largest_interval:\n",
    "            largest_interval = dt\n",
    "            d_idx = b - a\n",
    "            guard = 0.05\n",
    "            found_a, found_b = round(a - d_idx*guard), round(b + d_idx*guard)\n",
    "    \n",
    "    ax.axvspan(times[found_a], times[found_b], color='orange', alpha=.2, zorder=-3)\n",
    "    return found_a, found_b\n",
    "\n",
    "found_signals = [find_signal(*wf) for wf in wfs]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "47137733-4e22-4d79-b2ca-7c5ccd31285c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[(11048, 13972), (9629, 13752), (9343, 13659), (4075, 13049), (10516, 13525), (8839, 13655), (8990, 13824), (9234, 13488), (8442, 13697), (9657, 13884), (10409, 13918), (10417, 13926)]\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 78588 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 114964 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 100672 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 263428 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 80184 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 126952 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 96972 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 101752 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 106928 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 84944 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 100084 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 96752 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def find_signal(td, times, data):\n",
    "    sos = sig.butter(1, 50, 'highpass', output='sos', fs=10e3)\n",
    "    jitter = np.abs(sig.sosfilt(sos, data))\n",
    "    \n",
    "    def sweep(ys, coeff=0.999):\n",
    "        out = np.zeros(ys.shape)\n",
    "        acc = 0\n",
    "        for i, y in enumerate(ys):\n",
    "            if y > acc:\n",
    "                acc = y\n",
    "            else:\n",
    "                acc *= coeff\n",
    "            out[i] = acc\n",
    "        return out\n",
    "    \n",
    "    loc = sweep(jitter)\n",
    "    \n",
    "    loc_th = 0.333\n",
    "    sel = (loc > loc_th).astype(int)\n",
    "    indices = np.squeeze(np.argwhere(sel[1:] - sel[:-1]))\n",
    "    if sel[0]:\n",
    "        indices = indices[1:]\n",
    "    \n",
    "    fig, ax = plt.subplots(figsize=(28, 6))\n",
    "    ax.plot(times, jitter)\n",
    "    ax.twinx().plot(times, loc, color='red')\n",
    "    ax.set_xlim([times[0], times[-1]])\n",
    "    \n",
    "    largest_interval = 0.02 # Min interval length\n",
    "    found_a, found_b = None, None\n",
    "    for a, b in zip(indices[0::2], indices[1::2]):\n",
    "        dt = times[b] - times[a]\n",
    "        if dt < 1.5 and dt > largest_interval:\n",
    "            largest_interval = dt\n",
    "            d_idx = b - a\n",
    "            guard_left, guard_right = 0.05, -0.05\n",
    "            found_a, found_b = round(a - d_idx*guard_left), round(b + d_idx*guard_right)\n",
    "    \n",
    "    ax.axvspan(times[found_a], times[found_b], color='orange', alpha=.2, zorder=-3)\n",
    "    return found_a, found_b\n",
    "\n",
    "found_signals = [find_signal(*wf) for wf in wfs]\n",
    "print(found_signals)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "8895cf64-8beb-4e98-bb3c-27020bb84e35",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "adjusting edges Framing error\n",
      "101100110110110110011001101100110110110110011011011011001101100110110110011001100110011011\n",
      "\n",
      "adjusting edges Framing error\n",
      "101101100110011001011011011001101101101100110110110011011011011001100110110110011001100110011011\n",
      "\n",
      "adjusting edges Edges ok, found 64 manchester bits\n",
      "Resulting payload length: 32 bits\n",
      "Payload: 11011111011111111111111010010010\n",
      "Resuling data word: 2aaaaaa\n",
      "1010101001101010101001101010101010101010101010101001100101100101100101101\n",
      "\n",
      "adjusting edges Framing error\n",
      "101100110110110110011001101100110011011010010110110011001100110011011010101010101\n",
      "\n",
      "adjusting edges Framing error\n",
      "10101011011011001100110110011011011011001101101101100110110011011011011001100110011011\n",
      "\n",
      "adjusting edges Framing error\n",
      "101100110110110110011001101100110110110110010110110110010110011011011011001100110011011\n",
      "\n",
      "adjusting edges Edges ok, found 64 manchester bits\n",
      "Resulting payload length: 32 bits\n",
      "Payload: 10001101010111110001000100110011\n",
      "Resuling data word: 1111111\n",
      "1010100101011010011001100110101010100101011001010110010110100101101001101\n",
      "\n",
      "adjusting edges Framing error\n",
      "1011001101101101101101011010110110110011011011011001101100110110011001100110011001101\n",
      "\n",
      "adjusting edges Edges ok, found 64 manchester bits\n",
      "Resulting payload length: 32 bits\n",
      "Payload: 01010100100000000010101001101000\n",
      "Resuling data word: 3fff000\n",
      "1010011001100110010110010101010101010101100110011001011010011001010101101\n",
      "\n",
      "adjusting edges Edges ok, found 64 manchester bits\n",
      "Resulting payload length: 32 bits\n",
      "Payload: 00011110111010000111101111011000\n",
      "Resuling data word: 0cccccc\n",
      "1010010101101010100110101001100101010110101010011010101001101001010101101\n",
      "\n",
      "adjusting edges Framing error\n",
      "10110110011001010101101101100110110011010101100110110011001100110011001011001100101101\n",
      "\n",
      "adjusting edges Edges ok, found 64 manchester bits\n",
      "Resulting payload length: 32 bits\n",
      "Payload: 00001011110101101100010010001111\n",
      "Resuling data word: 30f0f0f\n",
      "1010010101011001101010100110011010011010010101100101100101011010101001101\n",
      "\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 188748 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 206764 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 218224 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 197144 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 159676 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 199308 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 203328 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 195552 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 209956 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 213864 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 204020 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 202016 characters]",
      "text/plain": [
       "<Figure size 2800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def decode_bits(ax, data, times, found):\n",
    "    found_a, found_b = found\n",
    "    d, t = data[found_a:found_b], times[found_a:found_b]\n",
    "    ax.plot(t, d)\n",
    "    ax.set_xlim([t[0], t[-1]])\n",
    "\n",
    "    subslice = d[len(d)//2 - len(d)//6:len(d)//2 + len(d)//6]\n",
    "    low, high = subslice.min(), subslice.max()\n",
    "    amp = high - low\n",
    "    # use slightly more than one half to account for the fact that we don't quite go dark during narrow black lines due to our detector's slit width\n",
    "    th = low + amp*0.5\n",
    "    ax.axhline(low, color='red', alpha=0.3, zorder=-3)\n",
    "    ax.axhline(high, color='green', alpha=0.3, zorder=-3)\n",
    "    ax.axhline(th, color='gray', alpha=0.3, zorder=-3)\n",
    "\n",
    "    bits = (d > th).astype(int)\n",
    "    edges = np.argwhere(bits[1:] - bits[:-1])[:,0]\n",
    "\n",
    "    print('adjusting edges', end=' ')\n",
    "    new_edges = np.copy(edges)\n",
    "    # for i, (left, mid, right) in enumerate(zip(edges[0:], edges[1:], edges[2:])):\n",
    "    #     new_value = i%2\n",
    "\n",
    "    #     if new_value:\n",
    "    #         min_v, max_v = d[left:mid].min(), d[mid:right].max()\n",
    "    #     else:\n",
    "    #         max_v, min_v = d[left:mid].max(), d[mid:right].min()\n",
    "\n",
    "    #     local_th = min_v + (max_v - min_v)/2\n",
    "    #     ax.plot(t[mid], local_th, marker='+', color='gray')\n",
    "    #     local_v = d[mid] > local_th\n",
    "    #     if local_v and new_value:\n",
    "    #         for j in range(mid, left, -1):\n",
    "    #             if d[j] <= local_th:\n",
    "    #                 break\n",
    "    #     elif local_v and not new_value:\n",
    "    #         for j in range(mid, right):\n",
    "    #             if d[j] <= local_th:\n",
    "    #                 break\n",
    "    #     elif not local_v and new_value:\n",
    "    #         for j in range(mid, right):\n",
    "    #             if d[j] >= local_th:\n",
    "    #                 break\n",
    "    #     elif not local_v and not new_value:\n",
    "    #         for j in range(mid, left, -1):\n",
    "    #             if d[j] >= local_th:\n",
    "    #                 break\n",
    "    #     print(j-new_edges[i+1], end=' ')\n",
    "    #     new_edges[i+1] = j\n",
    "\n",
    "    edges = new_edges\n",
    "    \n",
    "    for i, x in enumerate(edges):\n",
    "        new_value = not i%2\n",
    "        ax.axvline(t[x], color=('green' if new_value else 'red'), alpha=0.3, zorder=-5)\n",
    "\n",
    "    intervals = edges[1:] - edges[:-1]\n",
    "\n",
    "    mean_ref = intervals[len(intervals)//2 - len(intervals)//6:len(intervals)//2 + len(intervals)//6].mean()\n",
    "    \n",
    "    for i, x in enumerate(intervals):\n",
    "        if 0.1 < x/mean_ref < 10:\n",
    "            break\n",
    "    intervals = intervals[i:]\n",
    "    edges = edges[i:]\n",
    "\n",
    "    for i, x in enumerate(intervals[::-1]):\n",
    "        if 0.1 < x/mean_ref < 10:\n",
    "            break\n",
    "    if i>0:\n",
    "        intervals = intervals[:-i]\n",
    "        edges = edges[:-i]\n",
    "\n",
    "    ax.set_xlim([t[edges[0]-10], t[edges[-1]+10]])\n",
    "\n",
    "    last_val = 1\n",
    "    bits = [1]\n",
    "    text = lambda i, x, bit: ax.text(t[round(edges[i] + x)], th, str(bit), verticalalignment='center', horizontalalignment='center')\n",
    "    text(0, intervals[0]/2, 1)\n",
    "    for i, (ivl_left, ivl_right) in enumerate(zip(intervals[:-1], intervals[1:])):\n",
    "        bit_right = i%2\n",
    "        if last_val == 1:\n",
    "            if ivl_right / ivl_left > 1.5:\n",
    "                last_val = 2\n",
    "                bits.append(bit_right)\n",
    "                bits.append(bit_right)\n",
    "                text(i+1, ivl_right/3, bit_right)\n",
    "                text(i+1, ivl_right/3*2, bit_right)\n",
    "            else:\n",
    "                last_val = 1\n",
    "                bits.append(bit_right)\n",
    "                text(i+1, ivl_right/2, bit_right)\n",
    "        else: # last_val == 2\n",
    "            if ivl_left / ivl_right > 1.5:\n",
    "                last_val = 1\n",
    "                bits.append(bit_right)\n",
    "                text(i+1, ivl_right/2, bit_right)\n",
    "            else:\n",
    "                last_val = 2\n",
    "                bits.append(bit_right)\n",
    "                bits.append(bit_right)\n",
    "                text(i+1, ivl_right/3, bit_right)\n",
    "                text(i+1, ivl_right/3*2, bit_right)\n",
    "    bits = np.array(bits)\n",
    "    bits_out = np.copy(bits)\n",
    "\n",
    "    if (bits[:4] == [1, 0, 1, 0]).all() and (bits[-5:] == [0, 1, 1, 0, 1]).all() and len(bits) == (64 + 4 + 5):\n",
    "        bits_rm = bits[4:-5]\n",
    "        print(f'Edges ok, found {len(bits_rm)} manchester bits')\n",
    "        db = bits_rm[0::2] - bits_rm[1::2]\n",
    "        err = np.argwhere(db == 0)\n",
    "        if err.any():\n",
    "            print(f'Manchester errors at {indices[:,0]}')\n",
    "        bits = (db + 1)//2\n",
    "        print(f'Resulting payload length: {len(bits)} bits')\n",
    "        print('Payload:', ''.join(map(str, bits)))\n",
    "\n",
    "        rm = ReedMuller(3, 5)\n",
    "        #bits = [int(bool(0x1234567&(2**i))) for i in range(26)]\n",
    "        #bits = rm.encode(bits)\n",
    "        decoded = rm.decode(bits)\n",
    "        d_out = sum(2**i for i, x in enumerate(decoded) if x)\n",
    "        print(f'Resuling data word: {d_out:07x}')\n",
    "    else:\n",
    "        print('Framing error')\n",
    "        ax.set_title('Framing error')\n",
    "\n",
    "    return bits_out\n",
    "\n",
    "found_bits = []\n",
    "for (td, times, data), found in zip(wfs, found_signals):\n",
    "    fig, ax = plt.subplots(figsize=(28, 6))\n",
    "    bits = decode_bits(ax, data, times, found)\n",
    "    bits_s = ''.join(map(str, bits))\n",
    "    print(bits_s)\n",
    "    print()\n",
    "    found_bits.append(bits)\n",
    "#bits_ref = '1010010101011001100101100101101010010110101001011001101010100110011001101'\n",
    "#print(bits_ref)\n",
    "#print(''.join('.' if x == ref else '#' for x, ref in zip(bits_s, bits_ref)))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "72b817ee-16cb-4897-a6df-8f10a6f6fb81",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Found 116 transitions\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 270872 characters]",
      "text/plain": [
       "<Figure size 2800x1600 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "th = 0.5\n",
    "\n",
    "outval = []\n",
    "vrefout = []\n",
    "vthout = []\n",
    "start = 0\n",
    "val = False\n",
    "timestamps = []\n",
    "vref = data[0] + th\n",
    "mins, maxs = [], []\n",
    "for i, v in enumerate(data):\n",
    "    if abs(vref - v) > th and ((v > vref and not val) or (v < vref and val)):\n",
    "            for j in range(i-1, start, -1):\n",
    "                d = data[j] - vref\n",
    "                if val and d < -th or not val and d > th:\n",
    "                    timestamps.append((j+1, val))\n",
    "                    break\n",
    "            timestamps.append((i, None))\n",
    "            start = i\n",
    "\n",
    "            if val:\n",
    "                maxs.append(vref)\n",
    "            else:\n",
    "                mins.append(vref)\n",
    "                \n",
    "            vref = (v - .9*th) if val else (v + .9*th)\n",
    "            val = not val\n",
    "    elif val and v > vref:\n",
    "        vref = v\n",
    "    elif not val and v < vref:\n",
    "        vref = v\n",
    "    outval.append(val)\n",
    "    vrefout.append(vref)\n",
    "    vthout.append((vref - th) if val else (vref + th))\n",
    "\n",
    "timestamps = timestamps[1:-1]\n",
    "print(f'Found {len(timestamps)} transitions')\n",
    "#for ts, val in timestamps:\n",
    "#    print(ts, val)\n",
    "\n",
    "fig, axs = plt.subplots(3, 1, figsize=(28, 16), sharex=True)\n",
    "ax = axs[0]\n",
    "ax.twinx().plot(times, np.array(outval).astype(int), color='red', alpha=0.3)\n",
    "ax.plot(times, vrefout)\n",
    "ax.plot(times, vthout)\n",
    "ax.plot(times, data)\n",
    "ax.grid()\n",
    "\n",
    "ax = axs[1]\n",
    "for (prev_ts, prev_val), (next_ts, next_val) in zip(timestamps[:-1], timestamps[1:]):\n",
    "    if prev_val is not None:\n",
    "        col = 'green' if prev_val else 'red'\n",
    "        ax.axvspan(times[prev_ts], times[(next_ts-1)], color=col, alpha=0.5)\n",
    "ax.plot(times, data)\n",
    "ax.set_xlim([times[200], times[-200]])\n",
    "ax.grid()\n",
    "ax = axs[2]\n",
    "slopes = []\n",
    "slope_times = []\n",
    "bar_widths = []\n",
    "bar_vals = []\n",
    "slope_widths = []\n",
    "for (prev_ts, prev_val), (next_ts, next_val) in zip(timestamps[:-1], timestamps[1:]):\n",
    "    #print(prev_ts, prev_val, next_ts, next_val)\n",
    "    td = next_ts - prev_ts\n",
    "        \n",
    "    if prev_val is None:\n",
    "        vd = abs(data[next_ts] - data[prev_ts])\n",
    "        slope_times.append((times[prev_ts] + times[next_ts])/2)\n",
    "        if td > 0:\n",
    "            slopes.append(vd/td)\n",
    "            #slopes.append(1/td)\n",
    "        else:\n",
    "            slopes.append(abs(data[prev_ts-1] - data[prev_ts+1])/2)\n",
    "        slope_widths.append(td)\n",
    "        \n",
    "    else:\n",
    "        bar_widths.append(td)\n",
    "        bar_vals.append(prev_val)\n",
    "\n",
    "slope_widths[-1] = slope_widths[-2] # compensate for last slope usually being way long\n",
    "ax.plot(slope_times, slopes)\n",
    "ax.grid()\n",
    "#ax.set_xlim([slope_times[0], slope_times[-1]])\n",
    "#ax.set_xlim([slope_times[0] - .02, slope_times[-1] + .02])\n",
    "#ax.set_xlim([times_sel[-1300], times_sel[-700]])\n",
    "#ax.set_xlim([slope_times[0], slope_times[0]+(slope_times[1]-slope_times[0])*5])\n",
    "#ax.set_xlim([0.05, 0.15])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
