{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ff84bf2f-1be5-4d65-9914-c09fe8308d7b",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\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",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 104,
   "id": "3951cf00-6c4b-4749-9d35-444a9f94cc49",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "16384"
      ]
     },
     "execution_count": 104,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 43908 characters]\n",
      "text/plain": [
       "<Figure size 2016x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "#wf = rigol.Wfm.from_file('barcode_test_varspeed.wfm', 'DS1054Z')\n",
    "wf = rigol.Wfm.from_file('barcode_test8.wfm', 'DS1054Z')\n",
    "data = wf.channels[0].volts\n",
    "\n",
    "downsampling = 500\n",
    "data = scipy.signal.decimate(data, downsampling, ftype='fir')\n",
    "off = 6000#wf = rigol.Wfm.from_file('barcode_test_varspeed.wfm', 'DS1054Z')\n",
    "wf = rigol.Wfm.from_file('barcode_test8.wfm', 'DS1054Z')\n",
    "data = wf.channels[0].volts\n",
    "\n",
    "downsampling = 500\n",
    "data = scipy.signal.decimate(data, downsampling, ftype='fir')\n",
    "off = 6000\n",
    "data = data[off:off+16384]\n",
    "td = wf.channels[0].seconds_per_point * downsampling\n",
    "times = np.linspace(0, len(data)*td, len(data))\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(28, 6))\n",
    "ax.plot(times, data)\n",
    "ax.set_xlim([times[0], times[-1]])\n",
    "len(data)\n",
    "data = data[off:off+16384]\n",
    "td = wf.channels[0].seconds_per_point * downsampling\n",
    "times = np.linspace(0, len(data)*td, len(data))\n",
    "\n",
    "fig, ax = plt.subplots(figsize=(28, 6))\n",
    "ax.plot(times, data)\n",
    "ax.set_xlim([times[0], times[-1]])\n",
    "len(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 105,
   "id": "5b1bde6d-59c1-4ca6-ab06-dd150a6d719b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 43908 characters]\n",
      "text/plain": [
       "<Figure size 2016x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots(figsize=(28, 6))\n",
    "ax.plot(times, data)\n",
    "ax.set_xlim([times[0], times[-1]])\n",
    "\n",
    "state = '?'\n",
    "last = data[0]\n",
    "for t, v in zip(times, data):\n",
    "    pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "id": "974be198-2a18-49dc-b0cf-71216748a924",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 203476 characters]\n",
      "text/plain": [
       "<Figure size 2016x1296 with 27 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, axs = plt.subplots(14, 1, figsize=(28, 18), sharex=True)\n",
    "axs[0].plot(times, data)\n",
    "axs[0].set_xlim([times[0], times[-1]])\n",
    "\n",
    "times_rem = times\n",
    "meanl, varl = data, np.zeros(len(data))\n",
    "while len(meanl) > 2:\n",
    "    meann = (meanl[0::2] + meanl[1::2]) / 2\n",
    "    varn  = ( varl[0::2] +  varl[1::2]) / 2 + ((meanl[0::2] - meann)**2 + (meanl[1::2] - meann)**2)/2\n",
    "    meanl, varl = meann, varn\n",
    "    times_rem = (times_rem[0::2] + times_rem[1::2])/2\n",
    "    axs[1].plot(times_rem, meanl)\n",
    "    tx = axs[1].twinx()\n",
    "    tx.plot(times_rem, varl, color='orange')\n",
    "    tx.set_ylim([0, 0.1])\n",
    "    axs = axs[1:]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 119,
   "id": "0f0832ae-fcfd-40f2-893b-9543c5a2e432",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0.0, 0.15)"
      ]
     },
     "execution_count": 119,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 59720 characters]\n",
      "text/plain": [
       "<Figure size 2016x432 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "DECIMATION_ORDER = 7\n",
    "\n",
    "fig, ax = plt.subplots(1, 1, figsize=(28, 6), sharex=True)\n",
    "ax.plot(times, data)\n",
    "ax.set_xlim([times[0], times[-1]])\n",
    "\n",
    "times_rem = times\n",
    "meanl, varl = data, np.zeros(len(data))\n",
    "for _ in range(DECIMATION_ORDER):\n",
    "    meann = (meanl[0::2] + meanl[1::2]) / 2\n",
    "    varn  = ( varl[0::2] +  varl[1::2]) / 2 + ((meanl[0::2] - meann)**2 + (meanl[1::2] - meann)**2)/2\n",
    "    meanl, varl = meann, varn\n",
    "    times_rem = (times_rem[0::2] + times_rem[1::2])/2\n",
    "    \n",
    "ax.plot(times_rem, meanl)\n",
    "tx = ax.twinx()\n",
    "tx.plot(times_rem, varl, color='red')\n",
    "tx.set_ylim([0, 0.15])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 120,
   "id": "d60a27ab-35d6-4014-be81-764d75d2ff23",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Runs:\n",
      "power=   0.0354  39-39  (len 1) (109824.000s-109824.000)\n",
      "power=     1.14  41-46  (len 6) (115456.000s-129536.000)\n",
      "(16384,) (16384,)\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(0.25601563167612784, 0.30716875495829166)"
      ]
     },
     "execution_count": 120,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 119676 characters]\n",
      "text/plain": [
       "<Figure size 2016x432 with 2 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "VARL_THRESH = 0.02\n",
    "\n",
    "runs = [] # list of tuples of (start, end) indices, start inclusive, end exclusive (like in python array slicing)\n",
    "start = None # inclusive index\n",
    "\n",
    "for i, v in enumerate(varl > VARL_THRESH):\n",
    "    if v:\n",
    "        if start is None:\n",
    "            start = i\n",
    "    else:\n",
    "        if start is not None:\n",
    "            end = i\n",
    "            power = math.sqrt(np.mean(varl[start:end]**2)) * (end-start)\n",
    "            runs.append((power, start, end))\n",
    "            start = None\n",
    "\n",
    "runs = sorted(runs)\n",
    "dec_width = 2**DECIMATION_ORDER\n",
    "print('Runs:')\n",
    "for power, start, end in runs:\n",
    "    start_s = start*dec_width*td\n",
    "    end_s   =   (end-1)*dec_width*td\n",
    "    print(f'power={power:9.3g} {start:>3d}-{end-1:<3d} (len {end-start}) ({start_s:5.3f}s-{end_s:5.3f})')\n",
    "    \n",
    "power, start, end = runs[-1]\n",
    "power /= end-start\n",
    "start, end = start-1, end+1 # Add some guard zone to both indices to allow for misalignment / aliasing during variance downsampling\n",
    "start_sp, end_sp = start*dec_width, end*dec_width\n",
    "print(data.shape, times.shape)\n",
    "data_sel = data[start_sp:end_sp]\n",
    "times_sel = times[start_sp:end_sp]\n",
    "\n",
    "fig, ax = plt.subplots(1, 1, figsize=(28, 6), sharex=True)\n",
    "ax.plot(times_sel, data_sel)\n",
    "ax.plot(times_rem, meanl)\n",
    "tx = ax.twinx()\n",
    "tx.plot(times_rem, varl, color='red')\n",
    "ax.set_xlim([times_sel[0], times_sel[-1]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 121,
   "id": "3a00d89b-e37a-4be7-936d-60152b536df1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0.25601563167612784, 0.30716875495829166)"
      ]
     },
     "execution_count": 121,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 125760 characters]\n",
      "text/plain": [
       "<Figure size 2016x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots(1, 1, figsize=(28, 6), sharex=True)\n",
    "ax.plot((times_sel[:-1] + times_sel[1:])/2, (data_sel[1:] - data_sel[:-1]))\n",
    "ax.set_xlim([times_sel[0], times_sel[-1]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 122,
   "id": "b4506ffd-fb42-4fa7-a836-c907778d8ae7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "standard deviation = 0.43675554934930105\n",
      "Found 123 transitions\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "(0.23854078585574592, 0.3161430817581772)"
      ]
     },
     "execution_count": 122,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 289372 characters]\n",
      "text/plain": [
       "<Figure size 2016x1152 with 4 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "std = np.sqrt(power)\n",
    "print(f'standard deviation = {std}')\n",
    "th = std/2\n",
    "\n",
    "outval = []\n",
    "vrefout = []\n",
    "vthout = []\n",
    "start = 0\n",
    "val = False\n",
    "timestamps = []\n",
    "vref = data_sel[0] + th\n",
    "mins, maxs = [], []\n",
    "for i, v in enumerate(data_sel):\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_sel[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",
    "\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_sel, np.array(outval).astype(int), color='red', alpha=0.3)\n",
    "ax.plot(times_sel, vrefout)\n",
    "ax.plot(times_sel, vthout)\n",
    "ax.plot(times_sel, data_sel)\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_sel[prev_ts], times_sel[(next_ts-1)], color=col, alpha=0.5)\n",
    "ax.plot(times_sel, data_sel)\n",
    "ax.set_xlim([times_sel[200], times_sel[-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_sel[next_ts] - data_sel[prev_ts])\n",
    "        slope_times.append((times_sel[prev_ts] + times_sel[next_ts])/2)\n",
    "        if td > 0:\n",
    "            slopes.append(vd/td)\n",
    "            #slopes.append(1/td)\n",
    "        else:\n",
    "            slopes.append(abs(data_sel[prev_ts-1] - data_sel[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])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 123,
   "id": "9369a77d-47e0-4b65-b1e1-8c3e6fc3fdcf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.9553552758882811\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 28888 characters]\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots()\n",
    "ax.plot(mins)\n",
    "ax.plot(maxs)\n",
    "mean_amplitude = np.mean(maxs) - np.mean(mins)\n",
    "print(mean_amplitude)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 124,
   "id": "1ea3401e-9a78-4fe6-9f6e-f893f4a74602",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0.2585407858557459, 0.29614308175817716)"
      ]
     },
     "execution_count": 124,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 83596 characters]\n",
      "text/plain": [
       "<Figure size 2016x432 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "AVG_WIDTH = 5\n",
    "\n",
    "cs = np.cumsum([0, *slopes]) \n",
    "slopes_mean = (cs[AVG_WIDTH:] - cs[:-AVG_WIDTH]) / float(AVG_WIDTH)\n",
    "slopes_mean = np.hstack([[slopes_mean[0]]*(AVG_WIDTH//2), slopes_mean, [slopes_mean[-1]]*(AVG_WIDTH//2)])\n",
    "\n",
    "fig, ax = plt.subplots(1, 1, figsize=(28, 6))\n",
    "ax.plot(slope_times, slopes)\n",
    "ax.plot(slope_times, slopes_mean)\n",
    "ax.set_xlim([slope_times[0], slope_times[-1]])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 127,
   "id": "db99e126-b4a5-408c-966c-7095c57d8c2e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 233716 characters]\n",
      "text/plain": [
       "<Figure size 2016x1296 with 5 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "bar_widths_total = [slope_left/2 + bar + slope_right/2 \n",
    "                    for slope_left, bar, slope_right in zip([slope_widths[0], *slope_widths], bar_widths, [*slope_widths, slope_widths[-1]])]\n",
    "\n",
    "bar_widths_normalized = []\n",
    "for bar, slope in zip(bar_widths_total, slopes_mean):\n",
    "    # All times here are in samples\n",
    "    t_transition = mean_amplitude / slope\n",
    "    bar /= t_transition\n",
    "    # 'bar' is now in \"slit-width units\"\n",
    "    bar_widths_normalized.append(bar)\n",
    "bar_widths_normalized = np.array(bar_widths_normalized)[-59:]\n",
    "\n",
    "# note: we're still mirrored\n",
    "unit_mean_right = np.mean(np.hstack([bar_widths_normalized[:3], bar_widths_normalized[28:33]]))\n",
    "unit_mean_left = np.mean(np.hstack([bar_widths_normalized[28:33], bar_widths_normalized[-3:]]))\n",
    "bar_widths_normalized[:30] /= unit_mean_right\n",
    "bar_widths_normalized[30:] /= unit_mean_left\n",
    "\n",
    "fig, axs = plt.subplots(4, 1, figsize=(28, 18), sharex=True)\n",
    "axs[0].plot(bar_widths_total)\n",
    "#axs[0].twinx().plot(slope_widths, color='red', alpha=0.5)\n",
    "axs[0].twinx().plot(1/np.array(slopes_mean), color='red')\n",
    "axs[0].grid()\n",
    "\n",
    "for i in [1,2,3,4]:\n",
    "    axs[1].axhline(i, color='blue')\n",
    "axs[1].plot(bar_widths_normalized)\n",
    "for i, (val, w) in enumerate(zip(bar_vals, bar_widths_normalized)):\n",
    "    axs[1].plot([i], [w], marker='o', color=('green' if val else 'red'), alpha=.5)\n",
    "axs[1].grid()\n",
    "\n",
    "axs[2].plot(np.round(bar_widths_normalized))\n",
    "for i, (val, w) in enumerate(zip(bar_vals, bar_widths_normalized)):\n",
    "    axs[2].plot([i], [round(w)], marker='o', color=('green' if val else 'red'), alpha=.5)\n",
    "axs[2].grid()\n",
    "\n",
    "ref = [1, 1, 1, 1, 3, 1, 2, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 1, 2, 1, 3, 2, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 3, 2, 1, 1, 3, 2, 1, 1, 2, 2, 2, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1, 2, 2, 1, 1, 1]\n",
    "ref = list(reversed(ref))\n",
    "axs[3].plot(np.round(bar_widths_normalized), color='red')\n",
    "axs[3].plot(ref)\n",
    "for i, w in enumerate(ref):\n",
    "    axs[3].plot([i], [w], marker='o', color=('green' if i%2==0 else 'red'), alpha=.5)\n",
    "axs[3].grid()\n",
    "\n",
    "# Reference:\n",
    "# 1 (1) 1 (1) M\n",
    "# 3 (1) 2 (1)\n",
    "# 2 (2) 2 (2) 2 (2)\n",
    "# 1 (2) 2 (1) 2 (1)\n",
    "# 3 (2) 1 (1) 4 (1) \n",
    "\n",
    "# 1 (1) 1 (1) M\n",
    "\n",
    "# 3 (2) 1 (1) 3 (2) 1 (1) 2 (2) 2 (1)\n",
    "# 2 (1) 2 (2) 3 (2) 1 (1) 2 (1) 2 (2)\n",
    "\n",
    "# 1 (1) 1 M\n",
    "\n",
    "# Looks good so far, we just under-estimate the barcode speed when it's fast, so the results at the start of the barcode (end of trace) are garbled.\n",
    "# I suspect that we run into a slew-rate limitation of our *very* simple phototransistor circuit here, which only uses a fixed load resistor of IIRC 3k3.\n",
    "# TODO: Change that load resistor to something lower and see what happens.\n",
    "# Scratch that, slew rate's fine. Our hysteresis/threshold scheme is just not very precise in timing for fast edges.\n",
    "# TODO: Fix that."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 128,
   "id": "5859c9c7-1f97-4511-a2e0-8c2cecf93a39",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "7 L [1.    2.882 1.232 1.788] [ 5.453  2.988  5.178  1.923  6.713  4.524  8.492  0.113  2.302  7.642  5.603  1.413  1.877 11.824  1.688  1.224 13.217  8.288  8.753  6.067]\n",
      "1 G [1.    1.991 1.803 2.254] [ 6.218  2.611  2.085  6.255  2.478  3.004  4.676  1.728  1.202  5.692  1.577  0.103  1.71   7.379  4.236  2.629 12.2    4.986  6.593  3.184]\n",
      "1 L [1.666 2.009 1.4   1.   ] [ 1.94   0.472  2.489  4.569  5.02   3.003 10.621  2.586  4.604  3.958  5.821  1.803  1.272  8.22   1.254  1.786  6.626  3.689  3.157  5.289]\n",
      "2 G [1.644 2.165 1.    1.757] [ 2.439  1.727  2.543  4.355  5.832  5.016  6.802  1.171  1.987  3.254  4.317  1.501  0.213 11.347  1.397  2.686  7.48   6.058  4.769  3.028]\n",
      "5 G [1.    2.945 1.566 1.224] [ 5.265  2.132  5.574  1.483  6.442  2.999 11.811  0.926  4.368  8.707  7.126  1.684  2.816  9.757  1.374  0.241 13.155  6.89   8.023  8.259]\n",
      "3 L [1.162 3.677 1.243 1.   ] [ 6.25   4.087  9.442  0.19  11.279  5.925 16.253  1.544  6.899 11.604 11.766  4.412  4.574 14.793  1.22   1.057 15.28  10.955 11.118 11.929]\n",
      "first digit: 8\n",
      "0\n",
      "0\n",
      "1\n",
      "2\n",
      "0\n",
      "2\n",
      "Code: 8 711253 001202\n",
      "OK\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 61880 characters]\n",
      "text/plain": [
       "<Figure size 2016x288 with 15 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "d_in = bar_widths_normalized[::-1]\n",
    "\n",
    "# Digit codes in bar width notation\n",
    "l_codes = {0: (3, 2, 1, 1),\n",
    "           1: (2, 2, 2, 1),\n",
    "           2: (2, 1, 2, 2),\n",
    "           3: (1, 4, 1, 1),\n",
    "           4: (1, 1, 3, 2),\n",
    "           5: (1, 2, 3, 1),\n",
    "           6: (1, 1, 1, 4),\n",
    "           7: (1, 3, 1, 2),\n",
    "           8: (1, 2, 1, 3),\n",
    "           9: (3, 1, 1, 2)}\n",
    "g_codes = {0: (1, 1, 2, 3),\n",
    "           1: (1, 2, 2, 2),\n",
    "           2: (2, 2, 1, 2),\n",
    "           3: (1, 1, 4, 1),\n",
    "           4: (2, 3, 1, 1),\n",
    "           5: (1, 3, 2, 1),\n",
    "           6: (4, 1, 1, 1),\n",
    "           7: (2, 1, 3, 1),\n",
    "           8: (3, 1, 2, 1),\n",
    "           9: (2, 1, 1, 3),\n",
    "           9: (2, 1, 1, 3)}\n",
    "r_codes = {0: (3, 2, 1, 1),\n",
    "           1: (2, 2, 2, 1),\n",
    "           2: (2, 1, 2, 2),\n",
    "           3: (1, 4, 1, 1),\n",
    "           4: (1, 1, 3, 2),\n",
    "           5: (1, 2, 3, 1),\n",
    "           6: (1, 1, 1, 4),\n",
    "           7: (1, 3, 1, 2),\n",
    "           8: (1, 2, 1, 3),\n",
    "           9: (3, 1, 1, 2)}\n",
    "left_parity_pattern = {\n",
    "    0: 'LLLLLL',\n",
    "    1: 'LLGLGG',\n",
    "    2: 'LLGGLG',\n",
    "    3: 'LLGGGL',\n",
    "    4: 'LGLLGG',\n",
    "    5: 'LGGLLG',\n",
    "    6: 'LGGGLL',\n",
    "    7: 'LGLGLG',\n",
    "    8: 'LGLGGL',\n",
    "    9: 'LGGLGL'}\n",
    "l, g, r = [np.array(list(d.values())) for d in [l_codes, g_codes, r_codes]]\n",
    "p = np.array([[1 if c == 'G' else 0 for c in w] for w in left_parity_pattern.values()])\n",
    "\n",
    "marker_start, code_left, marker_mid, code_right, marker_end = np.split(d_in, np.cumsum([3, 6*4, 5, 6*4]))\n",
    "code_left, code_right = np.split(code_left, 6), np.split(code_right, 6)\n",
    "\n",
    "fig, axs = plt.subplots(1, 15, figsize=(28, 4), sharey=True)\n",
    "for i, elem in enumerate([marker_start, *code_left, marker_mid, *code_right, marker_end]):\n",
    "    axs[i].plot(elem)\n",
    "\n",
    "np.set_printoptions(linewidth=200, precision=3)\n",
    "first_digit = []\n",
    "digits = []\n",
    "for i, elem in enumerate(code_left):\n",
    "    elem = elem - elem.min() + 1\n",
    "    axs[i+1].plot(elem)\n",
    "    tiled = np.tile(elem, (20, 1))\n",
    "    probs = ((tiled - np.vstack([l, g]))**2).sum(axis=1).argmin()\n",
    "    digit = probs % 10\n",
    "    first_digit.append(probs//10)\n",
    "    print(digit, 'LG'[probs//10], elem, ((tiled - np.vstack([l, g]))**2).sum(axis=1))\n",
    "    digits.append(digit)\n",
    "\n",
    "first_digit = ((np.tile(first_digit, (10, 1)) - p)**2).sum(axis=1).argmin()\n",
    "print('first digit:', first_digit)\n",
    "digits.insert(0, first_digit)\n",
    "\n",
    "for elem in code_right:\n",
    "    elem = elem - elem.min() + 1\n",
    "    tiled = np.tile(elem, (10, 1))\n",
    "    digit = ((tiled - r)**2).sum(axis=1).argmin()\n",
    "    print(digit)\n",
    "    digits.append(digit)\n",
    "    \n",
    "code_str = ''.join(map(str, digits))\n",
    "code_str = f'{code_str[0]} {code_str[1:7]} {code_str[7:]}'\n",
    "print('Code:', code_str)\n",
    "print('OK' if code_str == '8 711253 001202' else 'ERROR')"
   ]
  }
 ],
 "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
}
