{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e18b6699",
   "metadata": {},
   "source": [
    "# Radar Image Data Exploration\n",
    "\n",
    "Eric Martin \n",
    "MBARI 2021\n",
    "\n",
    "This assumes you've built the binaries in this directory, the goal of this notebook is to demonstrate the following:\n",
    "\n",
    "1. Processing a Radar PCAP file into three CSV files: (1) Radar image data (2) radar configuration messages, and (3) the AHRS data. \n",
    "2. Plotting a single scan of radar data."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ffc58746",
   "metadata": {},
   "source": [
    "## Process the PCAP File\n",
    "To date the project has been collecting the data through a raw packet capture tool, `tshark`. We have written C tools to go through and parse these files into CSV files of interest for broader accessibility. Some sample data is present in the repository. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "97fb8719",
   "metadata": {},
   "source": [
    "### Radar Settings Reports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "335e9399",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "total 1635120\r\n",
      "-rw-r--r--  1 emartin  emartin   737B Oct  4 11:43 mbariAtSeaWecRadar_00315_202109\r\n",
      "-rwxrwxrwx  1 emartin  emartin   200M Sep 28 17:20 \u001b[31mmbariAtSeaWecRadar_00315_20210928171518.pcapng\u001b[m\u001b[m\r\n",
      "-rwxrwxrwx@ 1 emartin  emartin   200M Sep 28 17:25 \u001b[31mmbariAtSeaWecRadar_00316_20210928172018.pcapng\u001b[m\u001b[m\r\n",
      "-rwxrwxrwx@ 1 emartin  emartin   200M Sep 30 14:20 \u001b[31mmbariAtSeaWecRadar_00684_20210930000030.pcapng\u001b[m\u001b[m\r\n",
      "-rw-r--r--  1 emartin  emartin   200M Oct  5 21:07 mbariAtSeaWecRadar_02325_20211005164626.pcapng\r\n"
     ]
    }
   ],
   "source": [
    "!ls -lh ../data/"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "555dfc75",
   "metadata": {},
   "source": [
    "We are pretty interested in the biggest day observed yet, 9/28/2021. Let's process that one. First let's process the radar data. The program, if built and installed should print out help with no inputs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a1c32660",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "radar packet parser, usage pcaptoradar <pcap filename>\r",
      "\r\n",
      "  flags: -a | -b  : print report data for channels A or B\r",
      "\r\n",
      "         -h       : add csv header line for reports, use with -a/-b\r",
      "\r\n",
      "         -A | -B  : print scanline data for channels A or B\r",
      "\r\n",
      "         -H       : add csv header line for scan data, use with -A/-B\r",
      "\r\n",
      "         -s       : print statistics for information decodable in the file\r",
      "\r\n",
      "         -q       : quiet mode, suppress output\r",
      "\r\n"
     ]
    }
   ],
   "source": [
    "!pcaptoradar"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4fdc69ac",
   "metadata": {},
   "source": [
    "Let's make sure there are data in the file: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "6c6a7d0d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening file: ../data/mbariAtSeaWecRadar_02325_20211005164626.pcapng\n",
      "----------------\n",
      "REPORTS_A: 300\n",
      "REPORTS_B: 300\n",
      "FRAMES_A:  11462\n",
      "  SCANS:     366784\n",
      "  REVS_A:    89\n",
      "AHRS:      140817\n",
      "----------------\n"
     ]
    }
   ],
   "source": [
    "!pcaptoradar -s ../data/mbariAtSeaWecRadar_02325_20211005164626.pcapng"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64b12aa9",
   "metadata": {},
   "source": [
    "Yes, there are reports data for both channels on the radar, and many Frames of sonar data, covering 367K radar scan lines, which add up to 89 full revolutions of the radar for the entire file.\n",
    "\n",
    "Next, let's convert the reports, and just print the header:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "c39f2c53",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening file: ../data/mbariAtSeaWecRadar_02325_20211005164626.pcapng\n",
      "time,channel,range,gain,sea_auto,sea,rain,interference_rejection,target_expansion,target_boost\n",
      "1633477586.071070,3,5000,60,0,25,0,0,1,1\n",
      "1633477587.070790,3,5000,60,0,25,0,0,1,1\n",
      "1633477588.070737,3,5000,60,0,25,0,0,1,1\n"
     ]
    }
   ],
   "source": [
    "!pcaptoradar -ah ../data/mbariAtSeaWecRadar_02325_20211005164626.pcapng | head -n4"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4afa729d",
   "metadata": {},
   "source": [
    "We can see that this isn't changing, which is good, at the time of this analysis, there is little change happening on the radar setting, and all of these values make sense (but range is 500.0meters, so something is a bit off). If we wanted to make a file for all the reports we simply redirecto the stdout to a file, as so:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "4094ea20",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening file: ../data/mbariAtSeaWecRadar_02325_20211005164626.pcapng\r\n"
     ]
    }
   ],
   "source": [
    "!pcaptoradar -ah ../data/mbariAtSeaWecRadar_02325_20211005164626.pcapng > reports_a.csv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "00d53c0d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-rw-r--r--  1 emartin  emartin   633K Oct  6 05:55 ahrs.csv\r\n",
      "-rw-r--r--  1 emartin  emartin    12K Oct  8 10:59 reports_a.csv\r\n",
      "-rw-r--r--  1 emartin  emartin   738M Oct  6 15:19 scans.csv\r\n"
     ]
    }
   ],
   "source": [
    "!ls -lh *.csv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "2251ca2e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>time</th>\n",
       "      <th>channel</th>\n",
       "      <th>range</th>\n",
       "      <th>gain</th>\n",
       "      <th>sea_auto</th>\n",
       "      <th>sea</th>\n",
       "      <th>rain</th>\n",
       "      <th>interference_rejection</th>\n",
       "      <th>target_expansion</th>\n",
       "      <th>target_boost</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>3</td>\n",
       "      <td>5000</td>\n",
       "      <td>60</td>\n",
       "      <td>0</td>\n",
       "      <td>25</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>3</td>\n",
       "      <td>5000</td>\n",
       "      <td>60</td>\n",
       "      <td>0</td>\n",
       "      <td>25</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>3</td>\n",
       "      <td>5000</td>\n",
       "      <td>60</td>\n",
       "      <td>0</td>\n",
       "      <td>25</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>3</td>\n",
       "      <td>5000</td>\n",
       "      <td>60</td>\n",
       "      <td>0</td>\n",
       "      <td>25</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>3</td>\n",
       "      <td>5000</td>\n",
       "      <td>60</td>\n",
       "      <td>0</td>\n",
       "      <td>25</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>1</td>\n",
       "      <td>1</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "           time  channel  range  gain  sea_auto  sea  rain  \\\n",
       "0  1.633478e+09        3   5000    60         0   25     0   \n",
       "1  1.633478e+09        3   5000    60         0   25     0   \n",
       "2  1.633478e+09        3   5000    60         0   25     0   \n",
       "3  1.633478e+09        3   5000    60         0   25     0   \n",
       "4  1.633478e+09        3   5000    60         0   25     0   \n",
       "\n",
       "   interference_rejection  target_expansion  target_boost  \n",
       "0                       0                 1             1  \n",
       "1                       0                 1             1  \n",
       "2                       0                 1             1  \n",
       "3                       0                 1             1  \n",
       "4                       0                 1             1  "
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "reports_df = pd.read_csv('reports_a.csv')\n",
    "reports_df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "f9f63792",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<AxesSubplot:>"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 864x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "reports_df['time'].diff().plot(figsize=(12,4))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ca65d411",
   "metadata": {},
   "source": [
    "### AHRS Data Processing\n",
    "Much like the reports data, we can generate csv files using the `pcaptoahrs` tool."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "4346b735",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ahrs lcm parser, usage pcaptoahrs <pcap filename>\r\n",
      "\toptions: -h (enable csv header)\r\n"
     ]
    }
   ],
   "source": [
    "!pcaptoahrs"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "d6b28bc9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening file: ../data/mbariAtSeaWecRadar_02325_20211005164626.pcapng\r\n",
      "pcap_time,lcm_time,latitude,longitude,yaw_true,pitch,roll,x_accel,y_accel,z_accel,n_velocity,e_velocity,d_velocity,altitude,x_rate,y_rate,z_rate, x_rate_temp,\r\n",
      "1633477586.057278,1633477586.057142,36.743225097656250,-121.881103515625000,123.420410,-1.235962,-2.949829,-0.060730,0.033875,-1.022949,0.007812,-0.070312,-0.039062,-32.750000,-3.729858,-10.997314,-0.307617,29.324341,\r\n"
     ]
    }
   ],
   "source": [
    "!pcaptoahrs -h ../data/mbariAtSeaWecRadar_02325_20211005164626.pcapng | head -n2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "70049579",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening file: ../data/mbariAtSeaWecRadar_02325_20211005164626.pcapng\r\n"
     ]
    }
   ],
   "source": [
    "!pcaptoahrs -h ../data/mbariAtSeaWecRadar_02325_20211005164626.pcapng > ahrs.csv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "c1287840",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-rw-r--r--  1 emartin  emartin   633K Oct  8 11:00 ahrs.csv\r\n",
      "-rw-r--r--  1 emartin  emartin    12K Oct  8 10:59 reports_a.csv\r\n",
      "-rw-r--r--  1 emartin  emartin   738M Oct  6 15:19 scans.csv\r\n"
     ]
    }
   ],
   "source": [
    "!ls -lh *.csv"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "b24eee91",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>pcap_time</th>\n",
       "      <th>lcm_time</th>\n",
       "      <th>latitude</th>\n",
       "      <th>longitude</th>\n",
       "      <th>yaw_true</th>\n",
       "      <th>pitch</th>\n",
       "      <th>roll</th>\n",
       "      <th>x_accel</th>\n",
       "      <th>y_accel</th>\n",
       "      <th>z_accel</th>\n",
       "      <th>n_velocity</th>\n",
       "      <th>e_velocity</th>\n",
       "      <th>d_velocity</th>\n",
       "      <th>altitude</th>\n",
       "      <th>x_rate</th>\n",
       "      <th>y_rate</th>\n",
       "      <th>z_rate</th>\n",
       "      <th>x_rate_temp</th>\n",
       "      <th>Unnamed: 18</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>36.743225</td>\n",
       "      <td>-121.881104</td>\n",
       "      <td>123.420410</td>\n",
       "      <td>-1.235962</td>\n",
       "      <td>-2.949829</td>\n",
       "      <td>-0.060730</td>\n",
       "      <td>0.033875</td>\n",
       "      <td>-1.022949</td>\n",
       "      <td>0.007812</td>\n",
       "      <td>-0.070312</td>\n",
       "      <td>-0.039062</td>\n",
       "      <td>-32.75</td>\n",
       "      <td>-3.729858</td>\n",
       "      <td>-10.997314</td>\n",
       "      <td>-0.307617</td>\n",
       "      <td>29.324341</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>36.743225</td>\n",
       "      <td>-121.881104</td>\n",
       "      <td>123.431396</td>\n",
       "      <td>-2.169800</td>\n",
       "      <td>-3.180542</td>\n",
       "      <td>-0.114746</td>\n",
       "      <td>0.066528</td>\n",
       "      <td>-1.022339</td>\n",
       "      <td>0.007812</td>\n",
       "      <td>-0.078125</td>\n",
       "      <td>-0.023438</td>\n",
       "      <td>-32.75</td>\n",
       "      <td>-0.961304</td>\n",
       "      <td>-7.305908</td>\n",
       "      <td>-0.269165</td>\n",
       "      <td>29.321289</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>36.743225</td>\n",
       "      <td>-121.881104</td>\n",
       "      <td>123.436890</td>\n",
       "      <td>-2.609253</td>\n",
       "      <td>-3.147583</td>\n",
       "      <td>-0.144958</td>\n",
       "      <td>0.088501</td>\n",
       "      <td>-1.024170</td>\n",
       "      <td>0.031250</td>\n",
       "      <td>-0.164062</td>\n",
       "      <td>-0.054688</td>\n",
       "      <td>-32.75</td>\n",
       "      <td>1.807251</td>\n",
       "      <td>-1.730347</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>29.318237</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>36.743225</td>\n",
       "      <td>-121.881104</td>\n",
       "      <td>123.425903</td>\n",
       "      <td>-2.466431</td>\n",
       "      <td>-2.834473</td>\n",
       "      <td>-0.145264</td>\n",
       "      <td>0.095215</td>\n",
       "      <td>-1.020813</td>\n",
       "      <td>0.054688</td>\n",
       "      <td>-0.265625</td>\n",
       "      <td>-0.078125</td>\n",
       "      <td>-32.75</td>\n",
       "      <td>4.441223</td>\n",
       "      <td>3.864441</td>\n",
       "      <td>0.192261</td>\n",
       "      <td>29.318237</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>36.743225</td>\n",
       "      <td>-121.881104</td>\n",
       "      <td>123.414917</td>\n",
       "      <td>-1.823730</td>\n",
       "      <td>-2.296143</td>\n",
       "      <td>-0.108032</td>\n",
       "      <td>0.087585</td>\n",
       "      <td>-1.022949</td>\n",
       "      <td>0.046875</td>\n",
       "      <td>-0.273438</td>\n",
       "      <td>-0.039062</td>\n",
       "      <td>-32.75</td>\n",
       "      <td>6.152344</td>\n",
       "      <td>8.555603</td>\n",
       "      <td>0.076904</td>\n",
       "      <td>29.315186</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      pcap_time      lcm_time   latitude   longitude    yaw_true     pitch  \\\n",
       "0  1.633478e+09  1.633478e+09  36.743225 -121.881104  123.420410 -1.235962   \n",
       "1  1.633478e+09  1.633478e+09  36.743225 -121.881104  123.431396 -2.169800   \n",
       "2  1.633478e+09  1.633478e+09  36.743225 -121.881104  123.436890 -2.609253   \n",
       "3  1.633478e+09  1.633478e+09  36.743225 -121.881104  123.425903 -2.466431   \n",
       "4  1.633478e+09  1.633478e+09  36.743225 -121.881104  123.414917 -1.823730   \n",
       "\n",
       "       roll   x_accel   y_accel   z_accel  n_velocity  e_velocity  d_velocity  \\\n",
       "0 -2.949829 -0.060730  0.033875 -1.022949    0.007812   -0.070312   -0.039062   \n",
       "1 -3.180542 -0.114746  0.066528 -1.022339    0.007812   -0.078125   -0.023438   \n",
       "2 -3.147583 -0.144958  0.088501 -1.024170    0.031250   -0.164062   -0.054688   \n",
       "3 -2.834473 -0.145264  0.095215 -1.020813    0.054688   -0.265625   -0.078125   \n",
       "4 -2.296143 -0.108032  0.087585 -1.022949    0.046875   -0.273438   -0.039062   \n",
       "\n",
       "   altitude    x_rate     y_rate    z_rate   x_rate_temp  Unnamed: 18  \n",
       "0    -32.75 -3.729858 -10.997314 -0.307617     29.324341          NaN  \n",
       "1    -32.75 -0.961304  -7.305908 -0.269165     29.321289          NaN  \n",
       "2    -32.75  1.807251  -1.730347  0.000000     29.318237          NaN  \n",
       "3    -32.75  4.441223   3.864441  0.192261     29.318237          NaN  \n",
       "4    -32.75  6.152344   8.555603  0.076904     29.315186          NaN  "
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "ahrs_df = pd.read_csv('ahrs.csv')\n",
    "ahrs_df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "c9397e5a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1633477586.057278"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.to_datetime(ahrs_df['pcap_time'][0], "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "44e9affb",
   "metadata": {},
   "source": [
    "### Radar Scan Line Processing\n",
    "Our last pcap file process is to take the file, restructure the packets, and turn it into something we can parse in in a higher level language, like python:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "0761b147",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening file: ../data/mbariAtSeaWecRadar_02325_20211005164626.pcapng\r\n"
     ]
    }
   ],
   "source": [
    "!pcaptoradar -AH ../data/mbariAtSeaWecRadar_02325_20211005164626.pcapng > scans.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00f6802d",
   "metadata": {},
   "source": [
    "That took a while, but now we have the file in CSV form, let's take a look at how large it ended up."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "049a17cd",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "-rw-r--r--  1 emartin  emartin   633K Oct  6 05:55 ahrs.csv\r\n",
      "-rw-r--r--  1 emartin  emartin    12K Oct  6 05:55 reports_a.csv\r\n",
      "-rw-r--r--  1 emartin  emartin   738M Oct  6 15:19 scans.csv\r\n"
     ]
    }
   ],
   "source": [
    "!ls -lh *.csv"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "017014b1",
   "metadata": {},
   "source": [
    "So in the end, from a 200MB file, we are generating another file that is 745MB. Let's see if we can parse the file with pandas, and then the fun can begin. "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5405a7b3",
   "metadata": {},
   "source": [
    "### CSV Reading"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "b03b174a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "0621b5e6",
   "metadata": {},
   "outputs": [],
   "source": [
    "#Set the dtypes to improve memory performance on import\n",
    "dtypes_scans = {'time':'float',\n",
    "                'channel':'string',\n",
    "                'scan_line':'uint16',\n",
    "                'heading':'int32',\n",
    "                'angle':'uint16',\n",
    "                'angle_d':'float',\n",
    "                'range':'uint16',\n",
    "               } \n",
    "for i in range(1024):\n",
    "    colname = f'r{i:04}'\n",
    "    dtypes_scans[colname]='uint8'"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "id": "42964b83",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "CPU times: user 22.5 s, sys: 961 ms, total: 23.5 s\n",
      "Wall time: 23.5 s\n"
     ]
    }
   ],
   "source": [
    "%%time\n",
    "df_scans = pd.read_csv('scans.csv', dtype=dtypes_scans)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "e8656712",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>time</th>\n",
       "      <th>channel</th>\n",
       "      <th>scan_line</th>\n",
       "      <th>heading</th>\n",
       "      <th>angle</th>\n",
       "      <th>angle_d</th>\n",
       "      <th>range</th>\n",
       "      <th>r0000</th>\n",
       "      <th>r0001</th>\n",
       "      <th>r0002</th>\n",
       "      <th>...</th>\n",
       "      <th>r1015</th>\n",
       "      <th>r1016</th>\n",
       "      <th>r1017</th>\n",
       "      <th>r1018</th>\n",
       "      <th>r1019</th>\n",
       "      <th>r1020</th>\n",
       "      <th>r1021</th>\n",
       "      <th>r1022</th>\n",
       "      <th>r1023</th>\n",
       "      <th>Unnamed: 1031</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>366779</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>A</td>\n",
       "      <td>2748</td>\n",
       "      <td>-32768</td>\n",
       "      <td>601</td>\n",
       "      <td>52.822266</td>\n",
       "      <td>808</td>\n",
       "      <td>15</td>\n",
       "      <td>15</td>\n",
       "      <td>15</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>366780</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>A</td>\n",
       "      <td>2749</td>\n",
       "      <td>-32768</td>\n",
       "      <td>603</td>\n",
       "      <td>52.998047</td>\n",
       "      <td>808</td>\n",
       "      <td>15</td>\n",
       "      <td>15</td>\n",
       "      <td>15</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>366781</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>A</td>\n",
       "      <td>2750</td>\n",
       "      <td>-32768</td>\n",
       "      <td>605</td>\n",
       "      <td>53.173828</td>\n",
       "      <td>808</td>\n",
       "      <td>15</td>\n",
       "      <td>15</td>\n",
       "      <td>15</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>366782</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>A</td>\n",
       "      <td>2751</td>\n",
       "      <td>-32768</td>\n",
       "      <td>607</td>\n",
       "      <td>53.349609</td>\n",
       "      <td>808</td>\n",
       "      <td>15</td>\n",
       "      <td>15</td>\n",
       "      <td>15</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>366783</th>\n",
       "      <td>1.633478e+09</td>\n",
       "      <td>A</td>\n",
       "      <td>2752</td>\n",
       "      <td>-32768</td>\n",
       "      <td>609</td>\n",
       "      <td>53.525391</td>\n",
       "      <td>808</td>\n",
       "      <td>15</td>\n",
       "      <td>15</td>\n",
       "      <td>15</td>\n",
       "      <td>...</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>0</td>\n",
       "      <td>NaN</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>5 rows × 1032 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "                time channel  scan_line  heading  angle    angle_d  range  \\\n",
       "366779  1.633478e+09       A       2748   -32768    601  52.822266    808   \n",
       "366780  1.633478e+09       A       2749   -32768    603  52.998047    808   \n",
       "366781  1.633478e+09       A       2750   -32768    605  53.173828    808   \n",
       "366782  1.633478e+09       A       2751   -32768    607  53.349609    808   \n",
       "366783  1.633478e+09       A       2752   -32768    609  53.525391    808   \n",
       "\n",
       "        r0000  r0001  r0002  ...  r1015  r1016  r1017  r1018  r1019  r1020  \\\n",
       "366779     15     15     15  ...      0      0      0      0      0      0   \n",
       "366780     15     15     15  ...      0      0      0      0      0      0   \n",
       "366781     15     15     15  ...      0      0      0      0      0      0   \n",
       "366782     15     15     15  ...      0      0      0      0      0      0   \n",
       "366783     15     15     15  ...      0      0      0      0      0      0   \n",
       "\n",
       "        r1021  r1022  r1023  Unnamed: 1031  \n",
       "366779      0      0      0            NaN  \n",
       "366780      0      0      0            NaN  \n",
       "366781      0      0      0            NaN  \n",
       "366782      0      0      0            NaN  \n",
       "366783      0      0      0            NaN  \n",
       "\n",
       "[5 rows x 1032 columns]"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_scans.tail()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "d7389d2d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 366784 entries, 0 to 366783\n",
      "Columns: 1032 entries, time to Unnamed: 1031\n",
      "dtypes: float64(3), int32(1), string(1), uint16(3), uint8(1024)\n",
      "memory usage: 372.9 MB\n"
     ]
    }
   ],
   "source": [
    "df_scans.info()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "79b1a565",
   "metadata": {},
   "source": [
    "### Working with one scan"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a11f5b6c",
   "metadata": {},
   "source": [
    "Let's find the first full revolution, `scan_line` set to 0."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "fd9f31a4",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Get the first full scan\n",
    "idx = df_scans.index[df_scans['angle'] == 1].to_list()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "162cb013",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "scan = df_scans.loc[idx[40]:idx[41]-1]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "dfec7444",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "count    2048.000000\n",
       "mean     2048.000000\n",
       "std      1182.701991\n",
       "min         1.000000\n",
       "25%      1024.500000\n",
       "50%      2048.000000\n",
       "75%      3071.500000\n",
       "max      4095.000000\n",
       "Name: angle, dtype: float64"
      ]
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "scan.angle.describe()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2e61541b",
   "metadata": {},
   "source": [
    "Good! there are 2048 scans in one full revolution, we have something to look at."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "59ed37c4",
   "metadata": {},
   "outputs": [],
   "source": [
    "from PIL import Image as im\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "1b7b11e8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "240\n"
     ]
    }
   ],
   "source": [
    "# Get an uint8 array from the scans\n",
    "scan_data = scan.iloc[:, 7:-1].to_numpy()*16\n",
    "max_scan = np.amax(scan_data)\n",
    "print(max_scan)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "bdb826fd",
   "metadata": {},
   "outputs": [],
   "source": [
    "image_ = im.fromarray(scan_data)\n",
    "image_.save('scan_nonpolar.jpg')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5d37a1d4",
   "metadata": {},
   "source": [
    "Ok, let's see the result: <img src='./scan_nonpolar.jpg' width=\"200\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e0c87d67",
   "metadata": {},
   "source": [
    "### Polar Graphic Generation\n",
    "The final step in creating a monochrome image is to reshape the array into a cartesian one. At the end of the day, we should have a square bound by the radius of the spokes, or $2*(1024)=2048$. Let's take a brute force attempt at this. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "2ae95737",
   "metadata": {},
   "outputs": [],
   "source": [
    "#create an array to pack\n",
    "cart = np.zeros((2048,2048),dtype='uint8')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "4009066e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(0, 1024)"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# def a remapping function \n",
    "def map_polar(angle,length, max_angle=2048, max_length=1024):\n",
    "    '''\n",
    "        angle in decimal degrees\n",
    "        length of array to output\n",
    "        \n",
    "        returns -- x and y arrays for index mapping\n",
    "    '''\n",
    "    ANG2RAD = 2*np.pi/max_angle\n",
    "    x = length * np.cos(angle*ANG2RAD)\n",
    "    y = length * np.sin(angle*ANG2RAD)\n",
    "    \n",
    "    ## shift into positive spaces\n",
    "    x = np.round(x)\n",
    "    y = np.round(y)\n",
    "    x += max_length\n",
    "    y += max_length\n",
    "    return (int(x),int(y))\n",
    "\n",
    "map_polar(1024,1024)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "6f561dee",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Super stupidly, let's go line by line and write over the data for the one before\n",
    "it = np.nditer(scan_data, flags=['multi_index'])\n",
    "for x in it:\n",
    "    if x == 0:\n",
    "        continue\n",
    "    r,c = it.multi_index\n",
    "    crow,ccol = map_polar(r,c)\n",
    "    cart[crow,ccol] = x\n",
    "    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "598f6727",
   "metadata": {},
   "outputs": [],
   "source": [
    "# make an image from the cartesian converted array\n",
    "cart_img = im.fromarray(cart)\n",
    "cart_img.save('scan_polar.jpg')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f09ee58",
   "metadata": {},
   "source": [
    "Here is the result <img src=\"scan_polar.jpg\" width=\"400px\" />"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "21ad7647",
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "f44305b6",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "cart_color = cv2.applyColorMap(cart, cv2.COLORMAP_HOT)\n",
    "cv2.imwrite('scan_polar_hot.jpg',cart_color)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "84d47c7a",
   "metadata": {},
   "source": [
    "Here is the result <img src=\"scan_polar_hot.jpg\" width=\"400px\" />"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "28c57cf0",
   "metadata": {},
   "source": [
    "### Adding in a yaw offset\n",
    "\n",
    "Just a little sandbox area for me to sort out the best way to get a single yaw offset for the whole revolution."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "f24174e1",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1632874586.014958 1632874587.689166 1.674208164215088\n"
     ]
    }
   ],
   "source": [
    "start_t = scan.time.iloc[0]\n",
    "end_t = scan.time.iloc[-1]\n",
    "print(start_t, end_t, end_t-start_t)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "id": "de42676a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<class 'pandas.core.frame.DataFrame'>\n",
      "RangeIndex: 3005 entries, 0 to 3004\n",
      "Data columns (total 19 columns):\n",
      " #   Column        Non-Null Count  Dtype  \n",
      "---  ------        --------------  -----  \n",
      " 0   pcap_time     3005 non-null   float64\n",
      " 1   lcm_time      3005 non-null   float64\n",
      " 2   latitude      3005 non-null   float64\n",
      " 3   longitude     3005 non-null   float64\n",
      " 4   yaw_true      3005 non-null   float64\n",
      " 5   pitch         3005 non-null   float64\n",
      " 6   roll          3005 non-null   float64\n",
      " 7   x_accel       3005 non-null   float64\n",
      " 8   y_accel       3005 non-null   float64\n",
      " 9   z_accel       3005 non-null   float64\n",
      " 10  n_velocity    3005 non-null   float64\n",
      " 11  e_velocity    3005 non-null   float64\n",
      " 12  d_velocity    3005 non-null   float64\n",
      " 13  altitude      3005 non-null   float64\n",
      " 14  x_rate        3005 non-null   float64\n",
      " 15  y_rate        3005 non-null   float64\n",
      " 16  z_rate        3005 non-null   float64\n",
      " 17   x_rate_temp  3005 non-null   float64\n",
      " 18  Unnamed: 18   0 non-null      float64\n",
      "dtypes: float64(19)\n",
      "memory usage: 446.2 KB\n"
     ]
    }
   ],
   "source": [
    "ahrs_df.info()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "id": "09283a73",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "numpy.float64"
      ]
     },
     "execution_count": 72,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "scan_angles = ahrs_df.loc[(ahrs_df.lcm_time >= start_t) & (ahrs_df.lcm_time <=end_t)]\n",
    "scan_angles.yaw_true.describe()\n",
    "type(scan_angles.yaw_true.mean())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "id": "70ca2922",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "unsupported operand type(s) for +: 'int' and 'NoneType'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-71-7364e94b6574>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;36m10\u001b[0m\u001b[0;34m+\u001b[0m\u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for +: 'int' and 'NoneType'"
     ]
    }
   ],
   "source": [
    "10+None"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a72146dd",
   "metadata": {},
   "source": [
    "## Processing the whole file (VERY INEFFICIENTLY)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02321f7c",
   "metadata": {},
   "source": [
    "Let's turn this into and full script, here are the contents of [wec_radar_movie.py](wec_radar_movie.py) file subject to change: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 73,
   "id": "766f9520",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "import numpy as np\r\n",
      "import pandas as pd\r\n",
      "from pandas.core.frame import DataFrame\r\n",
      "\r\n",
      "\r\n",
      "class RadarScans():\r\n",
      "\r\n",
      "    def __init__(self, csvfile:str, ahrs_csv:str = 'None', mode='cartesian'):\r\n",
      "        print(f\"opening file: {csvfile}\")\r\n",
      "        self.scan_df = RadarScans.open_csv_file(csvfile)\r\n",
      "        self.ahrs_df = RadarScans.open_ahrs_file(ahrs_csv)\r\n",
      "        self.initialize_scan_indexes()\r\n",
      "        self.mode=mode\r\n",
      "\r\n",
      "    def open_csv_file(csvfile:str):\r\n",
      "        dtypes_scans = {'time':'float',\r\n",
      "                'channel':'string',\r\n",
      "                'scan_line':'uint16',\r\n",
      "                'heading':'int32',\r\n",
      "                'angle':'uint16',\r\n",
      "                'angle_d':'float',\r\n",
      "                'range':'uint16',\r\n",
      "               } \r\n",
      "        for i in range(1024):\r\n",
      "            colname = f'r{i:04}'\r\n",
      "            dtypes_scans[colname]='uint8'\r\n",
      "\r\n",
      "        scan_df = pd.read_csv(csvfile, dtype=dtypes_scans)\r\n",
      "        return scan_df\r\n",
      "\r\n",
      "    def open_ahrs_file(csvfile):\r\n",
      "        ahrs_df = pd.read_csv(csvfile)\r\n",
      "        return ahrs_df\r\n",
      "\r\n",
      "    def initialize_scan_indexes(self):\r\n",
      "        self.indexes = self.scan_df.index[self.scan_df['angle']==1].to_list()\r\n",
      "        self.num_scans = len(self.indexes)-1\r\n",
      "        print(f'found {self.num_scans} full revolutions in file.')\r\n",
      "\r\n",
      "\r\n",
      "    def select_scan(self, n:int):\r\n",
      "        if (n >= self.num_scans):\r\n",
      "            return None;\r\n",
      "\r\n",
      "        idx = self.indexes\r\n",
      "        scan = self.scan_df.loc[idx[n]:idx[n+1]-1]\r\n",
      "        return scan\r\n",
      "\r\n",
      "    def scan_angle_offset(self, scan:DataFrame):\r\n",
      "\r\n",
      "        # Guard against uninitialized access\r\n",
      "        if self.ahrs_df is None:\r\n",
      "            raise Exception('No AHRS data present')\r\n",
      "        \r\n",
      "        # Find the bounds of the time window\r\n",
      "        start_t = scan.time.iloc[0]\r\n",
      "        end_t = scan.time.iloc[-1]\r\n",
      "\r\n",
      "        yaw_true_mean = self.ahrs_df.loc[(self.ahrs_df.lcm_time >= start_t) & (self.ahrs_df.lcm_time <=end_t)].yaw_true.mean();\r\n",
      "        return yaw_true_mean\r\n",
      "\r\n",
      "\r\n",
      "\r\n",
      "\r\n",
      "    def convert_to_polar(self, scan, scan_df,  returns_per_scanline=1024):\r\n",
      "\r\n",
      "        #create an array to pack\r\n",
      "        out = np.zeros((2*returns_per_scanline,2*returns_per_scanline),dtype='uint8')\r\n",
      "        it = np.nditer(scan, flags=['multi_index'])\r\n",
      "        if self.ahrs_df is None: \r\n",
      "            yaw_angle=0 \r\n",
      "        else: \r\n",
      "            yaw_angle = self.scan_angle_offset(scan_df)\r\n",
      "        for x in it:\r\n",
      "            r,c = it.multi_index\r\n",
      "            if x == 0:\r\n",
      "                continue\r\n",
      "            crow,ccol = RadarScans.map_polar(r,c, yaw=yaw_angle)\r\n",
      "            out[crow,ccol] = x\r\n",
      "        return out\r\n",
      "\r\n",
      "    def map_polar(angle,length, yaw=0, max_angle=2048, max_length=1024):\r\n",
      "        '''\r\n",
      "            angle in integer representaion, scaled by Max_angle (default 0-2048)\r\n",
      "            length of array to output\r\n",
      "\r\n",
      "            returns -- x and y arrays for index mapping\r\n",
      "        '''\r\n",
      "        DEG2RAD = np.pi/180\r\n",
      "        ANG2RAD = 2*np.pi/max_angle\r\n",
      "        f_angle = ANG2RAD*angle\r\n",
      "        f_angle += yaw*DEG2RAD\r\n",
      "        x = length * np.cos(f_angle)\r\n",
      "        y = length * np.sin(f_angle)\r\n",
      "    \r\n",
      "        ## shift into positive spaces\r\n",
      "        x = np.round(x)\r\n",
      "        y = np.round(y)\r\n",
      "        x += max_length\r\n",
      "        y += max_length\r\n",
      "        return (int(x),int(y))        \r\n",
      "\r\n",
      "    def __iter__(self):\r\n",
      "        self.current_scan = 0\r\n",
      "        return self\r\n",
      "\r\n",
      "    def __next__(self):\r\n",
      "        \"\"\"Returns a ndarray of uint8 that can be used for image/movie conversion\r\n",
      "        \"\"\"\r\n",
      "        scan =self.select_scan(self.current_scan)\r\n",
      "        if scan is None:\r\n",
      "            raise StopIteration\r\n",
      "\r\n",
      "        out = scan.iloc[:, 7:-1].to_numpy()*16\r\n",
      "        if self.mode == 'polar':\r\n",
      "            out = self.convert_to_polar(out, scan)\r\n",
      "\r\n",
      "            \r\n",
      "\r\n",
      "        self.current_scan += 1\r\n",
      "        return out\r\n",
      "    \r\n",
      "if __name__ == '__main__':\r\n",
      "    from PIL import Image\r\n",
      "\r\n",
      "    fname = 'scans.csv'\r\n",
      "    scanner = RadarScans(fname, ahrs_csv='ahrs.csv', mode='polar')\r\n",
      "\r\n",
      "    cnt = 0\r\n",
      "\r\n",
      "    for scan in scanner:\r\n",
      "        image = Image.fromarray(scan)\r\n",
      "        im_name = f'./img/scan_pa_{cnt:04}.jpg'\r\n",
      "        print(f'Writing image: {im_name}...')\r\n",
      "        image.save(im_name)        \r\n",
      "        cnt+=1\r\n",
      "\r\n",
      "\r\n"
     ]
    }
   ],
   "source": [
    "!cat wec_radar_movie.py"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a418edf1",
   "metadata": {},
   "source": [
    "Now we have a set of images that we can turn into a movie. There are many ways to achieve this task, for now we will use [ffmpeg](https://ffmpeg.org/). "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "5f4cd986",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ffmpeg version 4.4 Copyright (c) 2000-2021 the FFmpeg developers\n",
      "  built with Apple clang version 12.0.0 (clang-1200.0.32.29)\n",
      "  configuration: --prefix=/usr/local/Cellar/ffmpeg/4.4_2 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-avresample --enable-videotoolbox\n",
      "  libavutil      56. 70.100 / 56. 70.100\n",
      "  libavcodec     58.134.100 / 58.134.100\n",
      "  libavformat    58. 76.100 / 58. 76.100\n",
      "  libavdevice    58. 13.100 / 58. 13.100\n",
      "  libavfilter     7.110.100 /  7.110.100\n",
      "  libavresample   4.  0.  0 /  4.  0.  0\n",
      "  libswscale      5.  9.100 /  5.  9.100\n",
      "  libswresample   3.  9.100 /  3.  9.100\n",
      "  libpostproc    55.  9.100 / 55.  9.100\n",
      "Input #0, image2, from './img/scan_p_%04d.jpg':\n",
      "  Duration: 00:00:14.92, start: 0.000000, bitrate: N/A\n",
      "  Stream #0:0: Video: mjpeg (Baseline), gray(bt470bg/unknown/unknown), 2048x2048 [SAR 1:1 DAR 1:1], 12 fps, 12 tbr, 12 tbn, 12 tbc\n",
      "Stream mapping:\n",
      "  Stream #0:0 -> #0:0 (mjpeg (native) -> h264 (libx264))\n",
      "Press [q] to stop, [?] for help\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0musing SAR=1/1\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0musing cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mprofile High, level 5.0, 4:2:0, 8-bit\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0m264 - core 163 r3060 5db6aa6 - H.264/MPEG-4 AVC codec - Copyleft 2003-2021 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=24 lookahead_threads=4 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=12 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00\n",
      "Output #0, mp4, to 'output.mp4':\n",
      "  Metadata:\n",
      "    encoder         : Lavf58.76.100\n",
      "  Stream #0:0: Video: h264 (avc1 / 0x31637661), yuv420p(tv, bt470bg/unknown/unknown, progressive), 2048x2048 [SAR 1:1 DAR 1:1], q=2-31, 12 fps, 12288 tbn\n",
      "    Metadata:\n",
      "      encoder         : Lavc58.134.100 libx264\n",
      "    Side data:\n",
      "      cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A\n",
      "frame=  179 fps= 44 q=-1.0 Lsize=    9146kB time=00:00:14.66 bitrate=5108.4kbits/s speed=3.63x    \n",
      "video:9143kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.032609%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mframe I:16    Avg QP:17.88  size: 88040\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mframe P:55    Avg QP:24.82  size: 46904\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mframe B:108   Avg QP:25.82  size: 49754\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mconsecutive B-frames: 15.6%  6.7% 15.1% 62.6%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mmb I  I16..4: 18.3% 75.9%  5.8%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mmb P  I16..4:  0.3% 10.4%  3.7%  P16..4:  1.4%  0.3%  0.1%  0.0%  0.0%    skip:83.8%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mmb B  I16..4:  0.1%  1.4%  3.0%  B16..8: 14.1%  0.9%  0.3%  direct: 0.3%  skip:79.8%  L0:50.8% L1:48.1% BI: 1.1%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0m8x8 transform intra:67.2% inter:33.8%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mcoded y,uvDC,uvAC intra: 19.6% 0.0% 0.0% inter: 1.4% 0.0% 0.0%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mi16 v,h,dc,p: 88%  7%  5%  0%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mi8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 51%  6% 39%  1%  1%  1%  1%  1%  1%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mi4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 19% 13% 38%  6%  5%  6%  5%  5%  4%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mi8c dc,h,v,p: 100%  0%  0%  0%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mWeighted P-Frames: Y:0.0% UV:0.0%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mref P L0: 68.6%  1.4% 21.7%  8.3%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mref B L0: 82.0% 15.8%  2.2%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mref B L1: 95.1%  4.9%\n",
      "\u001b[1;36m[libx264 @ 0x7fcc0c008e00] \u001b[0mkb/s:5020.83\n"
     ]
    }
   ],
   "source": [
    "!ffmpeg -framerate 12 -i ./img/scan_p_%04d.jpg -vf format=yuv420p  output.mp4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "25c0a17e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<video src=\"output.mp4\" controls  width=\"400\" >\n",
       "      Your browser does not support the <code>video</code> element.\n",
       "    </video>"
      ],
      "text/plain": [
       "<IPython.core.display.Video object>"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.display import Video\n",
    "Video('output.mp4', width=400)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 83,
   "id": "cced6e55",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2.4"
      ]
     },
     "execution_count": 83,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "frame_period = 300/180\n",
    "1/frame_period*4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "id": "898a419f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ffmpeg version 4.4 Copyright (c) 2000-2021 the FFmpeg developers\n",
      "  built with Apple clang version 12.0.0 (clang-1200.0.32.29)\n",
      "  configuration: --prefix=/usr/local/Cellar/ffmpeg/4.4_2 --enable-shared --enable-pthreads --enable-version3 --cc=clang --host-cflags= --host-ldflags= --enable-ffplay --enable-gnutls --enable-gpl --enable-libaom --enable-libbluray --enable-libdav1d --enable-libmp3lame --enable-libopus --enable-librav1e --enable-librubberband --enable-libsnappy --enable-libsrt --enable-libtesseract --enable-libtheora --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libxvid --enable-lzma --enable-libfontconfig --enable-libfreetype --enable-frei0r --enable-libass --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libspeex --enable-libsoxr --enable-libzmq --enable-libzimg --disable-libjack --disable-indev=jack --enable-avresample --enable-videotoolbox\n",
      "  libavutil      56. 70.100 / 56. 70.100\n",
      "  libavcodec     58.134.100 / 58.134.100\n",
      "  libavformat    58. 76.100 / 58. 76.100\n",
      "  libavdevice    58. 13.100 / 58. 13.100\n",
      "  libavfilter     7.110.100 /  7.110.100\n",
      "  libavresample   4.  0.  0 /  4.  0.  0\n",
      "  libswscale      5.  9.100 /  5.  9.100\n",
      "  libswresample   3.  9.100 /  3.  9.100\n",
      "  libpostproc    55.  9.100 / 55.  9.100\n",
      "Input #0, image2, from './img/scan_pa_%04d.jpg':\n",
      "  Duration: 00:01:14.58, start: 0.000000, bitrate: N/A\n",
      "  Stream #0:0: Video: mjpeg (Baseline), gray(bt470bg/unknown/unknown), 2048x2048 [SAR 1:1 DAR 1:1], 2.40 fps, 2.40 tbr, 2.40 tbn, 2.40 tbc\n",
      "Stream mapping:\n",
      "  Stream #0:0 -> #0:0 (mjpeg (native) -> h264 (libx264))\n",
      "Press [q] to stop, [?] for help\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0musing SAR=1/1\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0musing cpu capabilities: MMX2 SSE2Fast SSSE3 SSE4.2 AVX FMA3 BMI2 AVX2\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mprofile High, level 5.0, 4:2:0, 8-bit\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0m264 - core 163 r3060 5db6aa6 - H.264/MPEG-4 AVC codec - Copyleft 2003-2021 - http://www.videolan.org/x264.html - options: cabac=1 ref=3 deblock=1:0:0 analyse=0x3:0x113 me=hex subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=1 8x8dct=1 cqm=0 deadzone=21,11 fast_pskip=1 chroma_qp_offset=-2 threads=24 lookahead_threads=4 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=3 b_pyramid=2 b_adapt=1 b_bias=0 direct=1 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=2 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=crf mbtree=1 crf=23.0 qcomp=0.60 qpmin=0 qpmax=69 qpstep=4 ip_ratio=1.40 aq=1:1.00\n",
      "Output #0, mp4, to 'output_corrected_4x.mp4':\n",
      "  Metadata:\n",
      "    encoder         : Lavf58.76.100\n",
      "  Stream #0:0: Video: h264 (avc1 / 0x31637661), yuv420p(tv, bt470bg/unknown/unknown, progressive), 2048x2048 [SAR 1:1 DAR 1:1], q=2-31, 2.40 fps, 12288 tbn\n",
      "    Metadata:\n",
      "      encoder         : Lavc58.134.100 libx264\n",
      "    Side data:\n",
      "      cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A\n",
      "frame=  179 fps= 46 q=-1.0 Lsize=   13055kB time=00:01:13.33 bitrate=1458.4kbits/s speed=18.8x    \n",
      "video:13052kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.022872%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mframe I:28    Avg QP: 9.11  size:121326\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mframe P:61    Avg QP:18.43  size: 59969\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mframe B:90    Avg QP:19.67  size: 70108\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mconsecutive B-frames: 26.8% 14.5% 11.7% 46.9%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mmb I  I16..4: 48.4% 45.6%  5.9%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mmb P  I16..4:  0.8%  9.9%  3.2%  P16..4:  1.2%  0.3%  0.1%  0.0%  0.0%    skip:84.5%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mmb B  I16..4:  0.1%  1.6%  3.2%  B16..8: 12.2%  1.0%  0.4%  direct: 0.3%  skip:81.3%  L0:45.6% L1:52.5% BI: 1.9%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0m8x8 transform intra:49.6% inter:38.8%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mcoded y,uvDC,uvAC intra: 16.0% 0.0% 0.0% inter: 1.3% 0.0% 0.0%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mi16 v,h,dc,p: 95%  3%  2%  0%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mi8 v,h,dc,ddl,ddr,vr,hd,vl,hu: 44%  7% 43%  1%  1%  1%  1%  1%  1%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mi4 v,h,dc,ddl,ddr,vr,hd,vl,hu: 20% 13% 35%  6%  5%  5%  6%  5%  5%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mi8c dc,h,v,p: 100%  0%  0%  0%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mWeighted P-Frames: Y:0.0% UV:0.0%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mref P L0: 76.8%  1.4% 17.6%  4.3%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mref B L0: 89.6%  9.8%  0.6%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mref B L1: 95.9%  4.1%\n",
      "\u001b[1;36m[libx264 @ 0x7fa41a008e00] \u001b[0mkb/s:1433.56\n"
     ]
    }
   ],
   "source": [
    "!ffmpeg -framerate 2.4 -i ./img/scan_pa_%04d.jpg -vf format=yuv420p  output_corrected_4x.mp4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "id": "3e6fd527",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<video src=\"output_corrected.mp4\" controls  width=\"400\" >\n",
       "      Your browser does not support the <code>video</code> element.\n",
       "    </video>"
      ],
      "text/plain": [
       "<IPython.core.display.Video object>"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.display import Video\n",
    "Video('output_corrected.mp4', width=400)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3a68d6b6",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.8.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
