{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "import pandas\n", "import math\n", "import os\n", "import folium\n", "import geopy.distance" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "pos_df = pandas.DataFrame(\n", " data = [\n", " [\"walking\", \"50.81652\", \"8.77124\"],\n", " [\"city\", \"50.81774\", \"8.77236\"],\n", " [\"rural\", \"50.85028\", \"8.90166\"],\n", " ], \n", " columns = [\"scenario\", \"static_lat\", \"static_lon\"], \n", ")" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "def load_csv(name):\n", " scenario, mode, iteration = name.split(\".\")[0].split(\"_\")\n", " print(f\"Loading {scenario}, mode {mode}, iteration {iteration}...\")\n", " \n", " df = pandas.read_csv(name)\n", " df[\"recv_ts\"] = df[\"unix_nanosec\"] / 10**9\n", " df[\"payload\"] = df.apply(lambda row: bytes.fromhex(row[\"payload\"]).decode(\"utf-8\"), axis=1)\n", " df[\"channel\"] = df.apply(lambda row: row[\"payload\"].split(\"|\")[0], axis=1)\n", " df[\"sender\"] = df.apply(lambda row: row[\"payload\"].split(\"|\")[1], axis=1)\n", " df[\"gps_lat\"] = df.apply(lambda row: row[\"payload\"].split(\"|\")[2].split(\",\")[1], axis=1)\n", " df[\"gps_lon\"] = df.apply(lambda row: row[\"payload\"].split(\"|\")[2].split(\",\")[0], axis=1)\n", " df[\"send_ts\"] = df.apply(lambda row: int(row[\"payload\"].split(\"|\")[3]) / 1000, axis=1)\n", " df[\"delay_s\"] = df[\"recv_ts\"] - df[\"send_ts\"]\n", " df[\"scenario\"] = scenario\n", " df[\"mode\"] = mode\n", " df[\"iteration\"] = iteration\n", " \n", " return df" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loading city, mode 3, iteration 0...\n", "Loading city, mode 3, iteration 1...\n", "Loading city, mode 3, iteration 2...\n", "Loading city, mode 0, iteration 1...\n", "Loading city, mode 0, iteration 0...\n", "Loading walking, mode 0, iteration 2...\n", "Loading walking, mode 0, iteration 1...\n", "Loading walking, mode 0, iteration 0...\n", "Loading rural, mode 3, iteration 0...\n", "Loading rural, mode 0, iteration 0...\n" ] } ], "source": [ "# load and merge all data\n", "dfs = [load_csv(name) for name in os.listdir(\".\") if name.endswith(\".csv\")]\n", "df = pandas.concat(dfs)\n", "df = pandas.merge(df, pos_df, on=\"scenario\", how=\"inner\")" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "# compute distances\n", "df[\"distance\"] = df.apply(lambda row: float(geopy.distance.distance(\n", " (row[\"gps_lat\"], row[\"gps_lon\"]),\n", " (row[\"static_lat\"], row[\"static_lon\"])\n", " ).km), axis=1)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
unix_nanosecpayloadrssisnrrecv_tschannelsendergps_latgps_lonsend_tsdelay_sscenariomodeiterationstatic_latstatic_londistance
4101576940039423670773eval0|sender|8.901639149976264,50.850471349016...-56101.576940e+09eval0sender50.850471349016768.9016391499762641.576940e+090.745671rural0050.850288.901660.021337
4111576940054425122206eval0|sender|8.901673044751822,50.850309930046...-5291.576940e+09eval0sender50.8503099300467068.9016730447518221.576940e+090.732122rural0050.850288.901660.003454
4121576940069425578433eval0|sender|8.901673044751822,50.850309930046...-4991.576940e+09eval0sender50.8503099300467068.9016730447518221.576940e+090.733579rural0050.850288.901660.003454
4131576940084426011375eval0|sender|8.901673044751822,50.850309930046...-49101.576940e+09eval0sender50.8503099300467068.9016730447518221.576940e+090.734011rural0050.850288.901660.003454
4141576940099426344525eval0|sender|8.901673044751822,50.850309930046...-5591.576940e+09eval0sender50.8503099300467068.9016730447518221.576940e+090.734344rural0050.850288.901660.003454
\n", "
" ], "text/plain": [ " unix_nanosec payload \\\n", "410 1576940039423670773 eval0|sender|8.901639149976264,50.850471349016... \n", "411 1576940054425122206 eval0|sender|8.901673044751822,50.850309930046... \n", "412 1576940069425578433 eval0|sender|8.901673044751822,50.850309930046... \n", "413 1576940084426011375 eval0|sender|8.901673044751822,50.850309930046... \n", "414 1576940099426344525 eval0|sender|8.901673044751822,50.850309930046... \n", "\n", " rssi snr recv_ts channel sender gps_lat \\\n", "410 -56 10 1.576940e+09 eval0 sender 50.85047134901676 \n", "411 -52 9 1.576940e+09 eval0 sender 50.850309930046706 \n", "412 -49 9 1.576940e+09 eval0 sender 50.850309930046706 \n", "413 -49 10 1.576940e+09 eval0 sender 50.850309930046706 \n", "414 -55 9 1.576940e+09 eval0 sender 50.850309930046706 \n", "\n", " gps_lon send_ts delay_s scenario mode iteration \\\n", "410 8.901639149976264 1.576940e+09 0.745671 rural 0 0 \n", "411 8.901673044751822 1.576940e+09 0.732122 rural 0 0 \n", "412 8.901673044751822 1.576940e+09 0.733579 rural 0 0 \n", "413 8.901673044751822 1.576940e+09 0.734011 rural 0 0 \n", "414 8.901673044751822 1.576940e+09 0.734344 rural 0 0 \n", "\n", " static_lat static_lon distance \n", "410 50.85028 8.90166 0.021337 \n", "411 50.85028 8.90166 0.003454 \n", "412 50.85028 8.90166 0.003454 \n", "413 50.85028 8.90166 0.003454 \n", "414 50.85028 8.90166 0.003454 " ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "df[(df[\"scenario\"] == \"rural\") & (df[\"mode\"] == \"0\")].head()" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "def plot_map(scen_df, db_norm=150, snr_norm=4, zoom_start=14):\n", " scen_pos = (scen_df.iloc[0][\"static_lat\"], scen_df.iloc[0][\"static_lon\"])\n", " m = folium.Map(\n", " scen_pos, \n", " zoom_start=zoom_start, \n", " tiles=None, \n", " control_scale=True,\n", " )\n", " \n", " # add Tiles Layers\n", " folium.TileLayer(tiles='StamenTerrain', name=\"Terrain\").add_to(m)\n", " #folium.TileLayer(tiles='OpenStreetMap', name=\"Streets\").add_to(m)\n", " \n", " # add center marker\n", " folium.Marker(location=scen_pos).add_to(m)\n", " \n", "\n", " # Plot groups for modes\n", " for mode, mode_df in scen_df.groupby(\"mode\"):\n", " if mode == \"0\":\n", " name = \"Medium Range\"\n", " color = \"orange\"\n", " elif mode == \"3\":\n", " name = \"Long Range\"\n", " color = \"red\"\n", " else:\n", " name = \"Unknown Mode\"\n", " color = \"black\"\n", "\n", " # get longest distance point for group\n", " max_dist = mode_df.iloc[mode_df['distance'].values.argmax()]\n", " \n", " # create group for mode\n", " group = folium.FeatureGroup(name=f'{name}').add_to(m)\n", " \n", " # add line to max distance point\n", " folium.PolyLine([\n", " (float(max_dist[\"static_lat\"]), float(max_dist[\"static_lon\"])),\n", " (float(max_dist[\"gps_lat\"]), float(max_dist[\"gps_lon\"])),\n", " ], color=\"black\").add_to(group)\n", " \n", " # plot measurement points\n", " for i, row in mode_df.iterrows():\n", " popup = folium.map.Popup(\n", " html=\"{:.2f} km\".format(row['distance']), \n", " color=\"red\",\n", " sticky=False,\n", " show=False,\n", " )\n", " \n", " # enable max distances per default\n", " if row.equals(max_dist):\n", " popup.show = True\n", " popup.options[\"autoClose\"] = False\n", " popup.options[\"closeOnClick\"] = False\n", " \n", " measure_details = folium.map.Tooltip(text=\"RSSI: {}
SNR: {}\".format(row['rssi'], row['snr']))\n", " folium.CircleMarker(\n", " location = (row[\"gps_lat\"], row[\"gps_lon\"]), \n", " radius = (row[\"rssi\"] + db_norm) / db_norm * 25,\n", " opacity = 0.7,\n", " fill = True, \n", " color = color, \n", " tooltip = measure_details,\n", " popup = popup,\n", " ).add_to(group)\n", " \n", " # add legend\n", " folium.map.LayerControl('topright', name=\"Test\", collapsed=False).add_to(m)\n", "\n", " return m" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ "" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "rural_map = plot_map(df[df[\"scenario\"] == \"rural\"])\n", "rural_map" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
" ], "text/plain": [ "" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "city_map = plot_map(df[df[\"scenario\"] == \"city\"], zoom_start=14)\n", "city_map" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "def render_png(m, png_path, delay=1, width=1200, height=1000):\n", " from selenium import webdriver\n", " from folium.utilities import _tmp_html\n", " import time\n", "\n", " options = webdriver.firefox.options.Options()\n", " options.add_argument('--headless')\n", " driver = webdriver.Firefox(options=options)\n", "\n", " old_options = m.options\n", " m.options[\"zoomControl\"] = False\n", " \n", " html = m.get_root().render()\n", " with _tmp_html(html) as fname:\n", " driver.get('file:///{path}'.format(path=fname))\n", " driver.set_window_size(width, height)\n", " time.sleep(delay)\n", " png = driver.get_screenshot_as_png()\n", " driver.quit()\n", " \n", " m.options = old_options\n", " \n", " with open(png_path, \"wb\") as png_file:\n", " png_file.write(png)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [], "source": [ "# export maps as html and png\n", "rural_map.save(\"../gfx/rural.html\") \n", "city_map.save(\"../gfx/city.html\")\n", "\n", "render_png(rural_map, \"../gfx/rural.png\", width=800, height=600)\n", "render_png(city_map, \"../gfx/city.png\", width=800, height=1000)" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "image/png": "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ZP369SxcuBCtNRkZGbzwwgsRv2dHIipy7kqplYCnKU2/An4HrAJuAiYCrwFDgEXAl1rrVxxtPA+8r7X2mSVsq5y7IISKyLkL8UDcyblrrc/2dkwpNR940xFGFSml7EAmYAHcF4nu59gnCIIgRIGYS6ID/wSmAyilTgWSgUPAO8AcpVQXpdRgYBhQFDUrBUEQOjmxmAN5AXhBKbUVaASudUQjXymlXgdKARtwo9a6KYp2CoIgdGpizoForRuBq70c+x1GjkQQBEGIMrE4hCUIQphITExk7NixjB49mh/96EdUVVWFre0HH3zQpWPlibFjx7qkSdoDpRRXX32y72mz2cjKygpaan7atGk4J96cf/75YX1nTjqKJL04EEHowKSmplJSUsLWrVvp1asXzzzzTFDXNzWFNkr89ddf09TUxOrVq6mrq/N4TigSJr5IS0tj69atHD9+HICPPvoIs7lt6tnvvfceGRkZ4TCvFR1Bkl4ciB8q6ipYf2C9LGUrxD3u0uctF4FauHAhL774ImD0ju+66y7GjRvHsmXL+Mtf/sLEiRMZM2YMl112GfX19X7vtXTpUq655hrOOecc3n77bdf+adOmccsttzBhwgSefPJJKisrueyyy5g4cSITJ05kzZo1ABQVFTF58mQKCgo4/fTT+eabbwJ6xvPPP59///vfLhvchRbr6uqYN28ehYWFFBQUuOw6fvw4c+bMYeTIkVxyySUuB+R8F4cOHXLJ3zt57LHHePDBB13PdOuttzJhwgRGjhzJunXruPTSSxk2bBj33nuvX5vjWZJeHIgPKuoqWFyymDe3v8niksXiRISIE6kOS1NTEx9//DEXXnhhQOf37t2bDRs2MGfOHC699FLWrVvHpk2bGDlyJM8//7zf61977TXmzJnDlVdeydKlS5sda2xspLi4mNtuu42bb76ZW2+9lXXr1rF8+XKuv/56AEaMGMHq1avZuHEjDz30EL/85S8DsnvOnDm8+uqrNDQ0sHnzZk477TTXsd/97nfMmDGDoqIiVq1axR133EFdXR2LFy+ma9eufP311/z6179m/fr1Ad3LneTkZIqLi/n5z3/ORRddxDPPPMPWrVt58cUXXZLy3ohnSfqYS6LHEpZaCza7DXM3M5ZjFiy1FpF0FyKGs8Nis9tISkhi/tj5bf57O378OGPHjsVisTBy5EhmzpwZ0HVXXHGF6/PWrVu59957qaqqora2lnPP9a1jWlxcTGZmJgMGDMBsNjNv3jyOHDlCr169WrW9cuVKSktLXds1NTXU1tZSXV3Ntddey44dO1BKYbUGthJofn4+ZWVlLF26lPPPP7/ZsRUrVvDOO++48jYNDQ3s3buXzz77jJtuusl1fX5+8OKpTsecl5dHbm4uOTk5AAwZMoR9+/bRu3fvVtdMnz6dI0eOkJ6ezm9+8xvX/qeeeoq33noLwCVJ37t371aS9B999BFgSNL/85//BAxJ+ttvv931vE5JeoDa2lp27NjBGWecEfTzeUMiEB+Y080kJSRhOWYhKSFJViMUIop7h8Vmt2GpbXudrDMHsmfPHrTWrhxIUlISdrvddZ5T9txJWlqa6/N1113H008/zZYtW3jggQdanduSpUuXsm3bNgYNGsTQoUOpqalxKd62bNtut/Pll19SUlJCSUkJFouF9PR07rvvPqZPn87WrVv517/+5fee7lx44YXcfvvtzYavwND9W758uetee/fuDVgpwN/7csrTJyQkuD47t73lelatWsWePXsYO3asa3jJXZJ+06ZNFBQUtFmS3vm8O3fu5H/+538Cet5AEQfig+y0bOaPnc+lp14alt6gIPgikh2Wrl278tRTT/HHP/4Rm83GwIEDKS0t5cSJE1RVVfHxxx97vfbYsWPk5ORgtVr9rg1it9t5/fXX2bJlC2VlZZSVlfH222+3GsZycs4557Bo0SLXdklJCQDV1dWuBLgzNwOGrPpZZ53l04Z58+bxwAMPkJeX12z/ueeey6JFi5xirGzcuBGAM844g3/84x+AEW1t3ry5VZt9+vTh4MGDHD58mBMnTvDuu+/6tCFQ4l2SXhyIH7LTshnfZ7w4DyHiRLrDUlBQQH5+PkuXLqV///5cfvnljB49mssvv9w1zOGJ3/zmN5x22mlMmTKFESNG+LzH6tWrMZvN9O3b17XvjDPOoLS0lPLy8lbnP/XUUxQXF5Ofn8+oUaP485//DMCdd97JPffcQ0FBQbOednl5OUlJvkfe+/Xr5xqScue+++7DarWSn59Pbm4u9913HwDz58+ntraWkSNHcv/993tcBthkMnH//fdTWFjIzJkz/b6HYIhnSfqoiCm2JyKmKEQLEVMMP08//TQDBgwIeDJAZ6C+vp7U1FSUUrz66qssXbq02cw3f8SdmKIgCEIoLFy4MNomxBzRlKQXByIIghDHTJ06lU2bNkXl3pIDCZBlxftYVrwv2mYIgiDEDOJABEEQhJCQISw/OKOO/UePN9uePaG/12sEQRA6AxKBCIIgCCEhDsQPsyf0Z/aE/vTrmcqU2g+ZnfiZRB9C3NCZ5NzdRQQjzXXXXcfgwYMZO3YsY8aM8VmI2ZERByIIHZjOJOfe3jz66KOUlJTwxBNP8POf/zza5kQFcSCBsHEJsxM/ozCjFqr2wsYlxo8gxBGdQc69JWVlZcyYMYP8/HzOOuss9u7dCxgRxE033cTpp5/OkCFDeOONNwBDimXBggWMGDGCmTNncv7557uOeaOlHPtDDz3ExIkTGT16NDfccINLOmXatGncddddFBYWcuqpp7J69WrAKAS8/PLLGTVqFJdccgmnnXaaa0GrFStWMHnyZMaNG8fs2bNdsiSxgjgQQYghrOXl1BcXY/Ug+9EWOouce0t+8YtfcO2117J582bmzp3bTOKkvLyczz//nHfffZe7774bgDfffJOysjJKS0t5+eWXWbt2rd97tJRjX7hwIevWrXMtbuWum2Wz2SgqKuKJJ57g17/+NQDPPvssPXv2pLS0lN/85jcuOflDhw7x29/+lpUrV7JhwwYmTJjA448/HtJ7iBQyCysQCuYa/zqjDue2IIQRa3k5lc88CzYrJJnIunEBJocseKh0Njn3lqxdu5Y333wTgGuuuYY777zTdeziiy8mISGBUaNGuRZn+vzzz5k9ezYJCQlkZ2czffp0r23fcccd/PKXv2T//v3NHM2qVat45JFHqK+v58iRI+Tm5vKjH/0IgEsvvRQw5NjLyspc97z55psBGD16tEtO/ssvv6S0tJQpU6YAhuOdPHlySO8hUkgEIggxgtViAZsVk7kf2KzGdhvpjHLugeIuux6KJuCjjz7K9u3b+cMf/sC8efMA4z0uWLCAN954gy1btvDTn/60me3OewYqxz5z5kzXuyktLQ0o+mtPxIEEQ8FciT6EiGEymyHJhNWyH5JMxnaY6Exy7u6cfvrpLonzJUuWMHXqVJ/nT5kyheXLl2O32zlw4ACffvqp33ssXLgQu93Ohx9+6HIWmZmZ1NbW+s2fOO/5+uuvA1BaWsqWLVsAQ6Z9zZo17Ny5EzBWJNy+fbvf9toTcSCCECOYcnLIunEBGbNmhWX4qiUdXc69vr6efv36uX4ef/xxFi1axN/+9jfy8/N5+eWXefLJJ33af9lll9GvXz9GjRrF1Vdfzbhx41zy6N5QSnHvvffyyCOPkJGRwU9/+lNGjx7Nueeey8SJE31eC7BgwQIqKysZNWoU9957L7m5ufTo0YOsrCxefPFFrrzySvLz85k8eTLbtm3z2157InLughAhRM49/LSHnHttbS3p6ekcPnyYwsJC1qxZQ3Z25NYDampqwmq1kpKSwq5duzj77LP55ptvSE5Ojtg93RE59xjCWl6O1WLBZDaHvQcpCJ2d9pBzv+CCC6iqqqKxsZH77rsvos4DjMhp+vTpWK1WtNY8++yz7eY82oo4kDASiVk0giC0L4HkPcJJt27diNdREsmBhJFIzKIR4puOPkQsxDdt/fsUBxJGIjmLRog/UlJSOHz4sDgRISbRWnP48GFSUlJCbkOGsMKIcxaN5EAEgH79+rF//34qKyujbYogeCQlJYV+/fqFfL04kDBjyskRxyEAYDKZGDx4cLTNEISIIUNYgiAIQkjEnANRSo1VSn2plCpRShUrpQod+5VS6iml1E6l1Gal1Lho2yoIgtCZiTkHAjwC/FprPRa437EN8ANgmOPnBmBxdMwTBEEQIDYdiAa6Oz73AL5zfL4IeEkbfAlkKKUk2SAIghAlYjGJfgvwoVLqMQwHd7pjvxnY53befse+VgI7SqkbMKIUBgwYEFFjBUEQOitRcSBKqZWAJ32AXwFnAbdqrZcrpS4HngfODqZ9rfVzwHNgaGG10VxBEATBA1FxIFprrw5BKfUScLNjcxnwV8dnC9Df7dR+jn2CIAhCFIjFHMh3wJmOzzOAHY7P7wA/dszGmgRUa63Du+5nmFhWvI9lxfv8nygIghDHxGIO5KfAk0qpJKABRy4DeA84H9gJ1AM/iY55giAIAsSgA9Fafw6M97BfAze2v0WB44w69h89zqgD/6JoXzJ7+l/M7An9/VwpCIIQf8TiEJYgCIIQB8RcBBLPzJ7QHzYuoaj2CGmqktyMHhQmfgYbkbXUBUHocEgE0gYkWS4IQmcm4AhEKdUT6AscB8q01vaIWRXPFMylsADYuMS1LQiC0BHx6UCUUj0wEtdXAslAJZAC9FFKfQk8q7VeFXErYwz3ZLn7tiTLBUHoTPiLQN4AXgKmaq2r3A8opcYD1yilhmitn4+UgbFObdMh9tRVk5Hch2Z1jh4ij4q6Ciy1FszpZrLTPBXiC4IgxA8+HYjWeqaPY+uB9WG3KA5wRhp/XbuBDQffZEBaF47Zk6io69vMMbhHJhV1FSwuWYzNbiMpIYn5Y+eLExEEIa4JOImulMpXSl2olLrU+RNJw+KBqsYD2LUNczczNrsNS613ZRVLrQWbPbBzOyoVdRWsP7CeirqKaJsiCEIYCCiJrpR6AcgHvgKcyXMNvBkhu+KCjOQ+JKgkLMcsJCUkYU43A55zJNVWRVJC63M7CxKBCULHI9BZWJO01qMiakkc0sOUxZmnXEXBEO03r9HDlMUFI+cHlAOxlpdjtVgwmc0dZn119wjMcsyCpdbiegeSGxKE+CRQB7JWKTVKa10aUWvihOYRRjq796WyGyuzJxjHnTkST7Oz/H1BWsvLqXzmWbBZIclE1o0LOoQTMaebPUZgEpkIQvwSqAN5CcOJVAAnAIUhT5UfMcs6KVaLBWxWTOZ+WC37jUikAziQ7LRs5o9tHYH5ikwEQYhtAnUgzwPXAFs4mQPptPiKMDydFwwmsxmSTFgt+yHJZGx3ELLTsls5B2+RiSAIsU+gDqRSa/1ORC0RADDl5JB144IOlwPxhrfIRBCE2CdQB7JRKfUP4F8YQ1gAaK079SyssFeeO+RPTAVzO7zjcMdTZCIIQuwTqANJxXAc57jt6/TTeIWT+JpJJbOsBKFjEpAD0VrL6n+RxCm8WLW3+XacCDH6mkkls6wEoePisxJdKZWilLrWUYGulFJ3KqXeVUo9qZTKbC8jhdjGV5W9VOALQsfFXwTyEmAF0oDbgK3A08D3gReBCyJpXMwS7gjB2U6cRR5OfM2kkllWgtBx8edARmmtRyulkoD9WuszHfs/UEptirBtQgziKZ/hayaVzLIShI6LPwfSCKC1timlvmtxrCkyJsUwkc5VxHjk4Suf4WsmlcyyEoSOiT8H0k8p9RRG5bnzM45tGYvoZEjVuCAI7vhzIHe4fS5ucazldscnznMVbUXyGYIguONvQam/t5chQuwj+QxBENzxtyb6vzAKBj2itb4w7BbFA34ij2XF+1hXdsS1PXFQrw6zXrrkMwRBcOJvCOsxx7+XAtnAK47tK4EDkTJKiA2kglwQBF/4G8L6D4BS6o9a6wluh/6llOp8ORAfuEcd2buXk9Zg492E6QBstVSzruxIXEUiUkEuCII/Al0TPU0pNcS5oZQajFFcKESIaK8fLhXkgiD4I1AxxVuBT5VSuzGm8A4EboiYVXGE++qEZzWspGdaMtVp1ZAGI5LXAzA4M43CQb0CnrUVC71/mXElCII/AhVT/EApNQwY4di1TWt9wtc1Qui0d71FsNXl3q7pqHTENeoFIRz4m4X1fa315wAOh7GpxfHuwACt9dbImRjbuK9OWNPzcs6d0B82LqGo7Ag9wIg8qvZCVW3A9SPt2fsPpbo8FiKk9qKjrlEvCOHAXwRymVLqEeADYD1QCaQA3wOmYwxl3RZRCzsh4S+uIHAAACAASURBVK638BUthBLtfLd7Mz23VdBt4BB2d6np0BXpHXWNekEIB/5mYd2qlOoFXAbMBnKA48DXwP85o5NgUUrNBh4ERgKFWutit2P3AP+DobV1k9b6Q8f+84AngUTgr1rrh0O5d6Rovk76GexPPw7AnqZUBlb9M6gcCISv3sJftBBstGMtL6fHKx8yrHIPTWv3cOiivA6dH+nIa9QLQlvxmwPRWh8B/uL4CRdbMWpL/s99p1JqFDAHyAX6AiuVUqc6Dj8DzAT2A+uUUu9orUvDaFOHxF+EEWy0Y7VYSFUmRo2exrE9uyjoOTPs0Ucs5Vc62xr1ghAMgc7CCita668BlFItD10EvOrIt3yrlNoJFDqO7dRa73Zc96rj3JhzIM0jEcf2hF+4jjvrRQKpCWnWRogEEmEEE+04e+RdDlbRpVs2WUPzQrbNE7GYXzHl5IjjEAQPRMWB+MAMfOm2vZ+Tqr/7Wuw/zVsjSqkbcEwzHjBgQJhNjC9612iu11OozIC+Q/LJTstuUw8/XD1ybzObRPFXEOKHiDkQpdRKDPmTlvxKa/12pO4LoLV+DngOYMKECV61vMKNe8Qwe0J/lhXvY1nxPtfndWVHOFpnpabBytG6Rq+RiHttSct2g8E5gyjRZiU7yUTvG/OooO09/Lb2yH3NbJL6E0GIH/xN473U13Gt9Zs+jp0dgj0WwP1bsp9jHz72C17wNIPIkqCC6uFHogbC18wmUfwVhPjBXwTyIx/HNODVgYTIO8A/lFKPYyTRhwFFGNXvwxwSKhaMRPtVYb53yLSMGO58wyiXyemR2uz4I7PGUPTWIr5trGNwd6M6fVnTGdz5xqZmkYjHPEoIeJpBZE5XAffwI1UD4W9mkyj+CkJ84G8a708icVOl1CXAIiAL+LdSqkRrfa7W+iul1OsYyXEbcKPWuslxzULgQ4xpvC9orb+KhG0dCU/5imwIuIdvtViobzhGfWZXuh465ooU/EUl/nIsMrNJEDoGSmvvKQKl1I+AzVrrPY7t+zFqQvYAN2utv20XK9vAhAkTdHFx+wgHuyKGxM+M7aYzjG1HdToA294FwFJ1nKP1jazrcjrHrU1s7PVDeqaZYkqx97tdm9nw8N1ga4KkRMbd/TBZXbN8RiWxOItKEITgUEqtb6HA7hF/ary/w6g+Ryl1AXA1MA9jqOnPbTVSiBzBqPl6O7c83cqmC4ZTfc4ENl0wnPJ0a7P8BTbHthuBqvhGW21YEIS24y8HorXW9Y7PlwLPa63XA+uVUgsia1p8sax4HwP3/fOk9lXFZmZ3XQsDJgNuFegjLgCMuclm4OumM/i67AjnhDHyCCYK8HWuOd1MY2Z3Su02khK6Y043YzJrn/mLQGZRSZQiCB0Dfw5EKaXSgXrgLOBZt2MpEbNKaBPB1FL4Otd9RpQpwcTmys0oFLk/mU3G0UaP+Qt7xUFmHM3B1qcnuaPO9HhfqfUQhI6BPwfyBFAC1ABfOzWrlFIFQHmEbYsL3Gdg7U8/F8o+JPPIdoZ0BbqbYe9a46e7oyee4ShsdOhizY6ATcHUUvg71/nF/sd1f6T0iFH4P6r3KG6bcFurL/2WORP73SNhaGvHILUegtAx8DcL6wWl1IfAKTSXci8HIjJDS2g7wdRSBHKupdbCscZjpCalorXmWOMxj1HDgV1bwNZEQt9s7N9VcGDXFvoOzW+TfdEkljS5BCEW8VdIOBCo0lpbHNvTgYsxZmE9HXnzYp+WNRuFM3/BsuJ9HNr3T6iCPf3vaj4Ly02Rt2X9SNFbi4w2BvXyem6geZJgain8nWtON9MtuRv7ag0buiV38xg19BmahyUpEft3FZCUSB8fOlmxXusheRpB8I+/IazXgUuAaqXUWGAZ8HtgDEY+5PrImidEG2t5Od0tFm7tfw1bB1WgUORlGY5h/YH1zXrnfYfmw90Pc2DXFvoMzfMYfbQ3oUYR3tY8kahEEE7irw5ks9Y63/H5McCutb5TKZUAlDiPxTLRqAP52xqjPMac0ZXTj31ArrkHe/pf7DV6cEUeDV80PzDiAorKjrCn/8WuKKW82vj3kVljIvEIzfBWiR4vvfNQ7bSWl7P3icfYVvkVTQnw9UV5XDvDWDctHp5bENpKuOpA3PXWZwAfA2it7W2wTQgzkaqp8Fbz4a/WI1ZqPAKtSWmJ+5ong9L6c5VjzZNQ2xOEjoq/IaxPHNIi5UBP4BMApVQO0Bhh2+IWc0ZXflz5GMlHE6gxZVGYkUhh4mewEY+rErpyHlzQ/EDBXAoLjAVRvOlrTR1piliv2Jtmla9ZVLEUnYQ628vbmicye0wQmuPPgdwCXIGxlO33tdZWx/5s4FeRNEwIjEjWVHjTrPI1iyqWajxCne0VynMLQmfE3zReDbzq4dAm4MqIWBTHOGdbDex+hLSkQeT27QE1FuNn+j2tL3DOzKraa/zbokbEHWfOo+VsrIo6U0R7xd7W/vA2iyrWeumhzvYK9rkFoTPibxpvd+BGDNWNd4CPgIXAbRhOZEmkDRR8E2u94lizRxCEyOFvFtbbwFFgLYaUySkYifWbtdYl7WJhG2nPWVjNaFH34bOOw0ONSLsQrfsKghDTBDoLy18OZIjWOs/R4F8xkukDtNYNYbBRECLOqr2rKKooojC7kOkDpofURiRWZRSEjoA/B+JMmqO1blJK7Rfn4R9XXcclvwhsffMAKs6bqf22MWJw2ZdRa+zooJHIqr2ruGf1PWg0b+14i99P/X3QTiRSqzIKQkfAnwMZo5SqcXxWQKpjW2Hk2LtH1DrBL52xMrqirsKlDJyXldfquZ3v5NO9n6LR9OjSg+oT1RRVFAXvQFqs316xawsHE75zTQ7obO9eENzxNwsrsb0M6Qg4e/YDd7zk2IaBNI9EvOVAisqO8O2hOupP2KgafrnrfICB+/5J97pGeh35D5aKBCxlRygc1IuKU88KqubC2Wb3OqOEp4h0AAqnhyny8BTJhDm6qair8KkM7F6HcqDuAADVJ6pRKAqzC4O+n3stzHFt5Y2jH1GzPZnGpkZQkJyQHPV6F0GIFv4iECGGiUbNhd/10BtrsLTQyAon/pSB3d8JQGFOIUdPHKUwu5CRvUe20u/yh3tNSEWXI9RU/QdzNzObD24GBflZ+VGvdxGEaCEOJIwUJnwDwHHb4Wbb4Hv2leXLZZiBXo1NHGuwsb0siYG1aRRe8gvjvMReFJUdIfVYAuaMVMyOyvVgay6ca7V/pSoByB3kXS3XE97yAbufN5T9uw4ws7h6C7aKz0iqO8T8oZeSvfuz5o14i0QCjFT8KQO3fCcXfu9ClwhiqBXyzpqQvnUVJJWswXLMQrfkbqCImXoXQYgG4kDiGH81F+HOj1Ts2sLRYxV0GziULgerjEjELQqx2Oo41mSlqwmOaRsWazXh7pNnp2Vz28TbvOZAvL2TYKI1b++tZdvOdiUHInRWxIGEkwGTAUjdshyr3U6RfbibzpUHHL1tZ9+1qOwIAFc4Iw+38woLjJyKBSh066V7q4z22ON2XJdL8HmJiroK/nH0I0bW7SNx6z5GJKVxfPXjHN43mSZ6A1D13R6+VhZUUw0JCQmY/Gp10roaP4BIxF81uKfjgUZr/iKVlm2L4xA6M+JAOijhzo9Yai3UZCRTOWc61i2lmBNS6WavaXaODU3e0QTMSb041F1hJTZEm51rmvws+zLK060+IwZ/761ldNIZZ8EJghNxIOFk4yscrD1BD2XCriBn9xsc3JfIf5rO8L2SoKO3XVjg+bCrliT9XAD2BLA6oc8edwgzopztVViKGLPhAOkqjVRTMgV5iew7YuR8BuYUoj7ZB00VDDUlk9MnAUZc4Puezv0RqkVpmbfJv3EBJj+rLwaqNHzZqZexfPvymFAeFoRoIA4kAiTZ6kE30dV6mCbTKe1z0xZfwM7x+leKN5KR1Mf3F5u/L++NS8gG5o+dT/k3f6Bbsp3GRM2JqnoyKqtdp2VUNzEiMYP6TBNda01kVDcZOs5RpGUdR8u8TUuCURreemhrzCgPC0I0EAcSTua9zykAjw6jsaGOXcN/RuElv2B2G5ttue56MOuiD0wb3ca7N2+v9w/upHL3s9Tt3gLJ6ZimXccQxxeytbycrp9tpmt9E+QMxzTtOgi0ajtCVfDe1jTxRaBKw6MzR/PNkW9kJpbQafEpptgRaFcxxbcXwuFdsK+IJq2p6T6Mnhm9oODqkL8g3QsK15UdYeKgXs0diBdJ+GVNZzBw3z85WtdIaZ8f0a+nsRBVINe2GlZyO15UdoTvup5OxvpXsfVI5+hp85q1a135LNbKakzTrmvd09/YQrw50HfSxuGtcGpZSQ5E6AyES0xRCIWuvUi02+jZNTmszbZyHlHC2vsUNk78KQD9Whwznb0AU1vbD7N4obe1PULB0ywscRxCZ0UikHDi7Cn/989QfwSGTjem9obQc3ZGHiu+MuQ4Tj/2AQDpk64FPAxjufXS3cUSv/qumrquZp8ijO7ijz6fy5foo68owXls27vN93tIsDdLelfvIeuyqZgSHXmWGsca5Bc97dlODwQ77CcIQuARSAAT9QWh/XBPemNrwuqWpI8FrOXl1BcXYy0vj7YpghB1JAKJBGGckuqMDtLqjd537qi8Vm2750ngZNTy0+5rAe+RRUupeY95En8EsSxvIDkQj3IpRb8zDnY3+7+Hg7A8WyC2ibS70AGJ6RyIUmo28CAwEijUWhc79s8EHgaSgUbgDq31J45j44EXgVTgPYxVETu292sjCXsrSNx/kP05n3AwzUZWNWQPzYvpLz138cJwLuBkOnwQU+UBrOakoNp0z8cEMiVYFp8SOhNRiUCUUiMBO/B/wO1uDqQAOKC1/k4pNRr4UGttdhwrAm4C/ovhQJ7SWr/v715RW9I23PjIQzh72eXVxr9H6xrpY9nJZSueRzc1YtN1HM1MxJZmYkS/SQy45fZmX25hyRO0x6JUIdxjWfE+TIcPUrj6n0FHDi0jjozZs6ha9obXdiRCEToKMZ0D0Vp/rbX+xsP+jVrr7xybX2EsYNVFKZUDdNdaf+mIOl4CLm5Hk+OO7IpvUU1N1Gd0I8HaRI/qJuxo6o5WGj3pToSp8oBbXsXqen5/+QxnxJHQowfWigpsFRVk3biAjFmzPDqH5vkba6d7z0LnI5an8V4GbNBan1BKmYH9bsf2c1KDsHPgodfdssDw1pmnurZTu0wgfftqUqoOcYwmEutsnLK7kS7JO+Dbj2HChFbthNs+ILyRSQhtzJ7QH6s5icpta5sVEwYSLZjMZuyNVuo//Q9oTc2HK0jNz6frBM8ds1CKFgUhnomYA1FKrQSPat6/0lq/7efaXOAPwDkh3vsG4AaAAQMGhNJE3HP8eyPp+4eHaXj/b6ToExzb+DWpqSmkkAq22BA5bC885VXqi4v95jNMOTl0P/cc7DU1JA8Zgr2m2qcUSqTyN4IQq0TMgWitzw7lOqVUP+At4Mda612O3Raa16z1c+zzdu/ngOfAyIGEYkc80TKCOLndn65jnqBbeTmVe/8f2Jqgz3BMWT2MyKBgbvMIoQ3Rgte6kCBk2oM6L0haFhMGGi2k5udTu/pz7DXVAUUV4SxaFIRYJ6aGsJRSGcC/gbu11muc+7XW5UqpGqXUJIwk+o+BRVEyM+4w5eSQddnUkxIjFZ9E26SoE2i0IFGFIHgnWrOwLsFwAFlAFVCitT5XKXUvcA+ww+30c7TWB5VSEzg5jfd94BeBTOPtMLOwPOGptx5IRXjVXqjYDF17Q70hxe76nJ0fUJ2FE6/1FolelrL1Zt/bC41/g6j18El7zAoThA5KTNeBaK3fwhimarn/t8BvvVxTDIRPWlaIe6TmQhCii1SiR4CI6C95ylW4V3/vXWvobnmoCPepWxXI5wBw3cMZebS0w4lTD8upg+XEeb4XvauWel1eZ1EFWhkfyQhFoh8hzonpOhBBaCtScyEI0UcikDASCf0lnz3qlj3djUtORiJukUdQ9oSrB+/JNmgdgfjJjTgjj6YjewBI7DXQOG3SLCofOjmzrFUdhzOn0lK511Ouxe2dhURLja9AdMEEIYaJ6RyIILSVVjPL2ikHEo68i+RuhI6CRCARIOI5EG/HvPR8Q7LH2wwvH7mWkNv1dtzt8+7nfwLAkP/5W2BRmbccjHP/t59B7UGY+v8Cjras2TOa512m5WDq3cP7vQJVGw6HE5G8ixBGJAciUFFXwfoD66m2VkbblA5Bq7xLCGuVSO5G6EjIEFYECPvqd4FEHy167BV1FSwuWYzNbiMpIYkLmrJhY/fAe6gt8ipF9uGk1VvI7evIHThnS2UMaD3Tq0UuxmO73p7DWaOyd+3JGpW9axnStcGoUfH0vI5zWtnt6b4v/MD4d/AZJ4/VWDzb2iKaMTVVQ/UerGBUpU+7DtxnfgVQZW+qqg6vXlaoFf+CEAbEgXRQLLUWbHYb5m5mLMcsWKzVZCd393huW8fkreXldN22BWtWHyA8zrPC3oDFXodZmTwKqkUDU+8eRt4lozDkd2Xq3YOsGy+RHIjQIZAcSCzja6z/7YXsPlTH8ZQ+5Pbt0Wr8veLUs4wI5NB2klQC81MHk52U1mqc3189heXLZQBs7TaVvjUlHM8pBDDWWN/2Lvv21XBkcxcaT9ggMYFep3UlJaUec0ojpPQwKtzde/cB5EAqGmtYbKs4aftZfyR7+8etr3NGOvWHoaG6+f2ctKwv6W42IpzjVdB7aGDrqwfbq295frir7AO5pyC0AcmBdHKy07KZP3Y+l/Yew/w+3zechwfaOiZvr7GC3U5TRjqq4QRNO49gP2Ztk+0Wa7URPSX3wKbtWGpDyxNYD1dTs6GMmg17sFY1tMkmQRBaIxFIPNBi3L+o7Ahp9RZqGmyknzhAWpckDvUaZ0QFnqrTHREL0KrH7bei21G3UZRyOuCoBHc7Zq1upHJNFceqG6ivSSUrP5cE60GyJqVhyp/eOvIIYPZWRV0Fiz++DZu2k5R5KvPHzic7zcdAloeci7W8nAOPPkbDeiM3kjJ+Mn3OH4qprjQkza9mzxBIHgdORj3uemMQWNQjCFFE6kCEgGir2qypRzJZZw+mtmg/DTX96TJsGNbN5ViPHsfU4lzr4Wqsuw9g6p2OKcN7m9lp2czv830s1mrMY6/z7Ty8YLVYsNfUQGICuqkJW2Ul1spMTF2DbirqSN2IEKtIBBKvOCKRPf0vbj7rq2WthlN1t3wT9OjXugI8gPt4Pd9tbN/6TTGVa6ogpXfryvCNS7Aerqby03LY/TkkJpD1yCuevwwDiVQCGO+3lpdT/tBvqC8qAq1J7NUL8x8fo+uYMd6r5AONLPxFLs734szFeKu+DwBZZ12IBhKBCBHHWtVgRBr9M1yRiDV5qMfKcGtltZFr6ZWK9chxnyv7hUpFXQWWWgvmdDPZOTlkXHwR9tpaEnv1Qh8/TsOWrZhOOaVVZBTLuOeovK2cKAjRQhxIvFIwl8ICKPR0zJkL2LjkZI2DcxweWtdM+LmPJ6zl5VTuyIED38D6tWRNycA0PB9TxgCo+ARymleFm6qLwVKFtfE4JChMX/0ZEqe3bt9XVOCj5qHiv4tZfOBzbD0HkZSQxPyx8+mdn0/ywM+x19RwYu9e6tcV0bBt28kK8kBqKIJRKG4528qJr8jDT5s+V06UmVdClBEHIoSEq2d8SgbWbyuxVjX67Nm7IpTyA5gykjFlpITVHou1Gpu2n6x7qbXQm76kT/0+J3btAgVdvjfM6MVXVhsOJA6QFRGFWEZyIB0FP+P0RW8tIvPIBoaMO9vv2hut2gxE08nZq3fiqRcPJ+s2QpkF5ckux+eKQ1+zuHoLNlNXklQCP8u9C/62DGxW7I3GtOKEY/sgKZGss4di6pXmW7sqmLyHt9lXvmZbhXKNP9ucSEQitBHJgQgh45r1U2VoPVmLi11DJ+494WY9Y8c669bD1UYPP7s85Kr2UHrb2UlpzO+RhyU1HbOpB92PNlLlyB1U79nJsYmnknkiley+fTElBq9hFStYy8uxbtuLKasHpsRoWyN0diQC6Wh4WROk+7bXAagZcTnjN93PkMw0j5XR1pXPUvn35ZDSG/vxGgASUrtjtzVBz0EkJJs814tgOI/Kpe+DzQ5du7euBXG30XE/d9o046hFm8626huOsa16O5suGE5jZnejpsRTVbuvdgNdLySQnEQbqtT9Rn1tWYckXPkUyct0CKQSXfCJtaqB+h0HsB6pa76/shqa7JhOyaCp3oq9rhHTKRnY6xpoqqnBZO6HvaaGYx9/grW8vPW1NjumzHSwNWF1LGQVsE1hVKp1RkjHzpnIpguG03PgMGx2W8hV7bFAONSABSGcyBBWR8LZ++tuNnqjG5cw22L0nr9ShqR7buJnWAdONGoyDnxj5ATuv9EYgtq4BFNyHaSmYz1YRaLJBl0zsTb1IqFfNwBO7NhBw/btUL2XhjXvNssnmLJ6QNfuWMsroEc2pv6DDTveXgiHd0HB1UbPNJQZR56e070dD22acnLI6X4WjSXbsRyzkJSQhDndDAXj/b/DvWtP2uz2Ppvdq+V2IJGH+4wv98gmgJ57q/fjVANueY9QIg9PM9GCaa8jqQLHs+3tjDiQToirJuOUDKwHq4ycg2M83dQrjazzx2BtTMPUuAvM41zqswDHPv6EpoYGElQV9voGrIdrjTYrjHH5rMumYt26BtPoKUHnGiIx48ipCeaqDwmhqr29sB6u9ppvApmRJcQekgPpiPipZ/CZa/DT+6rftInv7robmpogMZFTZk2kdv0OY23ypESy7n/8ZFvOyCM146RabjAquN6eKxJrjjvb/u+fjX+t9ca/AyY3j57aaoeXVR1dlfrus8Za5psiiafII5QK+njuvUfy76uttPN7lVlYgleC6cnWb9pEw5atpOSNNmRArFZSTj2VhLQ07HV12A7VgK2peTTj1p61xob1YC2mVBum8JZ+dChcUaG5H/WbNqGALmPGSPW5ENNIBCJ4pWW00fcPD2M65ZRm0UvGmHSqPimB+hpISiDryh8YM4PcI50D30D1t2T9cAymAUONxtvSuwtHb8zb7Cr3HAgEVssRqh1eokK/EUgke6Pu76UNGl5xTSxFUVGq+ZEIRGgT1vJyav79HrqxEZPZjK2igoYtW+l69VyP9R8NW0pJGdi72bTSZtXqR7ShmzWg9X3iZUw/kra2jAqhdQ7EZUcba20EIVyIAxFa4ewN2w4epKnGqAVRycmk5I0GjC875xeXlRlULX8WDhynobIc0/kzTiZ9nbOGbL1g5A8wXbHA0MmCk73uO6+GJjsM+X5gY/3h6PU7q+EPbTdUit0jEV/aXy3zRm21r0VOxVQwt9nzt3oXTlVjZ63NZ5vJumwqprMX+L+XP9x7us6alJarSUaCWOrtO4lFW9zVHCBmZruJAxFa4YwcUvPyAEgeNIjuPzzfyIF4OddTDsSUk0PG7FmuHIopJwcqHNeVl3Ps40+wH7fSJTsdq6PuI5I9alcVd2MDpuQgr/Wkipvo1q4zWgi/2c3tcKu1sdY0GZFIhO8pCN4QByK0wr3eIOmUU+j9k9by7K3OtfWCPn2a1W5Yy8upWvYG2Kw0bNtmSKm7RR7241YaLEeh/jAJ6ZWYvjoEE/7q2ag21is0iyCq64wqeVNXY40U9zXUvdzHZJ5xsgajeg+mqiJIrMZ6pI7K596FaqP2xVUXE0xVurfaEA/7TNnl8NlmrDVN0Ge4UQsSCt7en/vnINdgCereLWeitaX9WIxiwk2Aag7tjTgQoRXBzNLyda63tSysFgs02emSnQ7WOrqeYqVbbkbQCr2uuokq/+q6zWw5vNvIxwRxu2bPWVVk3K+q2qiDsTUZ65ycaMJ6uNZwIBHClJNj1NpUVntcd0UQ2hOZhSVEDG/1Jq1mZ000Yeo3MLAxd4d2lLXwV1Q+9P+M+hP7cbLOH4NpyEjjnEB0pG5c4JoAEMo6Hc5z3Os3mulTBVsN3rJHXmNpXUMTjpyEe9TjXK3SlzpyOGsjPLUVqM5YoO2FapvQDJmFJUQdb9GJa/+nL2I6cBRTSkPQbRsRhaP+ZH+d0fMfEqQtFQHey8fsK1PvHmTdeMnJ2VNfvUX9tr0yQ0roFEQlAlFKzQYeBEYChVrr4hbHBwClwINa68cc+84DngQSgb9qrR8O5F4SgcQ47rOifK0R0qK3aW3qQeXy1dBjIFTvCd9spBYEoxAclvXLW862cdfkCmfPOljNq3DnQMLVViTaE2JejXcrcCnwmZfjjwPvOzeUUonAM8APgFHAlUqpUZE2UggOa3k59cXFrVR6AzlurW401IEPB6afZept6G5lzJplOI8IrTAYjEJwONWEBSEeiMoQltb6awClVKtjSqmLgW8Bd53xQmCn1nq345xXgYswohQhBvDX+/Z6vGAu1uwZjnzGd7C3G1k3XtJ6aqoHBVwTOM7z21Hyjrfeq6fZV74UgglCTTgQnFXgYORBnDaFq5ftR8nY5/nhvHcsthfvtGNEFlM5EKVUOnAXMBO43e2QGdjntr0fOM1HOzcANwAMGDDA22lCGPE24yqQ483yGe1QDxIM4ZqRFgmcuRlMJmwVRkInNT8/Zt6d0PGJmANRSq0EPGln/0pr/baXyx4E/qS1rvUUnQSK1vo54DkwciAhNyQEjL/et6/jJrMZ+gzH6ohOfPbcw7EqoPt5zhk8zpUBnTUhbvUJJsA0IbDenHuVfkh4s9vL6o32YzUc/6oUtEYlJpIyejR97rg9YHXlqBGrdsUzUViTJWIORGt9dgiXnQbMUko9AmQAdqVUA7Ae6O92Xj9ABphjCH+9b1/Hw9lz7yw6Uc6ILqFrGvrECRJSUkhITcVeUxNTEZzQsYmpISyt9VTnZ6XUg0Ct1vpppVQSMEwpNRjDccwBroqOlYI3/PW+fR1vc889WJ0onvrb5QAACM9JREFUb2uSO4l2TYGf+zojOvuxGlSXLmi7Hfvx4yR0724ci9UVAmPVro6At5UyI0hUHIhS6hJgEZAF/FspVaK1Ptfb+Vprm1JqIfAhxjTeF7TWX7WPtUK8EG2dqPZUFnaP2jzmQAKscRGEtiCV6EKHwVpefrI6vc/w4Oow2thrC0sNSCSI1R5+rNolAFKJLnRCoqkT5W8WmiB0RMSBCB0K09le1unwRxt7wmGtAQknsdrDj1W7hKAQByLEBbG+cmF714AIQiwgDkSIeWI2v9CCNs8kE4Q4I1paWIIQMKIxJQixiTgQIeaJ2fyCIHRyZAhLiHkkvyAIsYk4ECEukPyCIMQeMoQlCIIghIQ4EEEQBCEkxIEIgiAIISEORBAEQQgJcSCCIAhCSIgDEQRBEEJCHIggCIIQEuJABEEQhJDo8AtKKaUqgT1taCITOBQmc2IBeZ7Yp6M9kzxPbOPpeQZqrbP8XdjhHUhbUUoVB7IyV7wgzxP7dLRnkueJbdryPDKEJQiCIISEOBBBEAQhJMSB+Oe5aBsQZuR5Yp+O9kzyPLFNyM8jORBBEAQhJCQCEQRBEEJCHIggCIIQEuJAHCilzlNKfaOU2qmUutvD8S5Kqdccx/+rlBrU/lYGTgDPc51SqlIpVeL4uT4adgaKUuoFpdRBpdRWL8eVUuopx/NuVkqNa28bgyGA55mmlKp2+/3c3942BoNSqr9SapVSqlQp9ZVS6mYP58TN7yjA54mb35FSKkUpVaSU2uR4nl97OCf47zitdaf/ARKBXcAQIBnYBIxqcc4C4M+Oz3OA16Jtdxuf5zrg6WjbGsQznQGMA7Z6OX4+8D6ggEnAf6NtcxufZxrwbrTtDOJ5coBxjs/dgO0e/ubi5ncU4PPEze/I8c7THZ9NwH+BSS3OCfo7TiIQg0Jgp9Z6t9a6EXgVuKjFORcBf3d8fgM4Syml2tHGYAjkeeIKrfVnwBEfp1wEvKQNvgQylFIxuwZuAM8TV2ity7XWGxyfjwFfA+YWp8XN7yjA54kbHO+81rFpcvy0nEEV9HecOBADM7DPbXs/rf9YXOdorW1ANdC7XawLnkCeB+Ayx1DCG0qp/u1jWsQI9JnjicmOIYf3lVK50TYmUBxDHwUYvVx34vJ35ON5II5+R0qpRKVUCXAQ+Ehr7fX3E+h3nDiQzsu/gEFa63zgI072PITYYAOGHtEYYBHwzyjbExBKqXRgOXCL1rom2va0FT/PE1e/I611k9Z6LNAPKFRKjW5rm+JADCyAew+8n2Ofx3OUUklAD+Bwu1gXPH6fR2t9WGt9wrH5V2B8O9kWKQL5HcYNWusa55CD1vo9wKSUyoyyWT5RSpkwvmyXaK3f9HBKXP2O/D1PPP6OALTWVcAq4LwWh4L+jhMHYrAOGKaUGqyUSsZIIL3T4px3gGsdn2cBn2hHtikG8fs8LcaeL8QY441n3gF+7JjpMwmo1lqXR9uoUFFKZTvHn5VShRj/V2O1w4LD1ueBr7XWj3s5LW5+R4E8Tzz9jpRSWUqpDMfnVGAmsK3FaUF/xyWF29B4RGttU0otBD7EmMH0gtb6K6XUQ0Cx1vodjD+ml5VSOzGSn3OiZ7FvAnyem5RSFwI2jOe5LmoGB4BSainGrJdMpdR+4AGMRCBa6z8D72HM8tkJ1AM/iY6lgRHA88wC5iulbMBxYE4Md1gApgDXAFsc4+wAvwQGQFz+jgJ5nnj6HeUAf1dKJWI4ute11u+29TtOpEwEQRCEkJAhLEEQBCEkxIEIgiAIISEORBAEQQgJcSCCIAhCSIgDEQRBEEJCHIjQ6VBKNTnUU79yyFDcppRKcByboJR6yse1g5RSV7Wfta3un6qU+o9DlmKaUurdNrT1qlJqWDjtEzoX4kCEzshxrfVYrXUuRkHVDzDqMNBaF2utb/Jx7SAgag4EmAe8qbVuCkNbi4E7w9CO0EkRByJ0arTWB4EbgIWOCmlXr14pdabbWg8blVLdgIeBqY59tzoiktVKqQ2On9Md105TSn3qEKrcppRa4la1PFEp9YUj+ilSSnVzRBSPKqXWOQQuf+bF5LnA2y13OtrcqJQaqpR6UCn1d4dde5RSlyqlHlFKbVFKfeCQ6ABYDZztkK0QhKARByJ0erTWuzEq9k9pceh24EaHAN1UjGrju4HVjgjmTxjKpjO11uOAKwD34a8C4BZgFMbaLFMc0jKvATc7RPjOdrT7PxjSHhOBicBPlVKD3Y1xXDtEa13WYv/pwJ+Bi7TWuxy7hwIzMGRqXgFWaa3zHPf6oeO57RhV4WOCe2OCYCA9D0HwzhrgcaXUEoxho/2q9fIIJuBppdRYoAk41e1YkdZ6P4BDDmMQhkR2udZ6HRiCfI7j5wD5SqlZjmt7AMOAb93aywSqWtx/JPAccI7W+ju3/e9rra1KqS0YzvEDx/4tDjucHAT6Aut9vwpBaI04EKHTo5QagvHlfxDjCxkArfXDSql/Y+g3rVFKnevh8luBAxi9+ASgwe3YCbfPTfj+/6aAX2itP/RxznEgpcW+cse+AsDdgZxwPINdKWV102iyt7AjxdGuIASNDGEJnRqlVBbG8M/TLYXwlFJDtdZbtNZ/wFA4HgEcw1ji1EkPjIjCjiG+l+jnlt8AOUqpiY57dHPkID7EEOYzOfafqpRKc79Qa30USFRKuTuRKowhqd8rpaYF8ehOTgU8rssuCP6QCETojKQ6hpRMGGrELwOeJLtvUUpNx+i1f4WxnrcdaFJKbQJeBJ4FliulfowxTFTn68Za60al1BXAIoes9nGMPMhfMYaWNjiS7ZX/v707tkEgBoIo+qcK+iCmBAIaIKGDK4g6kBASHVwdkCEaWAIILiA42SQn/su9spyM1rK8wO5LiTOwAS6TmvckW+CU5DDrBIAkK94v0m5z10hT/sYrLUiSNTBU1f4HtQbgWVXH/p3pH3mFJS1IVY3A9TPXodcDRxmrgx2IJKmJHYgkqYkBIklqYoBIkpoYIJKkJgaIJKnJC60EjX/xkSEfAAAAAElFTkSuQmCC\n", "text/plain": [ "
" ] }, "metadata": { "needs_background": "light" }, "output_type": "display_data" } ], "source": [ "fig = plt.figure()\n", "ax = fig.add_subplot(1,1,1)\n", "\n", "for (scenario, mode), gdf in df.groupby([\"scenario\", \"mode\"]):\n", " if scenario == \"walking\":\n", " continue\n", " label = ('Rural Area' if scenario == 'rural' else 'City') + \\\n", " ', ' + (\"Medium Range\" if mode == '0' else \"Long Range\")\n", " color = \"orange\" if mode == '0' else \"red\"\n", " marker = \".\" if scenario == \"rural\" else \"+\"\n", " \n", " ax.scatter(gdf[\"distance\"], gdf[\"rssi\"], label=label, alpha=0.5, marker=marker)\n", "\n", "ax.set_xlabel(\"Distance (km)\")\n", "ax.set_ylabel(\"RSSI (dBm)\")\n", "legend = plt.legend()\n", "fig.savefig(\"../gfx/rssi-distance.pdf\")" ] } ], "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.7.6" } }, "nbformat": 4, "nbformat_minor": 2 }